Tracing and observability core for AI & LLM applications.
📘 Read the docs
Part of the AccordKit ecosystem
an open, AI-agnostic tracing SDK for LLM-powered and ChatGPT-interoperable applications.
AccordKit gives developers local-first observability: no vendor lock-in, no opaque dashboards, just clean event streams and tools that work anywhere.
Lightweight, vendor-agnostic tracing core for AccordKit; designed for SDKs, AI agents, and self-hosted analytics.
Provides a unified Tracer interface with message, usage, and span events that can be streamed or batched to any sink.
AccordKit Tracer provides a minimal, structured way to log, emit, and visualize events from your AI apps.
- 📄 Local file and HTTP sinks
- 🔍 Compatible with any SDK
- ⚙️ Built-in OpenAI adapter via
@accordkit/provider-openai
- Unified tracing model for messages, tool calls, spans, and usage.
- Parent–child span propagation with accurate
durationMs. - Middleware pipeline for sampling, transformation, or enrichment.
- Buffered and immediate sinks with
flush()/close()for graceful shutdowns. - No vendor lock-in; integrate with your own backend, file sink, or AccordKit Cloud.
pnpm add @accordkit/tracer
# or
npm install @accordkit/tracerimport { Tracer } from '@accordkit/tracer';
// simplest usage
const tracer = new Tracer({
sink: { write: (_session, e) => console.log('trace event', e) },
service: 'demo-service',
env: 'dev',
});
// emit a message
await tracer.message({ role: 'user', content: 'hello, Trabzon!' });
// start a span
const span = tracer.spanStart({ operation: 'db.query' });
// ... perform some work ...
await tracer.spanEnd(span, { status: 'ok', attrs: { rows: 61 } });const parent = tracer.spanStart({ operation: 'request' });
const child = tracer.spanStart({ operation: 'fetch.users', parent });
await tracer.spanEnd(child, { attrs: { count: 3 } });
await tracer.spanEnd(parent);When a parent is provided, the new span inherits the parent’s traceId and sets its parentSpanId to the parent’s spanId.
Each span produces a SpanEvent with:
{
type: 'span',
operation: string,
durationMs: number,
status: 'ok' | 'error',
ctx: { traceId, spanId, parentSpanId? },
service?: string,
env?: string,
region?: string,
}interface SpanToken {
ctx: { traceId: string; spanId: string; parentSpanId?: string };
operation: string;
service?: string;
env?: string;
region?: string;
attrs?: Record<string, unknown>;
t0: number;
}
interface SpanStartOptions {
operation: string;
service?: string;
env?: string;
region?: string;
attrs?: Record<string, unknown>;
parent?:
| SpanToken
| { traceId: string; spanId: string }
| { ctx: { traceId: string; spanId: string } };
}Middleware functions can transform, enrich, or drop events before they reach the sink.
import type { TraceMiddleware } from '@accordkit/tracer';
const redact: TraceMiddleware = (e) => {
if (e.type === 'message' && typeof e.content === 'string') {
e.content = e.content.replace(/secret/gi, '[REDACTED]');
}
return e;
};
const tracer = new Tracer({ sink, middlewares: [redact] });All sinks implement Sink from @accordkit/tracer, exposing a single write(sessionId, event) method. Some sinks also implement BufferedSink, adding flush() and optional close() for controlled delivery.
- FileSink (Node.js) – JSONL per session, optional buffered mode via
delivery: 'buffered'. - BrowserSink (Web) – Persists to
localStorageunderaccordkit:{sessionId}. - HttpSink (Node.js / Web) – Batches events to an HTTP endpoint; falls back to best effort delivery.
Tracer only depends on the interface, so you can bring your own sink or test with an in-memory implementation.
Buffered sinks accumulate events before writing them out. Tracer forwards flush() and close() so your app can do:
await tracer.flush(); // ensure buffered events are persisted
await tracer.close(); // close transport (if supported)If the sink lacks close(), the tracer falls back to flush(). For immediate sinks, both methods are no-ops.
region is optional and propagated to all emitted events (useful for multi-region ingestion).
const tracer = new Tracer({
sink: new HttpSink({ url: 'https://trace.ingest' }),
middlewares: [sample(0.1), maskPII()],
defaultLevel: 'warn',
sessionId: 'session-123', // optional override
service: 'support-bot',
env: 'prod',
region: 'us-east-1',
});defaultLevelsets the log level for emitted events (infoby default).sessionIdcan be provided for deterministic sessions (otherwise generated).service,env, andregiontags propagate on every event.middlewaresrun sequentially. Returnnullfrom a middleware to drop the event.
await tracer.message({ role: 'user', content: 'Hi there!' } as any);
await tracer.toolCall({ tool: 'weather', input: { city: 'AMS' } } as any);
await tracer.toolResult({ tool: 'weather', output: { temp: 12 }, ok: true } as any);
await tracer.usage({ inputTokens: 100, outputTokens: 18, cost: 0.02 } as any);Each helper injects timestamps, session id, level, and (if needed) a fresh trace context.
const parent = tracer.spanStart({ operation: 'db.query' });
const child = tracer.spanStart({ operation: 'cache.lookup', parent });
/* ... */
await tracer.spanEnd(child, { status: 'ok' });
await tracer.spanEnd(parent, { status: 'error', attrs: { reason: 'timeout' } });spanStartreturns{ ctx, operation, t0, attrs }. Reusectxon related events.spanEndcomputesdurationMs, merges attributes provided at start and end, and defaultsstatustook.
This package ships with comprehensive Vitest coverage under tests/, validating:
- span lifecycle and duration
- parent propagation
- middleware execution
- sink buffering and idempotency
- attribute merge and tag propagation
Run:
pnpm vitestFor unit tests, supply a simple Sink double:
class MemorySink {
events = [];
write(_sessionId, event) {
this.events.push(event);
}
}
const tracer = new Tracer({ sink: new MemorySink() });If you need to assert flush()/close() calls, implement the BufferedSink interface in your test double.
- Core event schema and sink details:
@accordkit/tracerdocs (docs/CORE.md). - Middleware helpers:
sample(rate),maskPII(), or roll your own by returning either a transformed event ornull. - Full TypeScript signatures are documented via TSDoc; run
pnpm run docsin the repo to generate API documentation.
This package is part of the AccordKit organization:
@accordkit/provider-openai— OpenAI API adapter with automatic trace streaming@accordkit/viewer— local-first viewer for traces@accordkit/docs— developer documentation site@accordkit/examples— sample integrations
MIT © AccordKit Contributors
Issues and PRs welcome!
Please follow the AccordKit Contribution Guide.
