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
trace b5e1·c0ff·ee42 total 124ms
  1. POST /checkout 124ms
  2. auth.verify 18ms
  3. db.query 47ms
  4. stripe.charge 52ms

Three primitives

Everything you need from a single import.

01 / trace

Trace any function

              
import { trace } from 'autotel';

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

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

02 / track

Emit product events

              
import { track } from 'autotel';

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

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

03 / log

One canonical log line

                  
import { getRequestLogger } from 'autotel';

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

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

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.