azure-monitor-opentelemetry-ts
Auto-instrument Node.js applications with distributed tracing, metrics, and logs.
- risk
- unknown
- source
- community
- date added
- 2026-02-27
Azure Monitor OpenTelemetry SDK for TypeScript
Auto-instrument Node.js applications with distributed tracing, metrics, and logs.
Installation
# 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
APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...;IngestionEndpoint=...
Quick Start (Auto-Instrumentation)
IMPORTANT: Call useAzureMonitor() BEFORE importing other modules.
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+)
node --import @azure/monitor-opentelemetry/loader ./dist/index.js
package.json:
{ "scripts": { "start": "node --import @azure/monitor-opentelemetry/loader ./dist/index.js" } }
Full Configuration
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
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
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
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
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
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
import { 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); } } }
Custom Span Processor
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
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
import { useAzureMonitor, shutdownAzureMonitor } from "@azure/monitor-opentelemetry"; useAzureMonitor(); // On application shutdown process.on("SIGTERM", async () => { await shutdownAzureMonitor(); process.exit(0); });
Key Types
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
- Call useAzureMonitor() first - Before importing other modules
- Use ESM loader for ESM projects -
--import @azure/monitor-opentelemetry/loader - Enable offline storage - For reliable telemetry in disconnected scenarios
- Set sampling ratio - For high-traffic applications
- Add custom dimensions - Use span processors for enrichment
- Graceful shutdown - Call
shutdownAzureMonitor()to flush telemetry
When to Use
This skill is applicable to execute the workflow or actions described in the overview.