azure-monitor-opentelemetry-ts — Claude Code skill: setup
This skill guides you through adding Azure Monitor and OpenTelemetry to Node.js apps for Application Insights traces, metrics, and logs.
npx skills add microsoft/skills --skill azure-monitor-opentelemetry-ts -a claude-codeWhat azure-monitor-opentelemetry-ts does
The skill covers automatic Node.js instrumentation with @azure/monitor-opentelemetry and manual OpenTelemetry setup. Its examples show custom spans and metrics, plus separate exporters for traces, metrics, and logs. You can also configure which libraries receive instrumentation.
For ESM applications, it gives a loader-based start command for Node.js 18.19+. Call useAzureMonitor() before importing other modules; reversing that order can prevent automatic instrumentation from working. The examples use APPLICATIONINSIGHTS_CONNECTION_STRING to configure the exporter.
The guide also includes custom log ingestion with @azure/monitor-ingestion. When using DefaultAzureCredential in production, set AZURE_TOKEN_CREDENTIALS=prod. Install the skill in Claude Code with the command below; the resulting skill file is at ~/.claude/skills/azure-monitor-opentelemetry-ts/SKILL.md.
What it helps with
- send Node.js traces, metrics, and logs to Azure Monitor
- configure automatic instrumentation for HTTP and database libraries
- create custom OpenTelemetry spans and metrics
- upload application logs with
@azure/monitor-ingestion - set up trace, metric, and log exporters manually
Example prompts for the agent
- Set up automatic Azure Monitor instrumentation for this Node.js app, including traces, metrics, and logs.
- Add a custom span around order processing and record an exception if it fails.
- Show me how to export metrics and logs to Azure Monitor manually.
How to install azure-monitor-opentelemetry-ts
1Skills CLI (Vercel)
npx skills add microsoft/skills --skill azure-monitor-opentelemetry-ts -a claude-code2Manually: put the files in place
~/.claude/skills/azure-monitor-opentelemetry-ts/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/azure-monitor-opentelemetry-ts/
Manually: put the files in place
.agents/skills/azure-monitor-opentelemetry-ts/SKILL.mdFor all projects use ~/.agents/skills/
Manually: put the files in place
.cursor/skills/azure-monitor-opentelemetry-ts/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install microsoft/skills azure-monitor-opentelemetry-ts2Manually: put the files in place
.github/skills/azure-monitor-opentelemetry-ts/SKILL.mdPlatform CLI
gemini skills install https://github.com/microsoft/skills --path .github/plugins/azure-sdk-typescript/skills/azure-monitor-opentelemetry-tsManually: put the files in place
.devin/skills/azure-monitor-opentelemetry-ts/SKILL.mdThe legacy .windsurf/skills/ path still works
Claude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about azure-monitor-opentelemetry-ts
How do I install azure-monitor-opentelemetry-ts in Claude Code?
Run npx skills add microsoft/skills --skill azure-monitor-opentelemetry-ts -a claude-code. The skill file is located at ~/.claude/skills/azure-monitor-opentelemetry-ts/SKILL.md.
What does azure-monitor-opentelemetry-ts do?
It guides setup of Azure Monitor and OpenTelemetry for Node.js applications. The examples cover automatic instrumentation, custom telemetry, and sending data to Application Insights.
What configuration does the skill use for Application Insights?
The exporter examples read APPLICATIONINSIGHTS_CONNECTION_STRING. For production use of DefaultAzureCredential, set AZURE_TOKEN_CREDENTIALS=prod.
Source text
SKILL.md · MIT
---
name: azure-monitor-opentelemetry-ts
description: Instrument applications with Azure Monitor and OpenTelemetry for JavaScript (@azure/monitor-opentelemetry). Use when adding distributed tracing, metrics, and logs to Node.js applications with Application Insights.
license: MIT
metadata:
author: Microsoft
version: "1.0.0"
package: '@azure/monitor-opentelemetry'
---
# Azure Monitor OpenTelemetry SDK for TypeScript
Auto-instrument Node.js applications with distributed tracing, metrics, and logs.
## Installation
```bash
# Distro (recommended - auto-instrumentation)
npm install @azure/monitor-opentelemetry
# Low-level exporters (custom OpenTelemetry setup)
npm install @azure/monitor-opentelemetry-exporter
# Custom logs ingestion
npm install @azure/monitor-ingestion
```
## Environment Variables
```bash
APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...;IngestionEndpoint=...
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
```
## Quick Start (Auto-Instrumentation)
**IMPORTANT:** Call `useAzureMonitor()` BEFORE importing other modules.
```typescript
import { useAzureMonitor } from "@azure/monitor-opentelemetry";
useAzureMonitor({
azureMonitorExporterOptions: {
connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING
}
});
// Now import your application
import express from "express";
const app = express();
```
## ESM Support (Node.js 18.19+)
```bash
node --import @azure/monitor-opentelemetry/loader ./dist/index.js
```
**package.json:**
```json
{
"scripts": {
"start": "node --import @azure/monitor-opentelemetry/loader ./dist/index.js"
}
}
```
## Full Configuration
```typescript
import { useAzureMonitor, AzureMonitorOpenTelemetryOptions } from "@azure/monitor-opentelemetry";
import { resourceFromAttributes } from "@opentelemetry/resources";
const options: AzureMonitorOpenTelemetryOptions = {
azureMonitorExporterOptions: {
connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING,
storageDirectory: "/path/to/offline/storage",
disableOfflineStorage: false
},
// Sampling
samplingRatio: 1.0, // 0-1, percentage of traces
// Features
enableLiveMetrics: true,
enableStandardMetrics: true,
enablePerformanceCounters: true,
// Instrumentation libraries
instrumentationOptions: {
azureSdk: { enabled: true },
http: { enabled: true },
mongoDb: { enabled: true },
mySql: { enabled: true },
postgreSql: { enabled: true },
redis: { enabled: true },
bunyan: { enabled: false },
winston: { enabled: false }
},
// Custom resource
resource: resourceFromAttributes({ "service.name": "my-service" })
};
useAzureMonitor(options);
```
## Custom Traces
```typescript
import { trace } from "@opentelemetry/api";
const tracer = trace.getTracer("my-tracer");
const span = tracer.startSpan("doWork");
try {
span.setAttribute("component", "worker");
span.setAttribute("operation.id", "42");
span.addEvent("processing started");
// Your work here
} catch (error) {
span.recordException(error as Error);
span.setStatus({ code: 2, message: (error as Error).message });
} finally {
span.end();
}
```
## Custom Metrics
```typescript
import { metrics } from "@opentelemetry/api";
const meter = metrics.getMeter("my-meter");
// Counter
const counter = meter.createCounter("requests_total");
counter.add(1, { route: "/api/users", method: "GET" });
// Histogram
const histogram = meter.createHistogram("request_duration_ms");
histogram.record(150, { route: "/api/users" });
// Observable Gauge
const gauge = meter.createObservableGauge("active_connections");
gauge.addCallback((result) => {
result.observe(getActiveConnections(), { pool: "main" });
});
```
## Manual Exporter Setup
### Trace Exporter
```typescript
import { AzureMonitorTraceExporter } from "@azure/monitor-opentelemetry-exporter";
import { NodeTracerProvider, BatchSpanProcessor } from "@opentelemetry/sdk-trace-node";
const exporter = new AzureMonitorTraceExporter({
connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING
});
const provider = new NodeTracerProvider({
spanProcessors: [new BatchSpanProcessor(exporter)]
});
provider.register();
```
### Metric Exporter
```typescript
import { AzureMonitorMetricExporter } from "@azure/monitor-opentelemetry-exporter";
import { PeriodicExportingMetricReader, MeterProvider } from "@opentelemetry/sdk-metrics";
import { metrics } from "@opentelemetry/api";
const exporter = new AzureMonitorMetricExporter({
connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING
});
const meterProvider = new MeterProvider({
readers: [new PeriodicExportingMetricReader({ exporter })]
});
metrics.setGlobalMeterProvider(meterProvider);
```
### Log Exporter
```typescript
import { AzureMonitorLogExporter } from "@azure/monitor-opentelemetry-exporter";
import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
import { logs } from "@opentelemetry/api-logs";
const exporter = new AzureMonitorLogExporter({
connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING
});
const loggerProvider = new LoggerProvider();
loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(exporter));
logs.setGlobalLoggerProvider(loggerProvider);
```
## Custom Logs Ingestion
```typescript
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
import { LogsIngestionClient, isAggregateLogsUploadError } from "@azure/monitor-ingestion";
const endpoint = "https://<dce>.ingest.monitor.azure.com";
const ruleId = "<data-collection-rule-id>";
const streamName = "Custom-MyTable_CL";
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();
const client = new LogsIngestionClient(endpoint, credential);
const logs = [
{
Time: new Date().toISOString(),
Computer: "Server1",
Message: "Application started",
Level: "Information"
}
];
try {
await client.upload(ruleId, streamName, logs);
} catch (error) {
if (isAggregateLogsUploadError(error)) {
for (const uploadError of error.errors) {
console.error("Failed logs:", uploadError.failedLogs);
}
}
}
```
## Custom Span Processor
```typescript
import { SpanProcessor, ReadableSpan } from "@opentelemetry/sdk-trace-base";
import { Span, Context, SpanKind, TraceFlags } from "@opentelemetry/api";
import { useAzureMonitor } from "@azure/monitor-opentelemetry";
class FilteringSpanProcessor implements SpanProcessor {
forceFlush(): Promise<void> { return Promise.resolve(); }
shutdown(): Promise<void> { return Promise.resolve(); }
onStart(span: Span, context: Context): void {}
onEnd(span: ReadableSpan): void {
// Add custom attributes
span.attributes["CustomDimension"] = "value";
// Filter out internal spans
if (span.kind === SpanKind.INTERNAL) {
span.spanContext().traceFlags = TraceFlags.NONE;
}
}
}
useAzureMonitor({
spanProcessors: [new FilteringSpanProcessor()]
});
```
## Sampling
```typescript
import { ApplicationInsightsSampler } from "@azure/monitor-opentelemetry-exporter";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
// Sample 75% of traces
const sampler = new ApplicationInsightsSampler(0.75);
const provider = new NodeTracerProvider({ sampler });
```
## Shutdown
```typescript
import { useAzureMonitor, shutdownAzureMonitor } from "@azure/monitor-opentelemetry";
useAzureMonitor();
// On application shutdown
process.on("SIGTERM", async () => {
await shutdownAzureMonitor();
process.exit(0);
});
```
## Key Types
```typescript
import {
useAzureMonitor,
shutdownAzureMonitor,
AzureMonitorOpenTelemetryOptions,
InstrumentationOptions
} from "@azure/monitor-opentelemetry";
import {
AzureMonitorTraceExporter,
AzureMonitorMetricExporter,
AzureMonitorLogExporter,
ApplicationInsightsSampler,
AzureMonitorExporterOptions
} from "@azure/monitor-opentelemetry-exporter";
import {
LogsIngestionClient,
isAggregateLogsUploadError
} from "@azure/monitor-ingestion";
```
## Best Practices
1. **Call useAzureMonitor() first** - Before importing other modules
2. **Use ESM loader for ESM projects** - `--import @azure/monitor-opentelemetry/loader`
3. **Enable offline storage** - For reliable telemetry in disconnected scenarios
4. **Set sampling ratio** - For high-traffic applications
5. **Add custom dimensions** - Use span processors for enrichment
6. **Graceful shutdown** - Call `shutdownAzureMonitor()` to flush telemetry
© Microsoft · MIT · microsoft/skills