DEV Community

丁久
丁久

Posted on • Originally published at dingjiu1989-hue.github.io

Distributed Tracing with OpenTelemetry

This article was originally published on AI Study Room. For the full version with working code examples and related articles, visit the original post.

Distributed Tracing with OpenTelemetry

Distributed Tracing with OpenTelemetry

Distributed Tracing with OpenTelemetry

Distributed Tracing with OpenTelemetry

Distributed Tracing with OpenTelemetry

Introduction

Distributed tracing provides end-to-end visibility into requests as they traverse multiple services. Unlike logs (which are service-local) and metrics (which are aggregate), traces capture the causal relationship between operations in a distributed system. OpenTelemetry has become the industry standard for instrumentation, offering a unified API for traces, metrics, and logs. This article covers implementing distributed tracing with OpenTelemetry in production.

Core Concepts: Traces, Spans, and Context

A trace represents a complete request flow. Each unit of work within a trace is a span, carrying metadata about timing, status, and parent-child relationships:

import { trace, Span, SpanStatusCode } from "@opentelemetry/api";

const tracer = trace.getTracer("payment-service");

async function processPayment(orderId: string, amount: number) {

// Create a new span as the root of a sub-operation

const span = tracer.startSpan("process-payment", {

attributes: {

"payment.order_id": orderId,

"payment.amount": amount,

"payment.currency": "USD",

},

});

try {

const result = await chargePaymentGateway(orderId, amount);

span.setStatus({ code: SpanStatusCode.OK });

span.setAttribute("payment.transaction_id", result.transactionId);

return result;

} catch (error) {

span.setStatus({

code: SpanStatusCode.ERROR,

message: error.message,

});

span.recordException(error);

throw error;

} finally {

span.end();

}

}

Context Propagation

Propagation carries trace context across service boundaries. For HTTP services, the W3C TraceContext format is standard:

// Instrument outgoing HTTP requests

import { context, propagation } from "@opentelemetry/api";

import * as http from "http";

function makeRequest(url: string, headers: Record) {

// Inject current context into outgoing headers

const activeContext = context.active();

const carrier: Record = {};

propagation.inject(activeContext, carrier);

const allHeaders = { ...headers, ...carrier };

return http.get(url, { headers: allHeaders });

}

For message queues, propagate context through message headers:

// Producer: inject context into message

import { propagation } from "@opentelemetry/api";

function publishMessage(topic: string, payload: any) {

const carrier: Record = {};

propagation.inject(context.active(), carrier);

const message = {

value: JSON.stringify(payload),

headers: {

...carrier,

"content-type": "application/json",

},

};

return kafkaProducer.send({ topic, messages: [message] });

}

// Consumer: extract context from message

import { propagation, context } from "@opentelemetry/api";

kafkaConsumer.on("message", (message) => {

const extractedContext = propagation.extract(

context.active(),

message.headers

);

context.with(extractedContext, async () => {

// This operation is now part of the parent trace

const span = tracer.startSpan("process-order");

// Process message...

span.end();

});

});

Sampling Strategies

Sampling controls the volume of traces c


Read the full article on AI Study Room for complete code examples, comparison tables, and related resources.

Found this useful? Check out more developer guides and tool comparisons on AI Study Room.

Top comments (0)