Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement distributed tracing with Jaeger and Tempo to track requests across microservices and identify performance bottlenecks. Use when debugging microservices, analyzing request flows, or implementing observability for distributed systems.
.claude/skills/dicklesworthstone-distributed-tracing/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-19 | ✓→✓ | = Same ✓ | 121% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 69% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 72% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 131% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 98% | 0% |
Implement distributed tracing with Jaeger and Tempo for request flow visibility across microservices.
Track requests across distributed systems to understand latency, dependencies, and failure points.
Trace (Request ID: abc123)
↓
Span (frontend) [100ms]
↓
Span (api-gateway) [80ms]
├→ Span (auth-service) [10ms]
└→ Span (user-service) [60ms]
└→ Span (database) [40ms]bash# Deploy Jaeger Operator kubectl create namespace observability kubectl create -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.51.0/jaeger-operator.yaml -n observability # Deploy Jaeger instance kubectl apply -f - <<EOF apiVersion: jaegertracing.io/v1 kind: Jaeger metadata: name: jaeger namespace: observability spec: strategy: production storage: type: elasticsearch options: es: server-urls: http://elasticsearch:9200 ingress: enabled: true EOF
yamlversion: "3.8" services: jaeger: image: jaegertracing/all-in-one:latest ports: - "5775:5775/udp" - "6831:6831/udp" - "6832:6832/udp" - "5778:5778" - "16686:16686" # UI - "14268:14268" # Collector - "14250:14250" # gRPC - "9411:9411" # Zipkin environment: - COLLECTOR_ZIPKIN_HOST_PORT=:9411
Reference: See references/jaeger-setup.md
pythonfrom opentelemetry import trace from opentelemetry.exporter.jaeger.thrift import JaegerExporter from opentelemetry.sdk.resources import SERVICE_NAME, Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.instrumentation.flask import FlaskInstrumentor from flask import Flask # Initialize tracer resource = Resource(attributes={SERVICE_NAME: "my-service"}) provider = TracerProvider(resource=resource) processor = BatchSpanProcessor(JaegerExporter( agent_host_name="jaeger", agent_port=6831, )) provider.add_span_processor(processor) trace.set_tracer_provider(provider) # Instrument Flask app = Flask(__name__) FlaskInstrumentor().instrument_app(app) @app.route('/api/users') def get_users(): tracer = trace.get_tracer(__name__) with tracer.start_as_current_span("get_users") as span: span.set_attribute("user.count", 100) # Business logic users = fetch_users_from_db() return {"users": users} def fetch_users_from_db(): tracer = trace.get_tracer(__name__) with tracer.start_as_current_span("database_query") as span: span.set_attribute("db.system", "postgresql") span.set_attribute("db.statement", "SELECT * FROM users") # Database query return query_database()
javascriptconst { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node"); const { JaegerExporter } = require("@opentelemetry/exporter-jaeger"); const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base"); const { registerInstrumentations } = require("@opentelemetry/instrumentation"); const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http"); const { ExpressInstrumentation, } = require("@opentelemetry/instrumentation-express"); // Initialize tracer const provider = new NodeTracerProvider({ resource: { attributes: { "service.name": "my-service" } }, }); const exporter = new JaegerExporter({ endpoint: "http://jaeger:14268/api/traces", }); provider.addSpanProcessor(new BatchSpanProcessor(exporter)); provider.register(); // Instrument libraries registerInstrumentations({ instrumentations: [new HttpInstrumentation(), new ExpressInstrumentation()], }); const express = require("express"); const app = express(); app.get("/api/users", async (req, res) => { const tracer = trace.getTracer("my-service"); const span = tracer.startSpan("get_users"); try { const users = await fetchUsers(); span.setAttributes({ "user.count": users.length }); res.json({ users }); } finally { span.end(); } });
gopackage main import ( "context" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" ) func initTracer() (*sdktrace.TracerProvider, error) { exporter, err := jaeger.New(jaeger.WithCollectorEndpoint( jaeger.WithEndpoint("http://jaeger:14268/api/traces"), )) if err != nil { return nil, err } tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exporter), sdktrace.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("my-service"), )), ) otel.SetTracerProvider(tp) return tp, nil } func getUsers(ctx context.Context) ([]User, error) { tracer := otel.Tracer("my-service") ctx, span := tracer.Start(ctx, "get_users") defer span.End() span.SetAttributes(attribute.String("user.filter", "active")) users, err := fetchUsersFromDB(ctx) if err != nil { span.RecordError(err) return nil, err } span.SetAttributes(attribute.Int("user.count", len(users))) return users, nil }
Reference: See references/instrumentation.md
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: congo=t61rcWkgMzEpythonfrom opentelemetry.propagate import inject headers = {} inject(headers) # Injects trace context response = requests.get('http://downstream-service/api', headers=headers)
javascriptconst { propagation } = require("@opentelemetry/api"); const headers = {}; propagation.inject(context.active(), headers); axios.get("http://downstream-service/api", { headers });
yamlapiVersion: v1 kind: ConfigMap metadata: name: tempo-config data: tempo.yaml: | server: http_listen_port: 3200 distributor: receivers: jaeger: protocols: thrift_http: grpc: otlp: protocols: http: grpc: storage: trace: backend: s3 s3: bucket: tempo-traces endpoint: s3.amazonaws.com querier: frontend_worker: frontend_address: tempo-query-frontend:9095 --- apiVersion: apps/v1 kind: Deployment metadata: name: tempo spec: replicas: 1 template: spec: containers: - name: tempo image: grafana/tempo:latest args: - -config.file=/etc/tempo/tempo.yaml volumeMounts: - name: config mountPath: /etc/tempo volumes: - name: config configMap: name: tempo-config
Reference: See assets/jaeger-config.yaml.template
yaml# Sample 1% of traces sampler: type: probabilistic param: 0.01
yaml# Sample max 100 traces per second sampler: type: ratelimiting param: 100
pythonfrom opentelemetry.sdk.trace.sampling import ParentBased, TraceIdRatioBased # Sample based on trace ID (deterministic) sampler = ParentBased(root=TraceIdRatioBased(0.01))
Jaeger Query:
service=my-service
duration > 1sJaeger Query:
service=my-service
error=true
tags.http.status_code >= 500Jaeger automatically generates service dependency graphs showing:
pythonimport logging from opentelemetry import trace logger = logging.getLogger(__name__) def process_request(): span = trace.get_current_span() trace_id = span.get_span_context().trace_id logger.info( "Processing request", extra={"trace_id": format(trace_id, '032x')} )
No traces appearing:
High latency overhead:
references/jaeger-setup.md - Jaeger installationreferences/instrumentation.md - Instrumentation patternsassets/jaeger-config.yaml.template - Jaeger configurationprometheus-configuration - For metricsgrafana-dashboards - For visualizationslo-implementation - For latency SLOs| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-19 | pass→pass | 15,558 | 15,497 | -0% | 1 | 1 | 0% | 2,728 | 6,027 | +121% | 0 | 0 | — |
case-01 | fail→fail | 17,703 | 18,300 | +3% | 1 | 1 | 0% | 3,614 | 5,817 | +61% | 0 | 0 | — |
case-02 | pass→pass | 18,326 | 15,470 | -16% | 1 | 1 | 0% | 3,600 | 6,096 | +69% | 0 | 0 | — |
case-03 | pass→pass | 17,747 | 18,615 | +5% | 1 | 1 | 0% | 3,602 | 6,207 | +72% | 0 | 0 | — |
case-04 | fail→fail | 11,079 | 9,087 | -18% | 1 | 1 | 0% | 2,097 | 4,448 | +112% | 0 | 0 | — |
case-05 | pass→pass | 11,457 | 9,637 | -16% | 1 | 1 | 0% | 2,026 | 4,674 | +131% | 0 | 0 | — |
case-06 | pass→pass | 15,726 | 13,939 | -11% | 1 | 1 | 0% | 2,783 | 5,518 | +98% | 0 | 0 | — |
case-07 | pass→pass | 8,436 | 6,237 | -26% | 1 | 1 | 0% | 1,601 | 3,999 | +150% | 0 | 0 | — |
case-08 | pass→pass | 9,939 | 9,086 | -9% | 1 | 1 | 0% | 1,828 | 4,196 | +130% | 0 | 0 | — |
case-09 | pass→pass | 11,499 | 11,062 | -4% | 1 | 1 | 0% | 2,236 | 4,639 | +107% | 0 | 0 | — |
case-10 | pass→pass | 8,926 | 6,845 | -23% | 1 | 1 | 0% | 1,707 | 4,145 | +143% | 0 | 0 | — |
case-11 | pass→pass | 14,197 | 4,073 | -71% | 1 | 1 | 0% | 2,462 | 3,553 | +44% | 0 | 0 | — |
case-12 | pass→pass | 10,350 | 4,982 | -52% | 1 | 1 | 0% | 1,672 | 3,709 | +122% | 0 | 0 | — |
case-13 | pass→pass | 9,873 | 5,762 | -42% | 1 | 1 | 0% | 1,627 | 3,823 | +135% | 0 | 0 | — |
case-14 | pass→pass | 16,300 | 8,284 | -49% | 1 | 1 | 0% | 2,617 | 4,163 | +59% | 0 | 0 | — |
case-15 | pass→pass | 13,784 | 15,696 | +14% | 1 | 1 | 0% | 2,386 | 5,275 | +121% | 0 | 0 | — |
case-16 | pass→pass | 11,316 | 10,510 | -7% | 1 | 1 | 0% | 2,256 | 4,990 | +121% | 0 | 0 | — |
case-17 | pass→pass | 14,421 | 10,226 | -29% | 1 | 1 | 0% | 2,411 | 4,489 | +86% | 0 | 0 | — |
case-18 | pass→pass | 17,968 | 19,725 | +10% | 1 | 1 | 0% | 2,799 | 5,850 | +109% | 0 | 0 | — |
case-20 | pass→pass | 6,509 | 3,187 | -51% | 1 | 1 | 0% | 1,349 | 3,501 | +160% | 0 | 0 | — |
case-21 | pass→pass | 6,639 | 6,796 | +2% | 1 | 1 | 0% | 915 | 4,048 | +342% | 0 | 0 | — |
case-22 | pass→pass | 5,653 | 5,661 | +0% | 1 | 1 | 0% | 762 | 3,965 | +420% | 0 | 0 | — |
case-23 | pass→pass | 15,113 | 12,558 | -17% | 1 | 1 | 0% | 2,451 | 5,465 | +123% | 0 | 0 | — |
case-24 | pass→pass | 6,514 | 6,727 | +3% | 1 | 1 | 0% | 1,237 | 4,016 | +225% | 0 | 0 | — |
case-25 | fail→fail | 7,317 | 6,927 | -5% | 1 | 1 | 0% | 1,348 | 4,101 | +204% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of -100 percentage points is the difference between those two pass rates over the 25 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Other measured skills in the registry, with their headline benchmark lift.