patternjavascriptModerate
Creating manual spans with OpenTelemetry for non-auto-instrumented code
Viewed 0 times
@opentelemetry/api ^1.x
startActiveSpanmanual spanrecordExceptionspan attributestracerSpanStatusCodecontext propagation
Problem
Auto-instrumentation covers HTTP, DB, and framework calls but misses custom business logic, background jobs, and third-party SDKs. These operations are invisible in traces, making it impossible to see where time is spent inside your application code.
Solution
Use the OpenTelemetry API tracer to manually create spans around important operations. Always end spans in a finally block to ensure they close even on error.
const { trace, SpanStatusCode } = require('@opentelemetry/api');
const tracer = trace.getTracer('my-service', '1.0.0');
async function processOrder(orderId) {
return tracer.startActiveSpan('processOrder', async (span) => {
span.setAttribute('order.id', orderId);
try {
const result = await doWork(orderId);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (err) {
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
throw err;
} finally {
span.end();
}
});
}Why
startActiveSpan automatically sets the new span as the active span in the current context, so any child spans created inside the callback will be correctly parented without manual context propagation.
Gotchas
- Using tracer.startSpan (not startActiveSpan) does not set the span as active — child spans won't be automatically linked
- span.end() must always be called — orphaned spans are dropped by the SDK after a timeout
- span.recordException() records the error but does not set the status to ERROR — you must call setStatus separately
- Attribute values must be strings, numbers, or booleans — objects will be silently dropped
Context
Adding observability to custom business logic or third-party library calls
Revisions (0)
No revisions yet.