Skip to content

Configuration

Autotel supports standard OpenTelemetry environment variables for configuration. This enables zero-code configuration changes across environments.

Service Configuration:

  • OTEL_SERVICE_NAME — Service name (maps to service in init())

Exporter Configuration:

  • OTEL_EXPORTER_OTLP_ENDPOINT — OTLP collector URL (e.g. http://localhost:4318)
  • OTEL_EXPORTER_OTLP_PROTOCOL — Protocol: http or grpc
  • OTEL_EXPORTER_OTLP_HEADERS — Auth headers as comma-separated key=value pairs

Resource Attributes:

  • OTEL_RESOURCE_ATTRIBUTES — Custom metadata tags as comma-separated key=value pairs
  1. Explicit init() parameters — highest priority
  2. YAML fileautotel.yaml or AUTOTEL_CONFIG_FILE env var
  3. Environment variablesOTEL_*, AUTOTEL_*
  4. Built-in defaults — sensible defaults for development
init({
service: 'my-service', // Overrides YAML and OTEL_SERVICE_NAME
endpoint: 'http://localhost:4318', // Overrides YAML and OTEL_EXPORTER_OTLP_ENDPOINT
});

Framework plugins can lock init() to prevent accidental re-initialization from user code:

import { init, lockLogger, isLoggerLocked } from 'autotel';
// Framework plugin calls this after setting up instrumentation
lockLogger();
// Subsequent init() calls are ignored
isLoggerLocked(); // true

Suppress internal autotel diagnostic output while keeping exporters running:

init({
service: 'my-app',
endpoint: 'http://localhost:4318',
silent: true, // Suppress console output (default: false)
minLevel: 'warn', // Only log warnings and errors (default: 'info')
});

Useful for platforms like GCP Cloud Run or AWS Lambda where stdout is managed externally.

Automatically redact PII and sensitive data from span attributes for compliance:

init({
service: 'my-app',
attributeRedactor: 'default', // 'default' | 'strict' | 'pci-dss'
});

Presets:

  • default — Emails, phone numbers, SSNs, credit cards (last 4), sensitive keys (password, token, etc.)
  • strict — All of default plus JWTs, Bearer tokens, IBANs, API keys in values
  • pci-dss — Focused on payment card data

Custom configuration:

init({
service: 'my-app',
attributeRedactor: {
keyPatterns: [/password/i, /secret/i], // Keys to redact entirely
valuePatterns: [
// Custom patterns with smart masking
{ name: 'customerId', pattern: /CUST-\d{8}/g, replacement: 'CUST-***' }
],
builtins: ['email', 'creditCard'], // Enable specific built-in patterns
replacement: '[REDACTED]', // Default replacement (default)
},
});

Built-in patterns: email, phone, creditCard, ipv4, jwt, bearer, iban

Create an autotel.yaml file in your project root:

autotel.yaml
service:
name: my-service
version: 1.0.0
environment: ${env:NODE_ENV:-development}
exporter:
endpoint: ${env:OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}
protocol: http
headers:
x-honeycomb-team: ${env:HONEYCOMB_API_KEY}
resource:
deployment.environment: ${env:NODE_ENV:-development}
team: backend
autoInstrumentations:
- express
- http
- pino
debug: false

Key features:

  • Auto-discovery: Automatically loads autotel.yaml or autotel.yml from the current directory
  • Explicit path: Set AUTOTEL_CONFIG_FILE=./config/otel.yaml to use a custom path
  • Environment variable substitution: Use ${env:VAR_NAME} or ${env:VAR_NAME:-default}
  • Programmatic loading: Use loadYamlConfigFromFile() from autotel/yaml

Usage with autotel/auto (zero-config):

Terminal window
# Just create autotel.yaml and run:
tsx --import autotel/auto src/index.ts

Programmatic loading:

import { loadYamlConfigFromFile } from 'autotel/yaml';
import { init } from 'autotel';
const yamlConfig = loadYamlConfigFromFile('./config/otel.yaml');
init({ ...yamlConfig, debug: true });

See packages/autotel/autotel.yaml.example for a complete template.

Development (local collector):

Terminal window
export OTEL_SERVICE_NAME=my-app
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

Production (Honeycomb):

Terminal window
export OTEL_SERVICE_NAME=my-app
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
export OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=YOUR_API_KEY
export OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3,deployment.environment=production

Production (Datadog):

Terminal window
export OTEL_SERVICE_NAME=my-app
export OTEL_EXPORTER_OTLP_ENDPOINT=https://http-intake.logs.datadoghq.com
export OTEL_EXPORTER_OTLP_HEADERS=DD-API-KEY=YOUR_API_KEY
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,team=backend

See packages/autotel/.env.example for a complete template.

Environment variable resolution is handled in packages/autotel/src/env-config.ts. The resolver:

  • Validates env var formats (URLs, enum values)
  • Parses complex values (comma-separated key=value pairs)
  • Provides type-safe config objects

YAML configuration is handled in packages/autotel/src/yaml-config.ts. The loader:

  • Auto-discovers autotel.yaml or autotel.yml in the current directory
  • Supports AUTOTEL_CONFIG_FILE env var for custom paths
  • Substitutes ${env:VAR} and ${env:VAR:-default} syntax in YAML values
  • Converts YAML structure to AutotelConfig type

Config merging happens in init() with the priority: explicit > yaml > env > defaults

Events via track() are batched and delivered asynchronously. The default flush interval is 10 seconds with a batch size of 100 events. Use flush() before process exit or in serverless environments to ensure events aren't lost.

Before assertions in tests or process shutdown, manually flush the event queue:

import { getEventQueue } from 'autotel';
await getEventQueue()?.flush();

Always flush before the process exits:

import { track, getEventQueue } from 'autotel';
track('script.started', { scriptName: 'data-import' });
// ... do work ...
track('script.completed', { recordsProcessed: 1000 });
await getEventQueue()?.flush();

Or use a process handler:

process.on('beforeExit', async () => {
await getEventQueue()?.flush();
});

When testing event tracking, flush before assertions:

import { track, getEventQueue } from 'autotel';
import { expect, test } from 'vitest';
test('tracks user signup', async () => {
track('user.signup', { userId: '123' });
// Flush events before assertions
await getEventQueue()?.flush();
// Now verify events were sent to your mock/test subscriber
expect(mockSubscriber.events).toContainEqual(
expect.objectContaining({ name: 'user.signup' }),
);
});

| Setting | Default | Description | | --------------- | -------- | --------------------------------------- | | maxSize | 50,000 | Maximum events in queue before dropping | | batchSize | 100 | Events per batch | | flushInterval | 10,000ms | Automatic flush interval | | maxRetries | 3 | Retry attempts for failed deliveries | | rateLimit | 100/sec | Maximum events per second |

The correlation ID is a stable join key across events, logs, and spans. It is available via getCorrelationId() and getOrCreateCorrelationId() from autotel.

  • Where it's set: The first use of getOrCreateCorrelationId() in an async context creates it; or set it at a boundary (e.g. HTTP handler, message processor) via runWithCorrelationId(id, fn) or setCorrelationId(id) from autotel/correlation-id.
  • Stability: The same value is used for the duration of the same AsyncLocalStorage context (the same async chain). A new context (e.g. a new request) gets a new ID unless you propagate it (e.g. via baggage or Kafka headers).
  • Public API: getCorrelationId() and getOrCreateCorrelationId() are exported from autotel. For cross-service propagation, use setCorrelationIdInBaggage() from autotel/correlation-id.
import { getOrCreateCorrelationId } from 'autotel';
// Inside a request or message handler: same ID for the whole async chain
const correlationId = getOrCreateCorrelationId();

Guarantee: Events tracked within a request context will have the same correlation_id as the HTTP response header. The ID is captured synchronously at track() call time, before any async delivery occurs.

With events.includeTraceContext and events.traceUrl configured in init(), every track(name, props) call automatically includes correlation_id and trace_url: no wrappers needed.

When using track() or the Event class, subscribers receive an optional autotel context (e.g. correlation_id, trace_id, span_id, trace_url) when events.includeTraceContext is enabled in init(). Subscribers that support it (e.g. WebhookSubscriber) receive this via the third parameter to trackEvent and include it in the payload (e.g. as a top-level autotel field or merged into attributes), so consumers can correlate events with traces.

EventAttributes accepts any JSON-serializable values including nested objects:

  • Primitives: string, number, boolean
  • Nested objects: { user: { id: '123', roles: ['admin'] } }
  • Arrays: ['tag1', 'tag2']

Nested objects are validated with a max depth of 3 levels (configurable). Circular references are replaced with '[CIRCULAR]'.