npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vtex/diagnostics-nodejs

v5.5.3

Published

Diagnostics library for Node.js applications

Readme

OpenTelemetry Diagnostics Library for Node.js

A comprehensive Node.js library that simplifies telemetry collection using OpenTelemetry, providing unified interfaces for metrics, logs, and traces with advanced sampling, hot-reload configuration, and easy-to-use instrumentation setups for popular frameworks.

Features

  • Unified Telemetry: Metrics, logs, and traces in one library
  • Structured Events: Send structured, severity-less logs for event tracking.
  • Auto-Instrumentation: Express, Fastify, Koa, NestJS support
  • Advanced Sampling: Rule-based, parent-based, debug override, hot-reload
  • Remote Configuration: File-based, S3, with live reload and polling
  • Configuration Override: Runtime config updates and hot-swapping
  • Resource Discovery: Automatic cloud and Kubernetes detection
  • Context Propagation: Baggage and trace context across services
  • Custom Processors: VTEX-specific attribute enrichment
  • Distributed Resampling: Override parent sampling decisions
  • gRPC Export: High-performance OTLP export

Installation

This library is hosted on a private AWS CodeArtifact registry. To install it, follow these steps:

1. Configure the Scoped Registry

Create or update an .npmrc file in your project root:

@vtex:registry=https://main-053131491888.d.codeartifact.us-east-1.amazonaws.com/npm/observability/
//main-053131491888.d.codeartifact.us-east-1.amazonaws.com/npm/observability/:always-auth=true
//main-053131491888.d.codeartifact.us-east-1.amazonaws.com/npm/observability/:_authToken=${CODEARTIFACT_AUTH_TOKEN}

2. Authenticate with AWS

You need a valid authorization token from AWS CodeArtifact. Run the following command (requires AWS CLI configured):

# Get the token
export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token --domain main --domain-owner 053131491888 --region us-east-1 --query authorizationToken --output text)

Note: Authorization tokens are valid for 12 hours.

3. Install the library

pnpm install @vtex/diagnostics-nodejs

Quick Start

import { NewTelemetryClient, Instrumentation } from '@vtex/diagnostics-nodejs';

async function main() {
  // 1. Define your instrumentations
  const instrumentations = Instrumentation.CommonInstrumentations.express();

  // 2. Initialize telemetry client BEFORE importing frameworks
  const telemetryClient = await NewTelemetryClient(
    'my-app-id',           // Application ID
    'my-client-instance',  // Client instance name
    'my-service-name'      // Service name
  );

  // 3. Create telemetry clients
  const tracesClient = await telemetryClient.newTracesClient({
    setGlobalProvider: true,
  });
  const metricsClient = await telemetryClient.newMetricsClient();
  const logsClient = await telemetryClient.newLogsClient();
  const eventsClient = await telemetryClient.newEventsClient();

  // 4. Register the instrumentations. This activates them.
  telemetryClient.registerInstrumentations(instrumentations);

  // 5. NOW import your frameworks
  const express = require('express');
  const app = express();

  // Your app is automatically instrumented!
  app.get('/api/users', (req, res) => {
    logsClient.info('Users endpoint called', { userId: req.query.id });
    eventsClient.event('api.users.called', { userId: req.query.id });
    res.json({ users: [] });
  });

  app.listen(3000);
}

main().catch(console.error);

Remote Configuration Management

File-Based Configuration with Hot Reload

const telemetryClient = await NewTelemetryClient(
  'my-app-id',
  'my-client',
  'my-service',
  {
    config: {
      configPath: './config/telemetry.json',
      enableHotReload: true,           // Enable live reload
      pollIntervalMs: 15000           // Check for changes every 15s
    }
  }
);

// Config changes are automatically applied without restart!
// Watch logs for: "🔄 Hot reloading sampling configuration..."

S3-Based Remote Configuration

const telemetryClient = await NewTelemetryClient(
  'my-app', 'my-client', 'my-service',
  {
    config: {
      s3Config: {
        bucket: 'my-telemetry-configs',
        key: 'environments/prod/my-service.json',
        region: 'us-east-1'
      },
      pollIntervalMs: 30000,          // Poll S3 every 30s
      enableHotReload: true
    }
  }
);

// Centralized config management:
// 1. Update config in S3
// 2. All instances pick up changes automatically
// 3. No service restarts required

Configuration Override at Runtime

// The ConfigManager is an internal component and not exposed via a public `getConfigManager()` method.
// Configuration updates are handled automatically via file/S3 polling when `enableHotReload` is true.
// The `onConfigChange` functionality would require implementing a custom configuration provider.

console.log('Configuration from file/S3 will be hot-reloaded automatically if enabled.');

Environment-Specific Configuration

// Base configuration
const telemetryClient = await NewTelemetryClient(
  'my-app', 'my-client', 'my-service',
  {
    config: {
      configPath: `./config/${process.env.NODE_ENV || 'development'}.json`,
      enableHotReload: process.env.NODE_ENV !== 'production'
    },

    // Override config based on environment
    environment: process.env.DEPLOYMENT_ENV,
    region: process.env.AWS_REGION,

    // Additional runtime attributes
    additionalAttrs: {
      'deployment.version': process.env.GIT_SHA,
      'deployment.timestamp': new Date().toISOString()
    }
  }
);

Using Configuration Files

const telemetryClient = await NewTelemetryClient(
  'my-app',
  'my-client',
  'my-service',
  {
    config: {
      configPath: './config/telemetry.json',
      enableHotReload: true,
      pollIntervalMs: 15000
    }
  }
);

Configuration Override Examples

// Development: High sampling for debugging
// config/development.json
{
  "traces": {
    "sampling": {
      "defaultRate": 1.0,
      "rules": [
        {
          "name": "debug_everything",
          "sampleRate": 1.0,
          "conditions": []
        }
      ]
    }
  }
}
```json
// Production: Selective sampling
// config/production.json
{
  "traces": {
    "sampling": {
      "defaultRate": 0.01,
      "rules": [
        {
          "name": "critical_endpoints",
          "sampleRate": 1.0,
          "conditions": [
            {
              "attribute": "http.target",
              "operator": "Regex",
              "value": "^/api/(checkout|payment|auth)"
            }
          ]
        },
        {
          "name": "error_traces",
          "sampleRate": 1.0,
          "conditions": [
            {
              "attribute": "http.status_code",
              "operator": "GreaterThanEqual",
              "value": "400"
            }
          ]
        }
      ]
    }
  }
}

Live Configuration Updates

// Monitor configuration changes in real-time
const telemetryClient = await NewTelemetryClient('app', 'client', 'service', {
  config: { enableHotReload: true }
});

// Define instrumentations
const instrumentations = Instrumentation.CommonInstrumentations.autoDetect();

// Initialize clients
const tracesClient = await telemetryClient.newTracesClient();

// Register instrumentations
telemetryClient.registerInstrumentations(instrumentations);

// Example: Change sampling rules via file edit
// 1. Edit config/telemetry.json
// 2. Change defaultRate from 0.1 to 0.5
// 3. Save file
// 4. Library automatically detects change
// 5. New sampling rules applied within seconds
// 6. See logs: "🔄 Hot reloading sampling configuration..."

console.log('Config will auto-reload from file changes...');

Conditional Configuration Loading

async function createTelemetryClient() {
  const isProd = process.env.NODE_ENV === 'production';
  const isK8s = !!process.env.KUBERNETES_SERVICE_HOST;

  const config = {
    config: {
      // Production: Use S3 remote config
      ...(isProd && {
        s3Config: {
          bucket: 'prod-telemetry-configs',
          key: `services/${process.env.SERVICE_NAME}/config.json`,
          region: process.env.AWS_REGION
        }
      }),

      // Development: Use local file
      ...(!isProd && {
        configPath: './config/dev.json'
      }),

      enableHotReload: true,
      pollIntervalMs: isProd ? 60000 : 15000  // Slower polling in prod
    },

    // Kubernetes-specific attributes
    ...(isK8s && {
      additionalAttrs: {
        'k8s.namespace': process.env.KUBERNETES_NAMESPACE,
        'k8s.pod': process.env.HOSTNAME,
        'k8s.node': process.env.NODE_NAME
      }
    }),

    disableCloudDetect: !isProd,
    debug: !isProd
  };

  return await NewTelemetryClient('app', 'client', 'service', config);
}

Global vs Service-Specific Configuration

// Hierarchical configuration override
const baseConfig = {
  "globalResourceAttributes": {
    "service.namespace": "ecommerce",
    "deployment.environment": "production"
  },
  "traces": {
    "exporters": {
      "mode": "otlp",
      "otlp": { "endpoint": "otel-collector:4317" }
    },
    "sampling": {
      "parentBased": true,
      "defaultRate": 0.05
    }
  }
};
```json
// Service-specific overrides
const serviceOverrides = {
  "user-service": {
    "traces": {
      "sampling": {
        "defaultRate": 0.1,
        "rules": [
          {
            "name": "auth_endpoints",
            "sampleRate": 1.0,
            "conditions": [
              { "attribute": "http.target", "operator": "StartsWith", "value": "/auth" }
            ]
          }
        ]
      }
    }
  },

  "payment-service": {
    "traces": {
      "sampling": {
        "defaultRate": 1.0,
        "parentBased": false
      }
    }
  },

  "inventory-service": {
    "traces": {
      "sampling": {
        "parentBased": false,
        "defaultRate": 0.01,
        "rules": [
          {
            "name": "resample_checkout_flow",
            "sampleRate": 1.0,
            "conditions": [
              { "attribute": "http.target", "operator": "Contains", "value": "checkout" }
            ]
          }
        ]
      }
    }
  }
};
```json
{
  "environment": "production",
  "globalResourceAttributes": {
    "service.namespace": "ecommerce",
    "deployment.environment": "prod"
  },
  "traces": {
    "exporters": {
      "mode": "otlp",
      "otlp": { "endpoint": "otel-collector:4317" }
    },
    "sampling": {
      "parentBased": true,
      "defaultRate": 0.1,
      "rules": [
        {
          "name": "sample_critical_endpoints",
          "sampleRate": 1.0,
          "conditions": [
            {
              "attribute": "http.target",
              "operator": "StartsWith",
              "value": "/api/checkout"
            }
          ]
        },
        {
          "name": "debug_override",
          "sampleRate": 1.0,
          "conditions": [
            {
              "attribute": "debug",
              "operator": "Equals",
              "value": "true"
            }
          ]
        }
      ]
    }
  }
}

Advanced Sampling

Rule-Based Sampling

Control sampling with flexible rules that support hot-reload:

// Configuration supports multiple operators and conditions
{
  "sampling": {
    "parentBased": true,
    "defaultRate": 0.05,
    "rules": [
      {
        "name": "high_priority_routes",
        "sampleRate": 1.0,
        "conditions": [
          {
            "attribute": "http.method",
            "operator": "Equals",
            "value": "POST"
          },
          {
            "attribute": "http.target",
            "operator": "Regex",
            "value": "^/api/(orders|payments)"
          }
        ]
      }
    ]
  }
}

Resampling in Distributed Traces

Override parent sampling decisions in specific services:

// Service A: parentBased: true (follows upstream decisions)
// Service B: parentBased: false (makes independent decisions)
{
  "sampling": {
    "parentBased": false,
    "rules": [
      {
        "name": "force_sample_inventory_operations",
        "sampleRate": 1.0,
        "conditions": [...]
      }
    ]
  }
}

Debug Sampling

Force sampling with debug flags:

# Always samples when debug is present
curl "http://api/users?debug=true"
curl -H "debug: true" http://api/users

Supported Sampling Operators

  • Equals, NotEquals
  • Contains, StartsWith, EndsWith
  • In, NotIn (arrays)
  • Regex
  • GreaterThan, LessThan, GreaterThanEqual, LessThanEqual

Instrumentation

Framework Support

// Choose your framework bundle
const instrumentations = {
  express: Instrumentation.CommonInstrumentations.express(),
  fastify: Instrumentation.CommonInstrumentations.fastify(),
  koa: Instrumentation.CommonInstrumentations.koa(),
  nestjs: Instrumentation.CommonInstrumentations.nestjs(),
  minimal: Instrumentation.CommonInstrumentations.minimal(), // HTTP only
  autoDetect: Instrumentation.CommonInstrumentations.autoDetect()
};

Adding Database & External Service Tracing

import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb';
import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';
import { NewTelemetryClient, Instrumentation } from '@vtex/diagnostics-nodejs';

async function setup() {
  // 1. Create a list of all desired instrumentations
  const myInstrumentations = [
    ...Instrumentation.CommonInstrumentations.express(),
    new MongoDBInstrumentation(),
    new RedisInstrumentation(),
    // Add any other OpenTelemetry community instrumentation
  ];

  // 2. Initialize the client
  const telemetryClient = await NewTelemetryClient('app-id', 'client-name', 'service-name');
  await telemetryClient.newTracesClient(); // and other clients

  // 3. Register all instrumentations
  telemetryClient.registerInstrumentations(myInstrumentations);
}

Context Propagation & Baggage

Adding Baggage

import { Traces } from '@vtex/diagnostics-nodejs';

app.get('/api/users', (req, res) => {
  // Add baggage items that propagate to downstream services
  Traces.addBaggage({
    'user.id': req.user.id,
    'request.priority': 'high',
    'trace.source': 'web-app'
  });

  // Single baggage item
  Traces.setBaggage('operation.type', 'user-lookup');

  // Retrieve baggage
  const baggage = Traces.getActiveBaggage();
  console.log('Current baggage:', baggage);
});

Middleware for Context Propagation

import { Instrumentation } from '@vtex/diagnostics-nodejs';

// Express
app.use(Instrumentation.Middlewares.ContextMiddlewares.Express.ContextPropagationMiddleware());

// Fastify
fastify.register(Instrumentation.Middlewares.ContextMiddlewares.Fastify.createContextPlugin());

// Koa
app.use(Instrumentation.Middlewares.ContextMiddlewares.Koa.ContextPropagationMiddleware());

Manual Tracing

Creating Custom Spans

const span = tracesClient.startSpan('database-migration', {
  attributes: { 'migration.version': '1.2.0' },
  kind: SpanKind.INTERNAL
});

try {
  span.setAttributes({
    'migration.tables': ['users', 'orders'],
    'migration.duration_estimate': '5min'
  });

  span.addEvent('migration-started', { timestamp: Date.now() });

  // Your operation
  await runMigration();

  span.addEvent('migration-completed');

} catch (error) {
  span.recordException(error);
  span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
  throw error;
} finally {
  span.end();
}

Trace Context Injection/Extraction

// Inject trace context into HTTP headers
const headers = {};
tracesClient.inject(headers);
console.log(headers); // { traceparent: "00-...", tracestate: "..." }

// Extract trace context from incoming headers
const context = tracesClient.extract(req.headers);
context.execute(() => {
  // Code executed within the extracted trace context
  logsClient.info('Processing with extracted context');
});

Metrics

Counter Metrics

import { Metrics } from '@vtex/diagnostics-nodejs';

const httpRequestsCounter = metricsClient.createCounter(
  'http_requests_total',
  {
    description: 'Total HTTP requests processed',
    unit: '1'
  }
);

// Increment counter
httpRequestsCounter.increment({ method: 'GET', status: '200' });
httpRequestsCounter.add(5, { method: 'POST', status: '201' });

Histogram Metrics

const responseTimeHistogram = metricsClient.createHistogram(
  'http_response_time_seconds',
  {
    description: 'HTTP response time distribution',
    unit: 's',
    boundaries: [0.01, 0.05, 0.1, 0.5, 1, 5, 10]
  }
);

responseTimeHistogram.record(0.145, { route: '/api/users', method: 'GET' });

Gauge Metrics

const activeConnectionsGauge = metricsClient.createGauge(
  'active_connections',
  { description: 'Current active database connections' }
);

activeConnectionsGauge.set(42);
activeConnectionsGauge.add(5);    // Increment by 5
activeConnectionsGauge.subtract(2); // Decrement by 2

Runtime Metrics

Node.js runtime metrics (event loop utilization/delay, GC duration, heap space usage) are collected automatically via @opentelemetry/instrumentation-runtime-node — no configuration required, no opt-out.

Structured Logging

Log Levels & Attributes

// Structured logging with attributes
logsClient.info('User login successful', {
  'user.id': '12345',
  'login.method': 'oauth',
  'client.ip': req.ip,
  'vtex.search_index': 'user_events' // Custom attributes
});

logsClient.warn('Rate limit approaching', {
  'rate_limit.current': 95,
  'rate_limit.max': 100,
  'time_window': '1min'
});

logsClient.error('Database query failed', {
  'query.table': 'users',
  'query.duration_ms': 5000,
  'error.type': 'timeout'
});

Log Correlation with Traces

Logs automatically include trace context when created within an active span:

const span = tracesClient.startSpan('process-order');
// Logs will automatically include trace and span IDs
logsClient.info('Processing order', { orderId: '12345' });
span.end();

Structured Events

The library supports sending structured events, which are essentially logs without a severity level, ideal for capturing business or system events for analysis.

Emitting Events

To send events, you first need to create an EventsClient. This client requires a gRPC exporter to be configured.

// In your telemetry setup
const eventsClient = await telemetryClient.newEventsClient();

// In your application logic
app.post('/api/users', (req, res) => {
  // ... user creation logic ...

  eventsClient.event('user.created', {
    'user.id': newUser.id,
    'user.name': newUser.name,
    'request.id': req.id,
  });

  res.status(201).json(newUser);
});

Configuration for Events

The EventsClient exclusively uses the OTLP gRPC exporter. You must configure the OTLP endpoint for logs in your configuration file for the newEventsClient() method to succeed.

// config/telemetry.json
{
  "logs": {
    "exporters": {
      "mode": "otlp",
      "otlp": {
        "endpoint": "otel-collector:4317"
      }
    }
  }
}

If the OTLP exporter is not configured, newEventsClient() will throw an error.

Resource Discovery

Automatic Attribute Detection

const telemetryClient = await NewTelemetryClient(
  'my-app', 'my-client', 'my-service',
  {
    // Environment variable mappings
    envMappings: [
      { envVar: 'REGION', attributeName: 'cloud.region' },
      { envVar: 'VERSION', attributeName: 'service.version' }
    ],

    // Additional static attributes
    additionalAttrs: {
      'service.team': 'platform',
      'service.tier': 'critical'
    },

    // Cloud detection
    disableCloudDetect: false,   // Auto-detect AWS/GCP/Azure
    disableK8sDetect: false,     // Auto-detect Kubernetes

    // Environment and region
    environment: 'production',
    region: 'us-east-1'
  }
);

Custom Attributes from Files

// Read attributes from JSON file
{
  "customAttributesFile": "./custom-attributes.json"
}
```json
// custom-attributes.json
{
  "environmentVariables": {
    "POD_NAME": "k8s.pod.name",
    "NODE_NAME": "k8s.node.name"
  },
  "attributes": {
    "service.owner": "platform-team",
    "cost.center": "engineering"
  }
}

Export Configuration

OTLP gRPC Export

// Local development (insecure)
const tracesClient = await telemetryClient.newTracesClient({
  exporter: CreateExporter(
    CreateTracesExporterConfig({
      endpoint: 'localhost:4317',
      compression: 'gzip',
      timeoutSeconds: 30
    }),
    'otlp'
  )
});
```javascript
// Production (secure)
const tracesClient = await telemetryClient.newTracesClient({
  exporter: CreateExporter(
    CreateTracesExporterConfig({
      endpoint: 'otel-collector.company.com:443',
      compression: 'gzip',
      insecure: false
    }),
    'otlp'
  )
});

Console Export (Development)

const tracesClient = await telemetryClient.newTracesClient({
  exporter: CreateExporter(
    CreateTracesExporterConfig({ endpoint: 'stdout' }),
    'stdout'
  )
});

Advanced Configuration

Configuration Providers

// File Provider (local development)
import { ConfigManager, FileConfigProvider } from '@vtex/diagnostics-nodejs';

const configManager = new ConfigManager('my-app', 'my-service', {
  configPath: './config/telemetry.json',
  enableHotReload: true
});
```javascript
// S3 Provider (production)
import { S3ConfigProvider } from '@vtex/diagnostics-nodejs';

const s3Provider = new S3ConfigProvider(
  'telemetry-configs-bucket',
  'services/my-service/config.json',
  'us-east-1',
  30000  // Poll interval
);
```javascript
// Custom provider implementation
class CustomConfigProvider {
  async load() {
    // Fetch from your custom source (API, database, etc.)
    return {
      traces: {
        sampling: {
          defaultRate: await this.getSamplingRateFromAPI()
        }
      }
    };
  }

  onChange(callback) {
    // Subscribe to config changes
    this.webhookSubscriber.on('config-changed', callback);
  }
}

Environment Variable Mappings

const telemetryClient = await NewTelemetryClient(
  'my-app', 'my-client', 'my-service',
  {
    // Map environment variables to resource attributes
    envMappings: [
      { envVar: 'REGION', attributeName: 'cloud.region' },
      { envVar: 'VERSION', attributeName: 'service.version' },
      { envVar: 'TEAM', attributeName: 'service.team' },
      { envVar: 'POD_NAME', attributeName: 'k8s.pod.name' },
      { envVar: 'NODE_NAME', attributeName: 'k8s.node.name' }
    ],

    // Prefix-based mapping (maps all TELEMETRY_ATTR_* vars)
    envPrefix: 'TELEMETRY_ATTR_',
    disableEnvPrefixDetection: false,

    // Custom attributes file
    customAttributesFile: './custom-attributes.json'
  }
);

// Environment variables automatically mapped:
// REGION=us-east-1 → cloud.region=us-east-1
// VERSION=1.2.3 → service.version=1.2.3
// TELEMETRY_ATTR_COST_CENTER=eng → cost.center=eng
const telemetryClient = await NewTelemetryClient(
  'my-app', 'my-client', 'my-service',
  {
    config: {
      s3Config: {
        bucket: 'my-telemetry-configs',
        key: 'services/my-service/config.json',
        region: 'us-east-1'
      },
      pollIntervalMs: 30000
    }
  }
);

Advanced Features

Configuration Validation & Error Handling

const telemetryClient = await NewTelemetryClient(
  'my-app', 'my-client', 'my-service',
  {
    config: {
      configPath: './config/telemetry.json',
      enableHotReload: true,

      // Validation options
      validateConfig: true,
      fallbackToDefaults: true,  // Use defaults if config is invalid

      // Error handling
      onConfigError: (error) => {
        console.error('Config error:', error);
        // Send alert, fallback to safe defaults, etc.
      },

      onConfigChange: (newConfig, oldConfig) => {
        console.log('Config changed:', {
          oldSamplingRate: oldConfig.traces?.sampling?.defaultRate,
          newSamplingRate: newConfig.traces?.sampling?.defaultRate
        });
      }
    }
  }
);

Feature Flags Integration

// Dynamic feature flags with configuration
{
  "features": {
    "enableAdvancedSampling": true,
    "enableCustomProcessors": true,
    "enableBaggagePropagation": true
  },
  "traces": {
    "sampling": {
      "defaultRate": "{{features.enableAdvancedSampling ? 0.1 : 0.01}}",
      "rules": [
        {
          "name": "feature_flag_sampling",
          "sampleRate": 1.0,
          "enabled": "{{features.enableAdvancedSampling}}",
          "conditions": [
            { "attribute": "feature.flag", "operator": "Equals", "value": "new_checkout" }
          ]
        }
      ]
    }
  }
}

Multi-Environment Configuration

// config/base.json (shared configuration)
{
  "globalResourceAttributes": {
    "service.namespace": "ecommerce"
  },
  "traces": {
    "exporters": {
      "mode": "otlp"
    }
  }
}
```json
// config/production.json (production overrides)
{
  "extends": "./base.json",
  "globalResourceAttributes": {
    "deployment.environment": "production"
  },
  "traces": {
    "exporters": {
      "otlp": { "endpoint": "prod-collector:4317" }
    },
    "sampling": {
      "defaultRate": 0.01
    }
  }
}
```json
// config/development.json (development overrides)
{
  "extends": "./base.json",
  "globalResourceAttributes": {
    "deployment.environment": "development"
  },
  "traces": {
    "exporters": {
      "otlp": { "endpoint": "localhost:4317" }
    },
    "sampling": {
      "defaultRate": 1.0
    }
  }
}

Circuit Breaker Configuration

// Automatic fallback when OTLP collector is down
{
  "traces": {
    "exporters": {
      "mode": "otlp",
      "otlp": {
        "endpoint": "otel-collector:4317",
        "timeoutMs": 5000,
        "maxRetries": 3
      },
      "fallback": {
        "mode": "console",
        "enabled": true
      }
    },
    "circuitBreaker": {
      "enabled": true,
      "failureThreshold": 5,
      "recoveryTimeMs": 30000
    }
  }
}
import { Processors } from '@vtex/diagnostics-nodejs';

const telemetryClient = await NewTelemetryClient(...);
const tracesClient = await telemetryClient.newTracesClient({
  // VTEX-specific attribute processor (enabled by default)
  enableVtexProcessor: true,
  vtexProcessorConfig: {
    customAttributes: {
      'vtex.workspace': process.env.VTEX_WORKSPACE
    }
  },

  // Baggage processor (enabled by default)
  enableBaggageProcessor: true
});

Performance & Resource Management

// Fine-tune performance settings
{
  "traces": {
    "batchTimeoutMs": 5000,
    "maxBatchSize": 512,
    "maxQueueSize": 2048,
    "useSimpleProcessor": false
  },
  "metrics": {
    "exportIntervalMs": 30000,
    "maxMetricsPoints": 1000,
    "cardinalityLimit": 1000
  },
  "logs": {
    "batchTimeoutMs": 1000,
    "maxBatchSize": 100,
    "maxQueueSize": 500
  }
}

Configuration Security

// Secure configuration with encrypted values
{
  "traces": {
    "exporters": {
      "otlp": {
        "endpoint": "{{decrypt:aws-kms:endpoint}}",
        "headers": {
          "authorization": "{{env:OTEL_API_KEY}}"
        }
      }
    }
  },
  "encryption": {
    "provider": "aws-kms",
    "keyId": "arn:aws:kms:us-east-1:123456789:key/12345678"
  }
}
```javascript
// Runtime decryption
const telemetryClient = await NewTelemetryClient(
  'my-app', 'my-client', 'my-service',
  {
    config: {
      configPath: './config/encrypted.json',
      decryptionProvider: new AWSKMSDecryption(),
      enableConfigValidation: true
    }
  }
);

Custom Processors

import { Processors } from '@vtex/diagnostics-nodejs';

const telemetryClient = await NewTelemetryClient(...);
const tracesClient = await telemetryClient.newTracesClient({
  // Built-in VTEX processor (enabled by default)
  enableVtexProcessor: true,
  vtexProcessorConfig: {
    customAttributes: {
      'vtex.workspace': process.env.VTEX_WORKSPACE,
      'vtex.account': process.env.VTEX_ACCOUNT
    }
  },

  // Baggage processor (enabled by default)
  enableBaggageProcessor: true,

  // Custom span processor
  customProcessors: [
    new MyCustomSpanProcessor({
      addTimestamp: true,
      enrichWithBusinessContext: true
    })
  ]
});
// Before: Manual OpenTelemetry setup
const tracer = trace.getTracer('my-service');
const meter = metrics.getMeter('my-service');

// After: Diagnostics library
const telemetryClient = await NewTelemetryClient('app', 'client', 'service');

const instrumentations = Instrumentation.CommonInstrumentations.autoDetect();
const tracesClient = await telemetryClient.newTracesClient();
const metricsClient = await telemetryClient.newMetricsClient();

telemetryClient.registerInstrumentations(instrumentations);

Troubleshooting

Common Issues

  1. No traces generated: Ensure telemetry client is initialized and instrumentations are registered before importing frameworks.
  2. Sampling not working: Check http.target attribute in rules (not http.route).
  3. Config not reloading: Verify file permissions and polling interval.
  4. gRPC connection failed: Use correct port (4317 for insecure, 443 for TLS).
  5. S3 config not loading: Check AWS credentials and bucket permissions.
  6. High memory usage: Reduce batch sizes and queue limits in the configuration.
  7. Missing baggage: Ensure context propagation middleware is installed and used correctly.

Debug & Monitoring

// Enable comprehensive debugging
const telemetryClient = await NewTelemetryClient(
  'app', 'client', 'service',
  {
    debug: true,  // OpenTelemetry debug logs
    config: {
      enableHotReload: true,
      onConfigError: (error) => console.error('Config error:', error),
      onConfigChange: (newConfig) => console.log('Config updated:', newConfig)
    }
  }
);

// The ConfigManager is internal and not exposed via a public `getConfigManager()` method.
// To monitor config, use the `onConfigChange` callback in the config options.

// Check sampling decisions by enabling debug logs or creating a custom span processor.

Performance Monitoring

// Monitor telemetry overhead
const telemetryMetrics = metricsClient.createHistogram('telemetry_overhead_ms');
const configReloadCounter = metricsClient.createCounter('config_reloads_total');

// The onConfigChange callback can be used to increment the counter.
// Example:
// onConfigChange: (newConfig) => {
//   configReloadCounter.increment();
// }

// Monitoring export success/failure would require custom exporters or processors.

Debug Logging

const telemetryClient = await NewTelemetryClient(
  'app', 'client', 'service',
  { debug: true }  // Enables OpenTelemetry debug logs
);

Examples & Use Cases

Microservices with Remote Configuration

# Centralized configuration management
my-microservices/
├── services/
│   ├── api-gateway/     # parentBased: true, high sampling
│   ├── user-service/    # parentBased: true, follows gateway
│   ├── inventory/       # parentBased: false, resamples
│   └── payment/         # parentBased: true, 100% sampling
├── configs/
│   ├── s3-bucket: telemetry-configs
│   ├── api-gateway.json
│   ├── user-service.json
│   ├── inventory.json
│   └── payment.json
└── shared/
    └── base-config.json

A/B Testing with Configuration

// Dynamic sampling based on feature flags
{
  "traces": {
    "sampling": {
      "rules": [
        {
          "name": "new_checkout_flow",
          "sampleRate": 1.0,
          "conditions": [
            { "attribute": "feature.checkout_v2", "operator": "Equals", "value": "true" }
          ]
        },
        {
          "name": "legacy_checkout_flow",
          "sampleRate": 0.1,
          "conditions": [
            { "attribute": "feature.checkout_v2", "operator": "Equals", "value": "false" }
          ]
        }
      ]
    }
  }
}
```javascript
// Usage in code
app.post('/checkout', (req, res) => {
  const useNewCheckout = featureFlag.isEnabled('checkout_v2', req.user.id);

  Traces.addBaggage({
    'feature.checkout_v2': useNewCheckout.toString(),
    'user.segment': req.user.segment
  });

  // Automatically sampled based on feature flag
});

Disaster Recovery Configuration

// Multi-region failover
{
  "traces": {
    "exporters": {
      "primary": {
        "mode": "otlp",
        "otlp": { "endpoint": "us-east-1-collector:4317" }
      },
      "secondary": {
        "mode": "otlp",
        "otlp": { "endpoint": "us-west-2-collector:4317" }
      },
      "fallback": {
        "mode": "console"
      }
    },
    "failover": {
      "enabled": true,
      "healthCheckIntervalMs": 10000,
      "failoverTimeoutMs": 5000
    }
  }
}

Cost Optimization

// Smart sampling for cost control
{
  "traces": {
    "sampling": {
      "defaultRate": 0.001,
      "rules": [
        {
          "name": "errors_always",
          "sampleRate": 1.0,
          "conditions": [
            { "attribute": "http.status_code", "operator": "GreaterThanEqual", "value": "400" }
          ]
        },
        {
          "name": "slow_requests",
          "sampleRate": 1.0,
          "conditions": [
            { "attribute": "http.response_time_ms", "operator": "GreaterThan", "value": "1000" }
          ]
        },
        {
          "name": "business_critical",
          "sampleRate": 0.1,
          "conditions": [
            { "attribute": "http.target", "operator": "Regex", "value": "^/api/(checkout|payment|auth)" }
          ]
        },
        {
          "name": "high_value_users",
          "sampleRate": 0.5,
          "conditions": [
            { "attribute": "user.tier", "operator": "Equals", "value": "premium" }
          ]
        }
      ]
    }
  }
}

Available Guides

For more information and detailed setup instructions, see our guides:

Migration from Basic OpenTelemetry

Version Policy

This library follows Semantic Versioning. The current version is in package.json.

| Tag pattern | Trigger | |---|---| | js-nodejs-v<semver> | dkcicd publishes @vtex/diagnostics-nodejs to AWS CodeArtifact npm registry |

To bump the version:

cd libs/javascript
make bump-patch   # bug fix
make bump-minor   # new feature (backwards-compatible)
make bump-major   # breaking change

Always run make test-all from the repo root before bumping. Never hand-edit semconv/ generated output — use make semconv-generate TAG=<version> instead.

Contributing

See CONTRIBUTING.md for the full workflow, including adding semantic convention attributes and bumping versions.


VTEX Proprietary Software - This library is proprietary to VTEX and licensed for internal use only.