TT Lab
Get started
Learn Learning paths Courses

OTCA — OpenTelemetry Certified Associate

The span was created—where did it disappear?

Continue in TT Lab

Goal

Fix real OpenTelemetry Python SDK code and use observation to tell which boundary a missing span was lost at: creation, sampling, ending, or delivery. You build 7 wiring functions and an overall report.

Why it matters

Having a trace ID and a True from flush does not mean the span you wanted was received. This lab uses the real SDK 1.44.0 and the official OTLP/HTTP exporter. The exporter sends protobuf to a loopback receiver inside the lab container. In some runs the receiver intentionally returns HTTP 400. This is a condition for reproducing an error.

No Collector, Jaeger, or external store is used. It verifies acceptance by the experimental receiver, and you must not broaden that interpretation into persistent storage or the success of a production deployment. The SDK and its dependencies are preinstalled in the dedicated environment, so no API key or network installation is needed.

Setup and running

The working directory is /root/otca-sdk. The starters folder has 8 files that are syntactically correct but behave wrongly. The file format examples for each step are the same starter code. Copy the relevant file into the working directory and fix it. For example, the first file starts like this.

cd /root/otca-sdk
cp starters/wiring.py wiring.py
/opt/otel-lab/bin/python /opt/app/otca_sdk/runner.py run 1

If you change the number after run, you can see the actual SDK observation and the failure conditions for that step. Do not modify the SDK's internal classes or the grader; fix the code of the given function. Grading runs a copy and does not change your current answer file. Code with the same meaning passes if its actual behavior is right. Ordinary grading is limited to at most 8 seconds, and the overall code check to a 50-second total budget, and infinite loops and output floods are treated as failures.

Steps

  1. /root/otca-sdk/wiring.py — Fix configure(provider, exporter) in wiring.py so that a finished checkout span from the given provider reaches the exporter. Do not change the global provider.
  2. /root/otca-sdk/decisions.py — sampler(mode) in decisions.py returns an SDK Sampler. Make it satisfy the behavior of DROP for drop, RECORD_ONLY for record, and RECORD_AND_SAMPLE for sample.
  3. /root/otca-sdk/parents.py — Fix parent_sampler() in parents.py so that a root with no parent is dropped and remote and local children follow the parent's sampled decision. Keep the link of the parent's trace ID and span ID.
  4. /root/otca-sdk/errors.py — record_failure(span, error) in errors.py receives an order validation exception that has already been caught. Leave the exception event for the original exception and also set the span status to ERROR.
  5. /root/otca-sdk/lifecycle.py — Fix finish(span, provider) in lifecycle.py so that it ends the span, then calls force_flush, and returns its return value. Before the function returns, the experimental receiver must have accepted one checkout span.
  6. /root/otca-sdk/delivery.py — delivered(observation) in delivery.py returns a boolean. Looking at flush, exporter_results, and accepted_spans together, judge only an HTTP 200 acceptance as True, and an HTTP 400 rejection and an unended span as False.
  7. /root/otca-sdk/identity.py — resource(service_name) in identity.py returns an SDK Resource. Set the service name received as input as the service.name resource attribute. You check the actual received spans for the two cases checkout-api and returns-api.
  8. /root/otca-sdk/report.json — Judge the eight boolean hypotheses in report.json according to the theory and the actual observations. Write JSON booleans, not strings or numbers, and the code of the earlier seven steps must also all work.

Notes

Connect the span to the exporter

/root/otca-sdk/wiring.py: Fix configure(provider, exporter) in wiring.py so that a finished checkout span from the given provider reaches the exporter. Do not change the global provider.

Getting a tracer and connecting the delivery path are separate things. Check which processor to register with the provider.

Separate recording from sampling

/root/otca-sdk/decisions.py: sampler(mode) in decisions.py returns an SDK Sampler. Make it satisfy the behavior of DROP for drop, RECORD_ONLY for record, and RECORD_AND_SAMPLE for sample.

Compare the four values together: whether it is recording, the sampled bit, the processor observation, and the exporter observation.

Fix the scope of the parent policy

/root/otca-sdk/parents.py: Fix parent_sampler() in parents.py so that a root with no parent is dropped and remote and local children follow the parent's sampled decision. Keep the link of the parent's trace ID and span ID.

AlwaysOff alone and ParentBased's root=AlwaysOff are different. Distinguish the four parent cases from the root.

Classify a caught exception as an error

/root/otca-sdk/errors.py: record_failure(span, error) in errors.py receives an order validation exception that has already been caught. Leave the exception event for the original exception and also set the span status to ERROR.

Look at the status after the record_exception call. The event and the final business status are not the same field.

End the open span and export it

/root/otca-sdk/lifecycle.py: Fix finish(span, provider) in lifecycle.py so that it ends the span, then calls force_flush, and returns its return value. Before the function returns, the experimental receiver must have accepted one checkout span.

If flush is True but the number received is 0, check whether the span is still open. The grader's cleanup behavior is not counted as the student's success.

Fix the judgment that trusted only the return value

/root/otca-sdk/delivery.py: delivered(observation) in delivery.py returns a boolean. Looking at flush, exporter_results, and accepted_spans together, judge only an HTTP 200 acceptance as True, and an HTTP 400 rejection and an unended span as False.

A body in received does not mean it was accepted. Check the exporter result and accepted_spans.

Connect the service and the current request

/root/otca-sdk/identity.py: resource(service_name) in identity.py returns an SDK Resource. Set the service name received as input as the service.name resource attribute. You check the actual received spans for the two cases checkout-api and returns-api.

Distinguish the Resource from the tracer name and from a span's ordinary attributes. Do not hardcode the one example service name in your code.

Judge the missing boundary overall

/root/otca-sdk/report.json: Judge the eight boolean hypotheses in report.json according to the theory and the actual observations. Write JSON booleans, not strings or numbers, and the code of the earlier seven steps must also all work.

Distinguish which boundary you proved: recording, sampling, ending, sending, acceptance, or storage. If only the report is right and the code is wrong, the overall check fails.