GitHub Copilot ChatGPT Claude Codex CLI Cursor opencode Skill Text

azure-monitor-opentelemetry-ts

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.

Ciza · 0 points · 21 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download microsoft-skills-.github_plugins_azure-sdk-typescript_skills_azure-monitor-opentelemetry-ts-e58528d.zip · 2 KB
Part of microsoft/skills — 195 skills

Install

skills CLI npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/azure-monitor-opentelemetry-ts
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git git clone https://github.com/microsoft/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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=...
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production

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, 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

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

  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
Files (skills)
  • SKILL.md 8.8 KB
    ---
    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
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related