Instruments applications with tracing and structured logging for comprehensive observability.

Install

mkdir -p .claude/skills/autotel && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12000" && unzip -o skill.zip -d .claude/skills/autotel && rm skill.zip

Installs to .claude/skills/autotel

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use when instrumenting with trace/span/track, reviewing code for logging and observability patterns, converting console.log to wide events, adding structured errors, setting up canonical log lines, configuring init(), adding subscribers, or working in the autotel monorepo.
273 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • →Instrument functions with tracing using `trace()`
  • →Add structured errors with `createStructuredError()`
  • →Accumulate request context using `getRequestLogger()`
  • →Track events using `ctx.track` or standalone `track()`

How it works

The skill provides APIs like `trace()` to wrap functions, `createStructuredError()` to add context to errors, and `getRequestLogger()` to accumulate request-specific attributes, all contributing to observability.

Inputs & outputs

You give it
Code to instrument, functions to trace, or errors to structure
You get back
Instrumented code with traces, structured errors, and accumulated request context

When to use autotel

  • →Adding tracing to functions
  • →Converting console.log to wide events
  • →Improving error observability

About this skill

Autotel

Philosophy: "Write once, observe everywhere" - instrument once, stream to any OTLP-compatible backend.

trace() wraps functions. getRequestLogger() accumulates context. createStructuredError() adds why/fix/link to errors. Canonical log lines emit one wide event per request automatically.

When to Use

  • Instrumenting functions with tracing
  • Code uses console.log / console.error for observability
  • Error handling lacks structured context (no why, fix, or link)
  • Adding tracing to any Node.js or edge runtime handler
  • Reviewing code for observability anti-patterns
  • Setting up observability in Cloudflare Workers, Hono, or Next.js
  • Working in the autotel monorepo

Quick Reference

TaskReference
Convert console.log to wide eventswide-events.md
Add structured errorsstructured-errors.md
Accumulate request contextrequest-logger.md
Review code for anti-patternscode-review.md
Add attribute redaction(use `init({ attributeRedactor: 'default'
Lock init from re-initialization(use lockLogger() in framework plugins)

Tracing API

import { trace, ctx, span } from 'autotel';

// trace(name, fn) wraps a reusable function under a stable span name
export const createUser = trace('user.create', async (data) => {
  // The ambient ctx reads the active span from anywhere inside a traced body
  ctx.setAttribute('user.id', data.id);
  return await db.users.create(data);
});

// trace(fn) infers the name from the function or const
export const getUser = trace(async (id) => {
  return await db.users.findById(id);
});

// Nested span
span('db.insert', async () => {
  await db.insert(record);
});

ctx is a live view of the active span, not a value captured at import time. Read it inside a traced body and it answers from that span. With nothing traced

  • an uninstrumented test run, or a plain node server.js - ctx.traceId reads undefined and the methods no-op, so instrumentation is never what throws.

Attribute values can be as rich as the data: an object, Map, Set or Date is flattened to dot-notation keys, so ctx.setAttribute('user', user) records user.id, user.plan. Never flatten by hand before the call.

requestCtx is the same ambient view aimed at the request span. Framework instrumentation opens a span per middleware and per route handler, so context describing the whole request - the authenticated user, the tenant, the plan - set from a shared middleware belongs there rather than on a layer span that ends at next():

import { ctx, requestCtx } from 'autotel';

app.use((req, _res, next) => {
  requestCtx.setAttributes({ user: req.user }); // on GET /users/:id
  ctx.setAttribute('auth.cache_hit', cached); // on the middleware's own span
  next();
});

withTracing({ name })((ctx) => fn) hands the context in as an argument instead. Both forms are supported: prefer the ambient ctx in application code, and the factory when a wrapper needs an explicit context handle.

Recording Errors

Default: throw, don't catch. trace() records status, exception, and structured attributes when the wrapped function rejects.

import { trace, createStructuredError } from 'autotel';

export const charge = trace('cart.charge', async (cart) => {
  if (!cart.items.length) {
    throw createStructuredError({
      message: 'Cart is empty',
      why: 'User submitted checkout with no items',
      fix: 'Validate cart on the client before submit',
      link: 'https://docs.example.com/errors/empty-cart',
    });
  }
  return await processCart(cart);
});

Fallbacks, in order:

  1. Attach call-site context, then rethrow: getRequestLogger(ctx).error(err, { step }). Use when the rethrown error needs context only known at the catch site.
  2. Writing instrumentation/middleware that wraps user handlers: ctx.recordError(err) from inside a withTracing({})((ctx) => ...) callback. Sets ERROR status, structured error.* attributes, and (during the back-compat window) records the exception. Accepts unknown so no as Error cast is needed in catch blocks. For code that doesn't have a ctx handle, use the standalone form recordStructuredError(ctx, err).
// Instrumentation wrapping a user handler. trace.run() is the immediate form:
// it runs now and hands the callback the span's ctx. trace(name, fn) returns a
// wrapper instead, and passes fn the caller's arguments, never a ctx.
return trace.run(name, async (ctx) => {
  try {
    return await userHandler(args);
  } catch (err) {
    ctx.recordError(err); // ergonomic replacement for ctx.recordException
    throw err;
  }
});

ctx.recordException(...) and ctx.addEvent(...) are intentionally hidden from the TraceContext type per OTEP 4430 (March 2026. Span Event API deprecation). The runtime methods exist for back-compat only; new code MUST go through createStructuredError, ctx.recordError(err) / recordStructuredError(ctx, err), or the request logger.

Request Logger

import { trace, getRequestLogger } from 'autotel';

// getRequestLogger() with no args reads the active span
export const handleOrder = trace('order.handle', async (req) => {
  const log = getRequestLogger();
  log.set({ feature: 'checkout', tier: req.user.tier });

  const cart = await loadCart(req.cartId);
  log.set({ cart_items: cart.items.length, cart_total: cart.total });

  try {
    const payment = await processPayment(cart);
    log.set({ payment_method: payment.method });
  } catch (error) {
    // Fallback pattern: attach call-site context, then rethrow.
    // Default is to let the error propagate and let trace() record it.
    log.error(error, { step: 'payment' });
    throw error;
  }
});

Event Tracking

import { trace, ctx, getEventQueue } from 'autotel';

// Inside a traced body — use ctx.track for ergonomic, ctx-bound emission:
export const signup = trace('user.signup', async (data) => {
  ctx.track('user.signup', { userId: data.id, plan: data.plan });
  return await db.users.create(data);
});

// Outside trace() — use the standalone track():
import { track } from 'autotel';
track('user.signup', { userId: '123', plan: 'pro' });

// MUST flush before assertions or shutdown
await getEventQueue()?.flush();

Experiments and Cohorts

import { experiment, getRequestLogger, trace } from 'autotel';
import { bucket } from 'autotel/analysis';

export const checkout = trace('checkout', async (order) => {
  // Names the guess: stamps experiment.name / .variant / .expectation on the
  // active span and puts the first two in baggage, so child spans and the
  // services behind this one carry the same answer.
  experiment({
    name: 'checkout-cache',
    variant: useCache ? 'cached' : 'direct',
    expect: 'cached should cut p95 by 200ms',
  });

  // compareCohorts skips fields whose values never repeat, so bucket numbers
  // at instrumentation time. Non-finite values give 'unknown'.
  getRequestLogger().set({
    'payload.size_bucket': bucket(order.bytes, [1024, 65_536]),
  });

  return submitOrder(order);
});

experiment() is ambient: call it at any depth inside a traced body, and it no-ops when nothing is being traced. Bare baggage keys need init({ baggage: '' }); baggage: true prefixes them. Devtools Compare then offers the experiment's arms instead of asking the reader to know them.

Correlation ID

import { getOrCreateCorrelationId, runWithCorrelationId } from 'autotel';

const correlationId = getOrCreateCorrelationId();
runWithCorrelationId(incomingId, () => handleRequest());

Framework Adapters

// Cloudflare Workers
import { init, wrapModule, trace } from 'autotel-cloudflare';

const processOrder = trace(async (orderId: string, kv: KVNamespace) => {
  return await kv.get(orderId);
});

export default wrapModule(
  { service: { name: 'my-worker' } },
  {
    async fetch(_req, env) {
      return Response.json(await processOrder('123', env.ORDERS_KV));
    },
  },
);
// Next.js
import { withAutotel, useLogger } from 'autotel-adapters/next';

export const POST = withAutotel(async (request) => {
  const log = useLogger(request);
  log.set({ feature: 'checkout' });
  return Response.json({ ok: true });
});
// Hono (with autotel-hono middleware already creating spans)
import { useLogger } from 'autotel-adapters/hono';

app.get('/orders/:id', (c) => {
  const log = useLogger(c);
  log.set({ route: c.req.path });
  return c.json({ ok: true });
});

Anti-Patterns to Detect

Anti-PatternFix
console.log('user created', userId)log.set({ user_id: userId }) inside trace()

Content truncated.

When not to use it

  • →When the user wants to use `ctx.recordException` or `ctx.addEvent` for new code
  • →When the user wants to hand-build log lines from string interpolation
  • →When the user wants to use `console.log` for observability

Limitations

  • →New code must use `createStructuredError`, `ctx.recordError(err)`, or the request logger
  • →`ctx.recordException` and `ctx.addEvent` are hidden from `TraceContext` type
  • →Requires flushing event queue before assertions or shutdown

How it compares

This skill implements a 'write once, observe everywhere' philosophy by providing a unified instrumentation framework for tracing, structured errors, and context accumulation, enabling streaming to any OTLP-compatible backend.

Compared to similar skills

autotel side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
autotel (this skill)03moReviewIntermediate
ai-debug-harness05moReviewAdvanced
gcloud-usage19moNo flagsIntermediate
devops-troubleshooter15moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

Search skills

Search the agent skills registry