From harness-claude
Correlates structured logs with OpenTelemetry distributed traces by injecting trace and span context into JSON log entries. Useful for unified observability, trace-linked log queries, and OTLP export to backends.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Correlate structured logs with distributed traces using OpenTelemetry context for unified observability
Instruments code with OpenTelemetry spans for distributed tracing to visualize request flows across services, debug microservice latency, and correlate logs/errors.
Guides implementing distributed tracing in microservices with OpenTelemetry, covering traces, spans, context propagation, and cross-service debugging.
Instruments apps with OpenTelemetry for distributed tracing and Jaeger/Tempo integration. Debugs latency in microservices, analyzes request flows, correlates traces with logs/metrics.
Share bugs, ideas, or general feedback.
Correlate structured logs with distributed traces using OpenTelemetry context for unified observability
// logger/otel-logger.ts
import pino from 'pino';
import { trace, context } from '@opentelemetry/api';
function getTraceContext(): Record<string, string> {
const span = trace.getActiveSpan();
if (!span) return {};
const spanContext = span.spanContext();
return {
trace_id: spanContext.traceId,
span_id: spanContext.spanId,
trace_flags: `0${spanContext.traceFlags.toString(16)}`,
};
}
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
log(object) {
// Automatically inject trace context into every log entry
return { ...object, ...getTraceContext() };
},
},
serializers: {
err: pino.stdSerializers.err,
},
});
// Create child loggers for specific modules
export function createLogger(module: string) {
return logger.child({ module });
}
// Usage in service code
import { createLogger } from '../logger/otel-logger';
const log = createLogger('order-service');
export async function createOrder(userId: string, items: OrderItem[]): Promise<Order> {
log.info({ userId, itemCount: items.length }, 'Creating order');
try {
const order = await db.orders.create({ userId, items });
log.info({ orderId: order.id, userId }, 'Order created successfully');
return order;
} catch (error) {
log.error({ err: error, userId }, 'Failed to create order');
throw error;
}
}
// Output (with trace context automatically injected):
// {"level":"info","time":1700000000,"module":"order-service",
// "trace_id":"abc123","span_id":"def456","trace_flags":"01",
// "userId":"u1","itemCount":3,"msg":"Creating order"}
OpenTelemetry Logs SDK (direct): For sending logs via OTLP without a logging library bridge:
import { logs, SeverityNumber } from '@opentelemetry/api-logs';
const logger = logs.getLogger('order-service');
logger.emit({
severityNumber: SeverityNumber.INFO,
severityText: 'INFO',
body: 'Order created',
attributes: { 'order.id': orderId, 'user.id': userId },
});
Pino + OpenTelemetry instrumentation:
// Auto-instrumentation adds trace context to Pino logs
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
const pinoInstrumentation = new PinoInstrumentation({
logHook: (span, record) => {
record['service.name'] = 'order-service';
},
});
Winston integration:
import winston from 'winston';
import { trace } from '@opentelemetry/api';
const otelFormat = winston.format((info) => {
const span = trace.getActiveSpan();
if (span) {
const ctx = span.spanContext();
info.trace_id = ctx.traceId;
info.span_id = ctx.spanId;
}
return info;
});
const logger = winston.createLogger({
format: winston.format.combine(otelFormat(), winston.format.json()),
transports: [new winston.transports.Console()],
});
Log levels and when to use them:
Correlation in observability backends: With trace ID in logs, you can: search Grafana Loki by trace ID, click from a Jaeger trace to its logs, filter Datadog logs by trace, and build Kibana dashboards that link logs to traces.
https://opentelemetry.io/docs/concepts/signals/logs/