Opens in a new tab
Observability · Lesson 6 of 12

Follow one local job through its reply, logs and trace

In brief

Start with work you recognize: one synthetic export. Three terminals show its acceptance, background completion and recorded steps without touching a production service.

4 min read

IntermediateLessonAbout reading levels
Is this the right starting point?
Before you read
Basic terminal, HTTP-request and Docker skills in an approved local Python environment.
What you will take away
Run a tiny service and Collector, then match one operation’s acceptance, completion, logs, counters and spans.
Sources
A blue wooden workshop mechanism has copper gears and pipes, a glowing bulb, a curling blank paper strip and a round dial with a black needle.
On this page5 sections

Make one export request, keep its job ID and follow it through three places: the reply, the service's log and the records printed by its Collector. Because you started the job, you know which work to look for. There is no unfamiliar production incident to untangle first.

The supplied Dispatch service accepts HTTP requests and simulates background work with a short delay. An OpenTelemetry Collector receives and prints records of the service's steps. Each step's record is a span; related spans form a trace. The small exercise lets you see where those records come from before you have to investigate a missing one.

Use a disposable Ubuntu VM or another approved local lab with Python 3 and Docker Engine already available. Docker setup is outside this lesson. Check python3 --version and docker version first. Use only synthetic requests. Both the service and the Collector's published port stay on loopback, reachable on the lab machine rather than exposed as public endpoints.

Two files, one local destination

Save dispatch-lab.py and collector.yaml together in one directory. The Python program uses only the standard library. Its /health route says it is listening, /exports starts a background job and /metrics displays counters held in memory.

From that directory, start the Collector:

docker run --rm --name dispatch-collector   -p 127.0.0.1:4318:4318   -v "$PWD/collector.yaml:/etc/otelcol/config.yaml:ro"   ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:0.165.0

Dispatch sends records to port 4318 on the lab machine's loopback address. Docker forwards that port to 4318 inside the container. The read-only volume mapping places your configuration where the Collector expects to find it.

The configuration receives trace data over HTTP using OTLP, the OpenTelemetry Protocol. A debug exporter then prints it. This is a way to inspect the records, not a lasting trace database. The Collector listens inside its container; the port published on the lab machine remains loopback-only. Dispatch builds this small teaching payload directly, whereas normal applications commonly use instrumentation libraries.

Give the three terminals the same job to describe

In a second terminal in the same lab environment, start the service:

python3 dispatch-lab.py --worker-delay 2

In a third terminal, request an export and inspect the counters:

curl -i -sS -X POST http://127.0.0.1:8090/exports
curl -sS http://127.0.0.1:8090/metrics

The POST returns a job ID, a trace ID and a status path. Substitute that job ID into curl -sS http://127.0.0.1:8090/exports/JOB_ID and query the exact path. It may first show pending. After the simulated two-second work, it should show success.

In the service terminal, look for structured acceptance, worker-start and worker-finish events. In the Collector terminal, look for spans named POST /exports and build export, with service name dispatch-lab. You are following the same job from its request to its background work, not comparing unrelated lines that happen to appear together.

An accepted request is only the first answer

The initial HTTP 202 means the request was accepted for processing. The later status says whether the job finished. Check that success, the increased completion counter and the matching trace at the Collector separately. Acceptance does not prove trace delivery; a printed span does not prove that a real customer's report is correct.

Use the job ID to follow application events and the trace ID to follow recorded steps. If the Collector stays quiet, check the service for telemetry_export_failed, then examine the Collector address and receiver pipeline. The failure counter on /metrics offers another view of export errors, outside the trace path that may itself be broken.

The two-second delay makes the handoff visible. The HTTP reply has already returned while the worker continues. You are not simply making one HTTP response take longer.

Keep the trail before stopping the lab

Save the IDs, matching events, spans and counter values. Mark any step you did not execute: reviewing a configuration is not an end-to-end test.

Stop the service with Ctrl+C, then stop the Collector in its terminal. Removing the container leaves the two local files in place. Restarting Python resets the jobs and counters; outstanding jobs do not survive. This is a private instrumentation exercise, not a durable production queue or a secure public server.

If you ran the whole path, you now have a useful reference for later fault exercises: one request, one job and a recognizable trail through the three terminals. When a piece goes missing, you can compare it with a path you have actually seen work.

Sources

Collector installation and configuration document the receiver and debug pipeline. The Python service is supplied specifically for this course.

Sources & context

Sources linked in this article. Read alongside the author’s analysis; a citation does not independently verify a publisher’s claims.

Report an error or outdated detail

Keep reading

Where to go next