@glimt.dev/otel
v2.1.1
Published
Glimt.dev wrapper around OpenTelemetry APIs
Readme
@glimt.dev/otel
OpenTelemetry for Node.js/Next.js/Edge. Works out of the box, highly configurable.
Quick Start (3 lines)
npm install @glimt.dev/otel @opentelemetry/api @opentelemetry/api-logs// instrumentation.ts (Next.js 13.4+)
import { registerOTel } from '@glimt.dev/otel'
export function register() {
registerOTel({ serviceName: 'my-app' })
}Done. Traces go to https://ingest.glimt.dev/v1/traces by default, logs to /v1/logs.
Local Development
For local development with a collector on localhost, set the env var:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318Custom Endpoint (programmatic)
To set the endpoint in code instead of env vars, use exporterUrl:
registerOTel({
serviceName: 'my-app',
exporterUrl: 'https://my-collector.example.com', // SDK appends /v1/traces and /v1/logs
exporterHeaders: { 'x-api-key': 'your-key' }, // optional custom headers
})Full Example (all options)
import { registerOTel, OTLPHttpProtoTraceExporter, OTLPHttpJsonLogsExporter } from '@glimt.dev/otel'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs'
export function register() {
registerOTel({
// Identity
serviceName: 'my-app',
attributes: {
'app.version': process.env.npm_package_version,
},
// Release metadata (CRITICAL for source mapping)
// Config takes precedence over env vars (COMMIT_SHA, COMMIT_REF, etc.)
release: {
commit: process.env.COMMIT_SHA,
branch: process.env.COMMIT_REF,
version: '1.0.0',
environment: 'production',
},
// Glimt auth (or set GLIMT_ORG_ID + GLIMT_PUBLISHABLE_KEY env vars)
organisationId: process.env.GLIMT_ORG_ID,
publishableKey: process.env.GLIMT_PUBLISHABLE_KEY,
// Extract request context
attributesFromHeaders: {
'enduser.id': 'x-user-id',
'tenant.id': 'x-tenant-id',
'client.ip': 'x-forwarded-for',
},
// Sampling (10% in production)
traceSampler: 'parentbased_traceidratio',
// Error handling
captureUnhandledErrors: true,
exitOnUnhandledException: true,
flushTimeoutMs: 2000,
// Custom endpoint (alternative to OTEL_EXPORTER_OTLP_ENDPOINT env var)
exporterUrl: 'https://collector.example.com',
exporterHeaders: { 'x-api-key': 'your-key' },
// Debug logging (alternative to OTEL_LOG_LEVEL env var)
logLevel: 'DEBUG', // 'ALL' | 'VERBOSE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE'
// Multi-backend fan-out (advanced - overrides exporterUrl)
// spanProcessors: [
// new BatchSpanProcessor(new OTLPHttpProtoTraceExporter({
// url: 'https://collector-a.example.com/v1/traces',
// })),
// new BatchSpanProcessor(new OTLPHttpProtoTraceExporter({
// url: 'https://collector-b.example.com/v1/traces',
// })),
// ],
})
}# Set sampling ratio
OTEL_TRACES_SAMPLER_ARG=0.1Environment Variables
Configure via env instead of code:
# Endpoints
OTEL_EXPORTER_OTLP_ENDPOINT=https://collector.example.com # Base URL
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://traces.example.com/v1/traces
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.example.com/v1/logs
# Protocol
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf # or http/json
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/json
# Headers
OTEL_EXPORTER_OTLP_HEADERS=x-api-key=secret,env=prod
# Sampling
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1
# Service
OTEL_SERVICE_NAME=my-service
OTEL_SDK_DISABLED=true # Kill switch
# Glimt auth
GLIMT_ORG_ID=org_xxx
GLIMT_PUBLISHABLE_KEY=pk_xxxBuild Metadata (required for source mapping)
Set commit info for Glimt to map errors to code. Use either the release config option or environment variables:
Via config (recommended):
registerOTel({
serviceName: 'my-app',
release: {
commit: process.env.COMMIT_SHA,
branch: process.env.COMMIT_REF,
version: '1.0.0',
environment: 'production',
}
})Via environment variables:
COMMIT_SHA=$(git rev-parse HEAD)
COMMIT_REF=$(git rev-parse --abbrev-ref HEAD)
BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")Also reads VERCEL_GIT_COMMIT_SHA, NEXT_PUBLIC_COMMIT_SHA, etc.
Manual Spans & Logs
import { trace } from '@opentelemetry/api'
import { logs, SeverityNumber } from '@opentelemetry/api-logs'
// Spans
const tracer = trace.getTracer('my-component')
const span = tracer.startSpan('operation')
span.setAttribute('key', 'value')
span.end()
// Logs
const logger = logs.getLogger('my-component')
logger.emit({
severityNumber: SeverityNumber.INFO,
body: 'User signed up',
attributes: { plan: 'pro' },
})Recording external-service failures on the active span
When integrating with third-party systems (GitHub, Slack, Stripe, Linear, email providers, etc), prefer attaching the failure reason to the current active span so traces are self-explanatory.
import { recordExternalFailure, recordExternalFailureReason } from '@glimt.dev/otel'
// When you have an Error/exception:
try {
await doExternalCall()
} catch (e) {
recordExternalFailure({
system: 'github',
operation: 'listInstallationRepos',
error: e,
attributes: {
'glimt.external.http.status_code': 404,
'glimt.external.installation_id': 123,
},
})
throw e
}
// When you only have a protocol-level error string (no exception thrown):
recordExternalFailureReason({
system: 'slack',
operation: 'chat.postMessage',
reason: 'not_in_channel',
attributes: { 'glimt.external.slack.channel': 'C123...' },
})Notes:
- Both helpers set span status to ERROR, attach
glimt.external.*attributes, and emit anexceptionevent. recordExternalFailureReasondoes not synthesize an Error/stacktrace (to avoid misleading stacks).
Defaults
| Feature | Default |
|---------|---------|
| Trace exporter | http/protobuf → https://ingest.glimt.dev/v1/traces |
| Log exporter | http/json → https://ingest.glimt.dev/v1/logs |
| Sampler | always_on |
| Propagators | W3C Trace Context + Baggage |
| Instrumentations | fetch, Node http/https |
| Error capture | uncaughtException, unhandledRejection → log + flush + exit |
License
MIT
