Skip to content

v1.x MIT OpenTelemetry-native

autotel

Zero-boilerplate OpenTelemetry for TypeScript. Trace functions, track product events, and ship structured errors with one import.

npm install autotel
traceb5e1·c0ff·ee42total 124ms
  1. POST /checkout124ms
  2. auth.verify18ms
  3. db.query47ms
  4. stripe.charge52ms

Three primitives

Everything you need from a single import.

trace()

Trace any function

Wrap a handler. Spans, error recording, and request context come automatically.

import { trace } from 'autotel';

export const checkout = trace((ctx) =>
  async (req, res) => {
    const result = await charge(req.body);
    return res.json(result);
  },
);
track()

Emit product events

Fan out to PostHog, webhooks, or any subscriber. Same call, every backend.

import { track } from 'autotel';

track('order.created', {
  userId: req.user.id,
  amount: result.total,
  currency: 'GBP',
});
log()

One canonical log line

Build context across the request, then emit one structured event at the end.

import { getRequestLogger } from 'autotel';

const log = getRequestLogger(ctx);
log.set({ userId, plan, source });
log.emitNow();

Before / after

Same telemetry. A fraction of the surface area.

Autotel keeps OpenTelemetry compatibility intact. You just stop writing the plumbing.

raw OpenTelemetry ~36 lines
import { trace as otelTrace } from '@opentelemetry/api';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter }
  from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes }
  from '@opentelemetry/semantic-conventions';

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'my-api',
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://localhost:4318/v1/traces',
  }),
});
sdk.start();

const tracer = otelTrace.getTracer('my-api');

export async function checkout(req, res) {
  const span = tracer.startSpan('checkout');
  try {
    const result = await processCheckout(req.body);
    span.setStatus({ code: 1 });
    return res.json(result);
  } catch (err) {
    span.recordException(err);
    span.setStatus({ code: 2 });
    throw err;
  } finally {
    span.end();
  }
}
autotel 8 lines
import { init, trace } from 'autotel';

init({ service: 'my-api', endpoint: 'http://localhost:4318' });

export const checkout = trace((ctx) =>
  async (req, res) => {
    const result = await processCheckout(req.body);
    return res.json(result);
  },
);

Ready to instrument?

One install. One init(). Every span, every backend.