Quick Start
Instrumenting your application means setting up OpenTelemetry to send traces to Lemma. Here’s how:For a run/step/tool-call lifecycle guide, see
Custom Instrumentation. This page focuses on OTel setup and architecture.
1. Set environment variables
2. Call registerOTel() before your application code runs
- Next.js
- Node.js
- Python
Create
instrumentation.ts at your project root:The
NEXT_RUNTIME check ensures instrumentation only runs on the server.3. Use wrapAgent to trace your agent functions
registerOTel() / register_otel() sets up export and batching. wrapAgent / wrap_agent creates the top-level run span (inputs, outputs, timing). Per-LLM-call prompts and completions require a separate layer — OpenInference on the provider SDK — see Provider instrumentation.
threadId and isExperiment at call time, see Wrapping your agent.
How It Works
Next.js
For Next.js 15+, use theinstrumentation.ts file at your project root. This file runs once when the Node.js runtime starts, before any application code executes.
The
NEXT_RUNTIME check ensures instrumentation only runs on the server, not in the browser or Edge runtime.Node.js (General)
For standalone Node.js applications, create a dedicatedtracer.ts or instrumentation.ts file and import it before any other application code:
Python
For Python applications, callregister_otel() at startup:
registerOTel() sets up the OpenTelemetry infrastructure to:
- Create spans when you call
wrapAgentor framework instrumentors - Batch them per agent run using
RunBatchSpanProcessor— all spans for a single agent execution are grouped together - Export them to Lemma’s API at
https://api.uselemma.ai/otel/v1/traceswhen the top-level agent span ends
Configuration
By default,registerOTel() reads from environment variables. You can also pass options directly:
- TypeScript
- Python
Exporting to Multiple Destinations
Already sending traces to Datadog, Jaeger, or another collector? Add Lemma as a second span processor on the sameTracerProvider. Every span — including from wrapAgent and any other instrumentation — is forwarded to each processor independently.
- TypeScript
- Python
create_lemma_span_processor() before or after your existing processor and both will see all spans.
If tracing is already initialized (a framework or APM agent already registered a provider), add Lemma as an additional processor rather than replacing the existing provider:
register_otel() replaces the global provider entirely, which would discard any processors your existing setup registered. For multi-destination setup and using OpenInference on the same custom provider, see the Dual export guide.
Provider spans (OpenInference)
To get child spans for each LLM call (prompt, completion, tokens, model), add OpenInference instrumentors for the OpenAI and/or Anthropic SDKs. That layer is separate fromwrapAgent: it does not create the top-level run span.
- Full procedure — installs,
registerOTel+registerInstrumentations, Pythoninstrument_openai()/instrument_anthropic()— Provider instrumentation - LiteLLM and import paths — Provider instrumentation, Import pitfalls
Troubleshooting
Traces not showing up?- Make sure
registerOTel()is called before any code that creates spans - Verify
LEMMA_API_KEYandLEMMA_PROJECT_IDare set:echo $LEMMA_API_KEY - Check console for authentication errors
- Use
wrapAgentortracer.startActiveSpan(nottracer.startSpan) - Context propagates automatically across async/await
- The
RunBatchSpanProcessoris production-ready by default — it batches all spans for an agent run and exports them together when the run ends - For high-volume apps, consider sampling to trace only a percentage of requests
Next Steps
- wrapAgent — Trace your agent functions
- Provider instrumentation — OpenInference setup, LiteLLM, import paths, run vs child spans
- Custom Instrumentation — Structured TypeScript and Python lifecycle docs
- Integrations — Framework-specific guides
- Implementation details — Lemma’s OTel pipeline,
RunBatchSpanProcessor, andROOT_CONTEXT

