@howezt/telemetry
v0.1.2
Published
OpenTelemetry SDK setup abstraction for multiple runtimes
Readme
@howezt/telemetry
OpenTelemetry SDK setup abstraction for multiple runtimes. Initialise tracing, metrics, and logging with a single function call — the library auto-detects your runtime and wires up the correct providers, exporters, and processors.
The SDK never throws — on any failure it returns a noop result so your application keeps running.
API Reference
Full auto-generated API docs are available on GitHub Pages.
Install
pnpm add @howezt/telemetryOn Node.js (or compatible runtimes like Bun), install pino for structured JSON logging to stderr:
pnpm add pinoPino is an optional peer dependency — the SDK falls back to a built-in formatter if pino is not installed.
Quick Start — Node.js
import { initSDK } from "@howezt/telemetry";
const sdk = initSDK({
serviceName: "my-api",
exporterEndpoint: "https://otel.example.com",
});
sdk.logger.info("server started", { port: 3000 });
// Graceful shutdown
process.on("SIGTERM", () => sdk.shutdown());Quick Start — Cloudflare Workers
import { instrument } from "@howezt/telemetry";
export default instrument({
serviceName: "my-worker",
exporterEndpoint: "https://otel.example.com",
env: {
OTEL_EXPORTER_OTLP_ENDPOINT: "https://otel.example.com",
},
handler: {
async fetch(request, env, ctx) {
return new Response("Hello from Workers!");
},
},
});Endpoint Resolution
The SDK resolves OTLP endpoints per signal (traces, metrics, logs) using this priority (highest first):
OTEL_EXPORTER_OTLP_{SIGNAL}_ENDPOINTenv var (full URL)OTEL_EXPORTER_OTLP_ENDPOINTenv var +/v1/{signal}config.{signal}ExporterEndpoint(full URL)config.exporterEndpoint+/v1/{signal}
If no endpoint resolves for a signal, that signal is disabled.
URLs without a protocol are normalized with https://. Trailing slashes are stripped.
const sdk = initSDK({
serviceName: "my-api",
// Base endpoint — SDK appends /v1/traces, /v1/metrics, /v1/logs
exporterEndpoint: "https://otel.example.com",
// Or override per signal:
tracesExporterEndpoint: "https://traces.example.com/v1/traces",
logsExporterEndpoint: "https://logs.example.com/v1/logs",
});For Cloudflare Workers where process.env is unavailable, pass env:
initSDK({
serviceName: "my-worker",
env: { OTEL_EXPORTER_OTLP_ENDPOINT: env.OTEL_ENDPOINT },
});Logger
Every SDKResult includes a structured logger with dual output:
- stderr — pino (if installed), built-in JSON formatter, or
console[level](Cloudflare) - OTLP — emits log records via the global
LoggerProviderwhen a logs endpoint resolves
const { logger } = initSDK({ serviceName: "my-api", exporterEndpoint: "https://otel.example.com" });
logger.info("request handled", { method: "GET", path: "/api/users" });
logger.error("database connection failed", { host: "db.example.com" });
logger.debug("cache miss", { key: "user:123" }, { timestamp: Date.now() });Log-trace correlation is automatic — traceId and spanId from the active span are included in every log record.
Metrics
Metrics are enabled automatically when a metrics endpoint resolves:
import { initSDK, metrics } from "@howezt/telemetry";
const sdk = initSDK({
serviceName: "my-api",
exporterEndpoint: "https://otel.example.com",
metricsExportIntervalMs: 30_000,
});
const meter = metrics.getMeter("my-api");
const counter = meter.createCounter("http.requests");
counter.add(1, { method: "GET" });Configuration Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| serviceName | string | (required) | Logical service name in every span |
| runtime | RuntimeName | auto-detect | "node", "cloudflare-worker", or custom |
| exporterEndpoint | string | — | Base OTLP endpoint; SDK appends /v1/{signal} |
| exporterHeaders | Record<string, string> | — | Headers for OTLP requests (e.g. auth) |
| resourceAttributes | Record<string, string> | — | Extra Resource attributes |
| tracesExporterEndpoint | string | — | Signal-specific traces endpoint (full URL) |
| logsExporterEndpoint | string | — | Signal-specific logs endpoint (full URL) |
| metricsExporterEndpoint | string | — | Signal-specific metrics endpoint (full URL) |
| metricsExportIntervalMs | number | 60000 | Metrics collection interval (ms) |
| instrumentations | unknown[] | [] | OpenTelemetry instrumentations (Node only) |
| env | Record<string, string> | process.env | Env var map (for Cloudflare Workers) |
Custom Runtime Adapters
Register a custom adapter for runtimes that aren't built-in:
import { register, initSDK, noopLogger } from "@howezt/telemetry";
import type { RuntimeAdapter } from "@howezt/telemetry";
const denoAdapter: RuntimeAdapter = {
name: "deno",
detect: () => "Deno" in globalThis,
setup(config) {
// Return { provider, logger, shutdown, forceFlush } ...
},
};
register(denoAdapter);
const sdk = initSDK({ serviceName: "deno-app" });