Implement comprehensive distributed tracing for HTTP requests and LLM operations using OpenTelemetry with GenAI semantic conventions. Features: - TelemetryService: SDK initialization with OTLP HTTP exporter - TelemetryInterceptor: Automatic HTTP request spans - @TraceLlmCall decorator: LLM operation tracing - GenAI semantic conventions for model/token tracking - Graceful degradation when tracing disabled Instrumented: - All HTTP requests (automatic spans) - OllamaProvider chat/chatStream/embed operations - Token counts, model names, durations Environment: - OTEL_ENABLED (default: true) - OTEL_SERVICE_NAME (default: mosaic-api) - OTEL_EXPORTER_OTLP_ENDPOINT (default: localhost:4318) Tests: 23 passing with full coverage Fixes #131 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
74 lines
1.9 KiB
TypeScript
74 lines
1.9 KiB
TypeScript
import { Injectable } from "@nestjs/common";
|
|
import { context, trace, type Span, type Context } from "@opentelemetry/api";
|
|
|
|
/**
|
|
* Service for managing OpenTelemetry span context propagation.
|
|
* Provides utilities for accessing and manipulating the active trace context.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* const activeSpan = spanContextService.getActiveSpan();
|
|
* if (activeSpan) {
|
|
* activeSpan.setAttribute('custom.key', 'value');
|
|
* }
|
|
* ```
|
|
*/
|
|
@Injectable()
|
|
export class SpanContextService {
|
|
/**
|
|
* Get the currently active span from the context.
|
|
*
|
|
* @returns The active span, or undefined if no span is active
|
|
*/
|
|
getActiveSpan(): Span | undefined {
|
|
return trace.getActiveSpan();
|
|
}
|
|
|
|
/**
|
|
* Get the current trace context.
|
|
*
|
|
* @returns The current context
|
|
*/
|
|
getContext(): Context {
|
|
return context.active();
|
|
}
|
|
|
|
/**
|
|
* Execute a function within a specific context.
|
|
*
|
|
* @param ctx - The context to run the function in
|
|
* @param fn - The function to execute
|
|
* @returns The result of the function
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* const result = spanContextService.with(customContext, () => {
|
|
* // This code runs with customContext active
|
|
* return doSomething();
|
|
* });
|
|
* ```
|
|
*/
|
|
with<T>(ctx: Context, fn: () => T): T {
|
|
return context.with(ctx, fn);
|
|
}
|
|
|
|
/**
|
|
* Set a span as active for the duration of a function execution.
|
|
*
|
|
* @param span - The span to make active
|
|
* @param fn - The function to execute with the active span
|
|
* @returns The result of the function
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* const result = await spanContextService.withActiveSpan(span, async () => {
|
|
* // This code runs with span active
|
|
* return await doAsyncWork();
|
|
* });
|
|
* ```
|
|
*/
|
|
withActiveSpan<T>(span: Span, fn: () => T): T {
|
|
return context.with(trace.setSpan(context.active(), span), fn);
|
|
}
|
|
}
|