Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Auto-instrument Node.js applications with distributed tracing, metrics, and logs.
.claude/skills/sickn33-azure-monitor-opentelemetry-ts/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -51% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 47% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 128% | 0% |
Auto-instrument Node.js applications with distributed tracing, metrics, and logs.
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
bashAPPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...;IngestionEndpoint=...
IMPORTANT: Call useAzureMonitor() BEFORE importing other modules.
typescriptimport { useAzureMonitor } from "@azure/monitor-opentelemetry"; useAzureMonitor({ azureMonitorExporterOptions: { connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING } }); // Now import your application import express from "express"; const app = express();
bashnode --import @azure/monitor-opentelemetry/loader ./dist/index.js
package.json:
json{ "scripts": { "start": "node --import @azure/monitor-opentelemetry/loader ./dist/index.js" } }
typescriptimport { 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);
typescriptimport { 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(); }
typescriptimport { 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" }); });
typescriptimport { 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();
typescriptimport { 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);
typescriptimport { 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);
typescriptimport { DefaultAzureCredential } 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"; const client = new LogsIngestionClient(endpoint, new DefaultAzureCredential()); 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); } } }
typescriptimport { 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()] });
typescriptimport { 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 });
typescriptimport { useAzureMonitor, shutdownAzureMonitor } from "@azure/monitor-opentelemetry"; useAzureMonitor(); // On application shutdown process.on("SIGTERM", async () => { await shutdownAzureMonitor(); process.exit(0); });
typescriptimport { 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";
--import @azure/monitor-opentelemetry/loadershutdownAzureMonitor() to flush telemetryThis skill is applicable to execute the workflow or actions described in the overview.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 13,944 | 10,456 | -25% | 1 | 1 | 0% | 2,779 | 3,991 | +44% | 0 | 0 | — |
case-02 | fail→pass | 16,586 | 9,960 | -40% | 1 | 1 | 0% | 3,349 | 4,275 | +28% | 0 | 0 | — |
case-03 | fail→pass | 44,182 | 8,432 | -81% | 1 | 1 | 0% | 8,264 | 4,021 | -51% | 0 | 0 | — |
case-04 | pass→pass | 14,704 | 7,894 | -46% | 1 | 1 | 0% | 2,502 | 3,594 | +44% | 0 | 0 | — |
case-05 | pass→pass | 14,993 | 9,716 | -35% | 1 | 1 | 0% | 3,052 | 4,208 | +38% | 0 | 0 | — |
case-06 | pass→pass | 8,782 | 4,542 | -48% | 1 | 1 | 0% | 1,672 | 3,184 | +90% | 0 | 0 | — |
case-07 | pass→pass | 11,134 | 6,174 | -45% | 1 | 1 | 0% | 2,202 | 3,500 | +59% | 0 | 0 | — |
case-08 | pass→pass | 11,194 | 7,375 | -34% | 1 | 1 | 0% | 2,059 | 3,649 | +77% | 0 | 0 | — |
case-09 | pass→pass | 13,266 | 7,757 | -42% | 1 | 1 | 0% | 2,537 | 3,906 | +54% | 0 | 0 | — |
case-10 | pass→pass | 16,865 | 8,951 | -47% | 1 | 1 | 0% | 3,275 | 3,987 | +22% | 0 | 0 | — |
case-11 | pass→pass | 8,360 | 4,137 | -51% | 1 | 1 | 0% | 1,541 | 2,988 | +94% | 0 | 0 | — |
case-12 | pass→pass | 7,325 | 5,149 | -30% | 1 | 1 | 0% | 1,455 | 3,248 | +123% | 0 | 0 | — |
case-13 | fail→pass | 12,292 | 4,678 | -62% | 1 | 1 | 0% | 2,115 | 3,110 | +47% | 0 | 0 | — |
case-14 | pass→pass | 15,771 | 6,005 | -62% | 1 | 1 | 0% | 2,590 | 3,557 | +37% | 0 | 0 | — |
case-15 | fail→pass | 6,775 | 4,091 | -40% | 1 | 1 | 0% | 1,329 | 3,033 | +128% | 0 | 0 | — |
case-16 | pass→pass | 8,845 | 8,193 | -7% | 1 | 1 | 0% | 1,844 | 3,998 | +117% | 0 | 0 | — |
case-17 | pass→pass | 9,957 | 4,021 | -60% | 1 | 1 | 0% | 1,819 | 3,016 | +66% | 0 | 0 | — |
case-18 | pass→pass | 7,641 | 3,211 | -58% | 1 | 1 | 0% | 1,360 | 2,801 | +106% | 0 | 0 | — |
case-19 | pass→pass | 2,763 | 2,320 | -16% | 1 | 1 | 0% | 481 | 2,659 | +453% | 0 | 0 | — |
case-20 | pass→pass | 9,394 | 9,214 | -2% | 1 | 1 | 0% | 1,816 | 4,112 | +126% | 0 | 0 | — |
case-21 | pass→pass | 11,657 | 11,137 | -4% | 1 | 1 | 0% | 2,146 | 4,243 | +98% | 0 | 0 | — |
case-22 | pass→pass | 3,519 | 2,965 | -16% | 1 | 1 | 0% | 704 | 2,875 | +308% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.