Getting Started with OTEL + Statsig
Setup and send OpenTelemetry telemetry to Statsig for Infra Analytics (Logs Explorer, Metrics Explorer, Alerts).
This guide helps you set up and send OpenTelemetry telemetry to Statsig so you can use Infra Analytics (Logs Explorer, Metrics Explorer, Alerts).
There are two common paths:
- Kubernetes/OpenTelemetry Collector: scrape logs and metrics from your cluster and export to Statsig. Go to Open Telemetry Logs and Metrics for a more complete guide.
- Applications: export traces, metrics, and logs to your OpenTelemetry Collector (or traces directly from TypeScript/Node). Go to the quick starts below.
Endpoint & Auth
- Endpoint:
https://api.statsig.com/otlp - Auth header:
statsig-api-key: <your Server SDK Secret key>
Direct trace export to the Statsig OTLP endpoint is only available for TypeScript/Node. For all other languages, send traces to your OpenTelemetry Collector and forward from the Collector to Statsig over OTLP/HTTP.
Application Telemetry quick starts
Install dependencies:
npm install --save \
@opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/exporter-metrics-otlp-http \
@opentelemetry/api-logs \
@opentelemetry/sdk-logs \
@opentelemetry/exporter-logs-otlp-http \
@opentelemetry/resources \
@opentelemetry/semantic-conventions
Initialize OpenTelemetry (e.g., instrumentation.js):
// instrumentation.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions');
// import if you want to enable traces
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
// const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
const statsigKey = process.env.STATSIG_SERVER_SDK_SECRET;
const headers = { 'statsig-api-key': statsigKey ?? '' };
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || 'statsig-node-service',
[ATTR_SERVICE_VERSION]: process.env.VERSION || '1',
env: process.env.NODE_ENV || 'development',
}),
traceExporter: new OTLPTraceExporter({
url: 'https://api.statsig.com/otlp/v1/traces',
// or
// url: <your-collector-endpoint>/v1/traces
headers,
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: 'https://api.statsig.com/otlp/v1/metrics',
// or
// url: <your-collector-endpoint>/v1/metrics
headers,
}),
exportIntervalMillis: 60000,
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Install the pino instrumentation:
npm i pino @opentelemetry/instrumentation-pino
// instrumentation.js (continued)
const { BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs');
const statsigKey = process.env.STATSIG_SERVER_SDK_SECRET;
const headers = { 'statsig-api-key': statsigKey ?? '' };
const sdk = new NodeSDK({
// ... other config ...
logRecordProcessors: [
new BatchLogRecordProcessor(
new OTLPLogExporter({
url: 'https://api.statsig.com/otlp/v1/logs',
// or
// url: <your-collector-endpoint>/v1/logs
headers
})
),
],
instrumentations: [getNodeAutoInstrumentations(), new PinoInstrumentation()],
});
// in your application code, e.g., app.js
const pino = require('pino');
const logger = pino();
logger.info('OTel logs initialized');
The Statsig SDK also supports forwarding logs to Log Explorer. Go to the alternative logging example below.
// Requires: npm i @statsig/statsig-node-core
const { Statsig, StatsigUser } = require('@statsig/statsig-node-core');
const s = new Statsig(process.env.STATSIG_SERVER_SDK_SECRET);
await s.initialize();
const user = new StatsigUser({
userID: 'a-user',
custom: { service: process.env.OTEL_SERVICE_NAME || 'my-node-service' },
});
// levels: trace, debug, info, log, warn, error
s.forwardLogLineEvent(user, 'info', 'service started', { version: process.env.npm_package_version });
try {
// your app code
} catch (err) {
s.forwardLogLineEvent(user, 'error', 'unhandled error', {
message: String(err?.message || err),
stack: err?.stack,
});
}
Run your service:
Require or import instrumentation.js before any other application code to ensure instrumentation sets up correctly.
STATSIG_SERVER_SDK_SECRET=YOUR_SECRET \
OTEL_SERVICE_NAME=my-node-service \
node -r ./instrumentation.js app.js
Tip: you can configure exporters through environment variables instead of code:
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.statsig.com/otlpOTEL_EXPORTER_OTLP_HEADERS=statsig-api-key=${STATSIG_SERVER_SDK_SECRET}OTEL_EXPORTER_OTLP_PROTOCOL=http/json
Collector quick starts
Running a Collector is optional but recommended for production workloads.
Use the OpenTelemetry Collector as a gateway to receive OTLP from your applications and forward to Statsig. This is useful for centralizing telemetry collection, adding advanced sampling methods like tail-based sampling, or scraping logs and metrics from hosts or Kubernetes.
Create a minimal values.yaml for the OpenTelemetry Collector that forwards all signals (traces, metrics, logs) to Statsig:
config:
receivers:
otlp:
protocols:
http:
grpc:
processors:
batch: {}
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
Install the Collector with Helm:
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm install otel-gateway open-telemetry/opentelemetry-collector \
-n otel --create-namespace \
-f values.yaml
Provide the Statsig key as an environment variable to the Collector pods (for example, through a Secret and envFrom). Your applications then send OTLP to the in-cluster Collector endpoint (for example http://otel-gateway-collector.otel.svc.cluster.local:4318).
Version requirement
The encoding: json option in the OTLP HTTP exporter requires Collector v0.95.0 or newer. If you pin the image through Helm values, set image.tag: "0.95.0" (or newer).
Common Collector configs (K8s & Docker)
The following examples show popular receivers and processors you can enable in your Collector and still export to Statsig through the same otlphttp exporter.
These components live in the contrib distribution. Use an image that includes them:
- Docker:
otel/opentelemetry-collector-contribor newer - Helm: set
image.repository: otel/opentelemetry-collector-contrib(and a compatibleimage.tag)
Helm values (contrib image):
image:
repository: otel/opentelemetry-collector-contrib
tag: "latest"
pullPolicy: IfNotPresent
A. File logs (filelog receiver)
Reads and parses logs from files on disk. This is useful for hosts, containers, or Kubernetes nodes.
Minimal example:
receivers:
filelog:
include: [ /var/log/myservice/*.json ]
start_at: beginning
operators:
- type: json_parser
timestamp:
parse_from: attributes.time
layout: '%Y-%m-%dT%H:%M:%S%z'
processors:
batch: {}
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
logs:
receivers: [filelog]
processors: [batch]
exporters: [otlphttp]
Kubernetes tip: to tail container logs on nodes, mount host paths (for example, /var/log/pods and /var/lib/docker/containers) into the Collector DaemonSet and set include to those paths.
B. EC2 resource detection (resourcedetection processor)
Automatically adds AWS EC2 metadata (cloud provider, region/zone, instance ID) to your telemetry.
processors:
resourcedetection/ec2:
detectors: [env, ec2]
timeout: 2s
override: false
service:
pipelines:
traces:
receivers: [otlp]
processors: [resourcedetection/ec2, batch]
exporters: [otlphttp]
metrics:
receivers: [otlp]
processors: [resourcedetection/ec2, batch]
exporters: [otlphttp]
logs:
receivers: [otlp]
processors: [resourcedetection/ec2, batch]
exporters: [otlphttp]
The Collector must be able to reach the EC2 metadata service (IMDS). Ensure network access to 169.254.169.254 and IMDSv2 where required.
C. Docker container metrics (docker_stats receiver)
Emits container CPU, memory, network, and block I/O metrics by querying the Docker daemon.
receivers:
docker_stats:
endpoint: unix:///var/run/docker.sock
collection_interval: 15s
processors:
batch: {}
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
metrics:
receivers: [docker_stats]
processors: [batch]
exporters: [otlphttp]
Requirements:
- Linux only (not supported on macOS or Windows).
- Mount the Docker socket into the Collector container:
/var/run/docker.sock.
Related resources
- OpenTelemetry Collector: https://opentelemetry.io/docs/collector/
- Kubernetes Collector components: https://opentelemetry.io/docs/platforms/kubernetes/collector/components/
- Helm chart: https://github.com/open-telemetry/opentelemetry-helm-charts
- Collector configuration reference: https://opentelemetry.io/docs/collector/configuration
- OTLP protocol specification: https://opentelemetry.io/docs/specs/otlp/
- Filelog receiver (contrib): https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/filelogreceiver
- Resource detection processor (contrib): https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/resourcedetectionprocessor
- Docker stats receiver (contrib): https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/dockerstatsreceiver
- Collector contrib distribution: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
Was this helpful?