What Are Trace Flags in OpenTelemetry?
A friendly, ground-up guide to the tiny set of on and off switches that travel with every trace: what each switch means, how a single decision at the front door reaches every service, where the switches are stored, and how to keep them honest and safe.
Imagine a relay race with a long chain of runners. Before the race starts, an official pins a small coloured badge on the baton. A green badge means “this race is being filmed, so please run in front of the cameras.” A plain badge means “no filming today.” Every runner who receives the baton looks at the badge and behaves accordingly. Nobody phones the official to ask. The badge itself carries the message from the first runner to the last.
In OpenTelemetry, a request that travels through many services carries a similar badge. That badge is made of trace flags: a very small group of on and off switches that ride along with the trace identity. They answer simple but important questions, above all, “is this trace being kept?” In this guide, we will learn what trace flags are, what each switch means, where they are stored, how they travel, and how to use and protect them, using simple words and everyday examples.
1The Problem: How Do Many Services Agree Without Talking?
Before learning the tool, let us feel the problem it solves.
A quick reminder about traces and sampling
A trace is the complete story of one request as it travels through your system, made of timed steps called spans. In a large system, recording every trace is expensive, so most companies keep only a share of them. Choosing which traces to keep is called sampling.
The agreement problem
Suppose a customer’s request passes through five services: a gateway, an order service, a payment service, an inventory service and a notification service. If the gateway decides to keep this trace, the other four must also record their parts. Otherwise the trace will have holes, and holes make traces hard to trust. But the five services may run in different languages, on different machines, in different countries, owned by different teams. They cannot all pause to hold a meeting about each request.
The elegant solution is to make the decision once, at the start, and write it into a tiny message that travels along with the request. Every later service reads the message, learns the decision, and follows it. That tiny message is stored in the trace flags.
Think of a school trip permission slip stapled to every child’s bag. The slip carries a few tick boxes: “photos allowed,” “medical form on file.” Every teacher, guide and driver along the way can read the ticks. Nobody needs to call the school office for each child. The tick boxes are the trace flags.
sequenceDiagram
participant G as API Gateway
participant O as Order Service
participant P as Payment Service
participant N as Notification Service
G->>G: Decide to keep this trace and set the sampled flag
G->>O: Request with trace identity and flags
O->>O: Read flags and record the span
O->>P: Request with the same flags
P->>P: Read flags and record the span
P->>N: Request with the same flags
N->>N: Read flags and record the span
2So, What Are Trace Flags?
The short answer first, and then the pieces.
Trace flags are a small set of on and off switches stored as part of a span’s identity, called the span context. They are one byte in size, which means eight switches. Each switch is a single bit that is either zero or one. Trace flags describe facts about the whole trace, and they travel from service to service with the trace ID and span ID.
Where trace flags fit in the span context
A span context is the small, unchangeable identity card of a span. It holds five items. We introduce each one briefly, so that you can see where the flags sit.
Trace ID
The shared number of the whole journey. Every span in the trace has the same one.
Span ID
The number of this one step inside the trace.
Trace Flags
The group of on and off switches. This is our topic today.
Trace State
An optional list of vendor notes in key and value form, separate from the flags.
Is Remote
A local marker saying whether this context arrived from another program.
What trace flags are not
- They are not span attributes. Attributes describe one span in detail. Flags describe the trace as a whole, and they are tiny.
- They are not the trace state. The trace state holds free-form vendor notes. Flags hold fixed, standard switches.
- They are not baggage. Baggage holds your own key and value notes. Flags hold only standard switches.
- They are not a command. As we will see, the main flag is more like a strong hint than an order.
- They are not about success or failure. That is the job of the span status.
3Anatomy: One Byte, Eight Switches
Reading trace flags is easy once you know how to read a single byte.
A quick lesson on bits and hex
A bit is the smallest piece of computer data: it is either 0 or 1, like a light switch that is off or on. A byte is a group of eight bits. Because eight switches in a row are awkward to write down, people write a byte using two hexadecimal characters, which is a counting system that uses the digits 0 to 9 and the letters a to f. So the byte with all switches off is written “00”, and the byte with only the lowest switch on is written “01”.
flowchart LR
A["Trace flags byte"] --> B["Bit 7: reserved"]
A --> C["Bit 6: reserved"]
A --> D["Bit 5: reserved"]
A --> E["Bit 4: reserved"]
A --> F["Bit 3: reserved"]
A --> G["Bit 2: reserved"]
A --> H["Bit 1: random trace ID flag in newer versions"]
A --> I["Bit 0: sampled flag"]
What each position means
| Bit Position | Hex Value When On | Name | Status |
|---|---|---|---|
| Bit 0, the lowest | 01 | Sampled | Standard and widely used |
| Bit 1 | 02 | Random trace ID | Defined in newer revisions of the W3C standard, with support still maturing |
| Bits 2 to 7 | 04, 08, 10, 20, 40, 80 | Reserved | Not assigned. They exist for future needs |
Reading some real values
| Flags as Written | Switches That Are On | Plain Meaning |
|---|---|---|
00 | None | The trace is not sampled |
01 | Sampled | The trace is sampled |
02 | Random trace ID only | Not sampled, and the trace ID was generated randomly |
03 | Sampled and random trace ID | Sampled, and the trace ID was generated randomly |
Where you see them
In the standard web header called traceparent, there are four parts joined by dashes: a version, the trace ID, the parent span ID and the flags. The flags are the last two characters. A full header might end in “01”, which tells the receiver that the sampled switch is on.
| Part of the Header | Example | What It Tells You |
|---|---|---|
| Version | 00 | The format version |
| Trace ID | 0af7651916cd43dd8448eb211c80319c | The shared journey number, 32 characters |
| Parent Span ID | b7ad6b7169203331 | The caller’s span, 16 characters |
| Trace Flags | 01 | The sampled switch is on |
Think of a row of eight light switches by a door. Only the first switch has a label, “filming on.” The second switch has a newer label that not every building understands yet. The other six have blank labels, saved for later. When someone describes the row as “01”, they mean “only the first switch is up.”
4The Sampled Flag in Depth
The one flag every OpenTelemetry user meets, and the reason the others are rarely discussed.
The sampled flag says whether the trace is being kept. When it is on, the caller recorded its part of the trace, and downstream services are encouraged to record theirs as well. When it is off, the caller did not record, and downstream services should normally not record either.
A hint, not a command
The standard describes the sampled flag carefully. It says that the flag indicates that the caller may have recorded trace data, and that when the flag is off, the caller did not record. It is a statement of what the caller did, which the receiver may use as strong guidance. The receiver is still free to make its own decision, within the rules of its own sampler.
| Flag Received | What the Caller Is Saying | What a Typical Receiver Does |
|---|---|---|
| Sampled on | “I recorded my part of this trace” | With parent-based sampling, record too, so the trace stays whole |
| Sampled off | “I did not record my part” | With parent-based sampling, do not record either |
| No header at all | “I did not pass a trace to you” | Start a new trace and make a fresh decision |
How the flag is first created
At the start of a new trace, there is no parent. The sampler, which is the component in the SDK that decides whether to keep a span, makes the first decision. If it says “record and sample,” the sampled switch is turned on in the new span’s flags. If it says “drop,” the switch is left off. Every child span and every call made onward inherits that value, unless a sampler somewhere along the chain deliberately chooses otherwise.
Most SDKs offer a simple helper on the span context, often called “is sampled,” that reads this one switch for you. You rarely need to work with the raw byte yourself.
Why the flag matters even when a trace is dropped
When a trace is not sampled, the span context is still created and still passed on. The next service needs to learn that the decision was “not sampled.” If the context were not passed, the next service would think it was the start of a brand new journey, make its own decision, and perhaps keep a trace that the front door had dropped. That would produce a partial trace, with a root that appears in the middle of the story. So the rule is simple: always pass the context on, even when the flag is off.
Production Example: The Orphan Middle
A team’s gateway dropped ninety percent of traces by sampling. But a proxy between the gateway and the order service stripped the trace header whenever the sampled flag was off, to “save bandwidth”. The order service saw no header, started its own new trace, and kept ten percent of those. The result was a stream of traces that began halfway through the story, and a puzzled team. Fixing the proxy to pass the header unchanged restored whole traces.
5Sampled versus Recording: Two Different Ideas
The most common confusion about trace flags, cleared up.
The words “recording” and “sampled” sound alike, but they mean different things. Understanding the difference makes many odd behaviours make sense.
Recording
A property of one span inside one program. A recording span collects details such as attributes and events. It is not stored in the trace flags.
Sampled
A property of the trace as a whole, stored in the trace flags and passed to every service. It says whether the trace will be kept and exported.
| Recording? | Sampled Flag? | Sampler Decision | What It Means in Practice |
|---|---|---|---|
| No | Off | Drop | The span is a lightweight shell. Nothing is recorded or exported. |
| Yes | Off | Record only | The program collects details for its own use, such as computing metrics from spans, but the trace is not promised to anyone and is normally not exported as a kept trace. |
| Yes | On | Record and sample | The span is collected, marked as sampled, and exported. |
| No | On | Unusual | Can happen when a program has no SDK and simply passes on a sampled parent. It records nothing, but keeps the flag intact for others. |
flowchart TD
A["Sampler is asked about a new span"] --> B{"Which decision?"}
B -->|Drop| C["Not recording. Flag stays off."]
B -->|Record only| D["Recording. Flag stays off."]
B -->|Record and sample| E["Recording. Flag is turned on."]
C --> F["Context with flag off passes to next service"]
D --> F
E --> G["Context with flag on passes to next service"]
Think of a student taking notes in class. Taking notes is “recording.” Handing the notes in to the teacher is “sampled.” A student can take notes without handing them in. A person with no pen and paper at all can still pass along the message “the teacher wants everyone’s notes today,” which is like a program with no SDK passing on a sampled flag without recording anything.
Do not read the trace flags to decide whether your own span is recording. Use the span’s own “is recording” check. The flags describe the trace, and recording describes one span in your program.
6The Random Flag: A Newer Switch
A second switch that helps fairness and counting in advanced sampling.
Many sampling methods use the trace ID as a fair lottery number. For this to be fair, the trace ID must be truly random. If some system builds trace IDs from a counter or from a pattern, such as starting with the date, then a sampler that uses part of the ID as a random number could pick traces unevenly: all the traces from one morning might be kept, or none.
To address this, newer revisions of the W3C trace context standard define a second flag, often called the random trace ID flag. When it is on, the creator of the trace promises that the trace ID, or at least the part of it used for sampling, was generated with proper randomness. Receivers and tools can then trust it for sampling decisions and for estimating totals.
flowchart TD
A["Trace ID arrives with flags"] --> B{"Is the random flag on?"}
B -->|Yes| C["Safe to use the trace ID as a lottery number for consistent sampling"]
B -->|No| D["Do not assume randomness. Use another method or generate a fresh ID"]
Think of raffle tickets. If every ticket number was drawn from a well-shuffled drum, picking all numbers ending in 7 gives a fair share. But if the tickets were printed in order and handed out by street, then “all numbers ending in 7” might all go to one street. A note on the book saying “numbers were drawn from a shuffled drum” is the random flag.
This area is newer than the sampled flag. Support differs between languages, Collectors and backends, and the exact rules have been evolving. Most teams today only rely on the sampled flag. Check the current standard and your tools before depending on the random flag.
7Reserved Bits and Forward Compatibility
Why six switches have no labels, and what to do with them.
The remaining bits in the trace flags byte are reserved. This means the standard keeps them for future uses that have not been defined yet. A good standard plans for the future, and the reserved bits are the room left for it.
The rules for reserved bits
- Do not give them your own meaning. If every vendor invented its own use for the spare switches, two systems could read the same bit in different ways, and trace data would be misread.
- Set them to zero when you create flags. A new trace should begin with the reserved bits off.
- Ignore bits you do not understand. A program that reads a flag byte with an unfamiliar bit turned on should still read the parts it knows, and not reject the whole header.
- Be careful when passing on. Libraries differ in whether they keep or clear unknown bits. Check the behaviour of your propagator, particularly if you rely on a newer flag that your older components do not know.
Think of a form with some boxes marked “reserved for office use.” Applicants should leave those boxes blank, and clerks who do not know what the boxes are for should not scribble in them. When the office later decides how to use them, old forms still work.
Why forward compatibility matters
Because the flags byte is part of a standard used by thousands of products, a new switch can only be added if old software keeps working when it sees the new switch turned on. By ignoring unknown bits, old and new software can share the same traces, and the standard can grow slowly, without a day when everything must change at once.
8Where Trace Flags Live
The same switches appear in several places along a telemetry journey.
The Span Context
Inside your program, the flags are a field of the span context, readable through helper methods such as an “is sampled” check.
The Trace Parent Header
Between services, the flags are the last two hexadecimal characters of the standard trace parent header.
Message Headers
For queues and event streams, the same header text is stored inside the message’s own headers or properties.
Exported Spans
When spans are sent in the standard OpenTelemetry protocol, each span carries a flags field, which also holds extra bits saying whether the parent was remote.
Log Records
A log record can carry the trace flags of the span that was active when it was written, along with the trace and span IDs.
Backends and Viewers
Tracing backends may show whether a span was sampled, and some tools use the flag when linking logs, metrics and traces.
The exported span flags
In the standard protocol used to send telemetry, called OTLP, each span has a field for flags. The lowest eight bits hold the same trace flags we have been discussing. Two further bits record whether the span’s parent was remote, meaning it came from another program, and whether that information is known at all. This extra detail helps tools tell the difference between a span whose parent is missing because it was lost and a span whose parent is simply in another service. The exact fields can differ between protocol versions, so check the version you use.
Flags on log records
The log data model includes a trace flags field next to the trace ID and span ID. This lets a logging pipeline see whether the trace that a log line belongs to was sampled. A pipeline might use this to keep log lines only for sampled traces, or to keep all error logs regardless, depending on policy.
flowchart LR
A["Span context in memory"] --> B["traceparent header on outgoing requests"]
A --> C["Message headers for queues"]
A --> D["Exported span flags in OTLP"]
A --> E["Log record trace flags"]
B --> F["Next service reads the flags"]
C --> F
D --> G["Collector and backend"]
E --> G
9Internal Working: How the Flags Are Set, Inherited and Passed On
Let us follow the flags through the life of a request.
A Request Arrives With No Trace
The first service sees no trace header. It creates a root span, generates a trace ID and a span ID, and asks its sampler for a decision.
The Sampler Sets the First Flag
If the sampler says “record and sample,” the sampled switch is turned on in the new span context. Otherwise it is left off.
Child Spans Inherit
Any child span created inside the same program copies the parent’s flags. A well-behaved parent-based sampler follows the parent’s sampled value.
The Propagator Writes the Header
When making an outgoing call, a component called a propagator writes the span context, including the flags, into the request header.
The Next Service Reads the Header
The receiving service reads the header, builds a remote span context, and uses it as the parent of its new server span.
The Sampler Consults the Parent
With a parent-based sampler, the new span follows the parent’s sampled flag, and the same flags continue in its own outgoing calls.
sequenceDiagram
participant U as User
participant A as Service A with sampler
participant B as Service B parent based
participant C as Service C parent based
U->>A: Request with no trace header
A->>A: New trace. Sampler rolls. Flags become 01
A->>B: traceparent ends with 01
B->>B: Parent sampled so keep. Flags stay 01
B->>C: traceparent ends with 01
C->>C: Parent sampled so keep. Flags stay 01
U->>A: Another request with no trace header
A->>A: New trace. Sampler rolls. Flags become 00
A->>B: traceparent ends with 00
B->>B: Parent not sampled so drop. Flags stay 00
B->>C: traceparent ends with 00
C->>C: Parent not sampled so drop. Flags stay 00
Immutability: changing a flag means making a new context
A span context cannot be edited after it is created. If something needs to change a flag, for example a sampler that wants to override the parent’s decision, it creates a new span context with the new value. This keeps data safe when many parts of a program read the same context at once, and avoids surprising changes in the middle of a request.
What happens in a program with no SDK
If only the API is present and no SDK is installed, the API still reads the incoming context and passes it on unchanged when calling other services. The flags travel through that program untouched, even though it records nothing. This is a clever safety feature: an uninstrumented hop in the middle of a journey does not break the trace or change its sampling decision.
10Flags in Other Propagation Formats
The W3C format is the default, but older and cloud-specific formats carry the same idea in their own way.
Before the W3C standard, various tools invented their own headers. Many companies still run systems that speak these formats. OpenTelemetry’s propagators, the components that read and write headers, can translate between them and the common internal span context.
| Format | How the Sampling Decision Appears | Special Extras |
|---|---|---|
| W3C Trace Context | The flags byte, with the lowest bit as sampled | Newer revisions add a random trace ID flag |
| B3, from Zipkin | A sampled value of 1 or 0, in a single header or in separate headers | A debug value that means “force this trace to be kept” |
| Jaeger | A flags field inside the combined trace header, with the lowest bit as sampled | A debug bit, and a separate debug identifier header, used to force tracing |
| AWS X-Ray | A sampled value of 1 or 0 in the trace header | A value that means “sampling decision is requested from the receiver” |
| Google Cloud trace header | An options value at the end of the header, where 1 means traced | Tied to Google’s own trace identity format |
flowchart LR
A["Incoming B3 header with sampled 1"] --> P["Propagator reads it"]
B["Incoming W3C header with flags 01"] --> P
C["Incoming Jaeger header with sampled bit"] --> P
P --> S["Internal span context with sampled flag on"]
S --> O1["Outgoing W3C header with flags 01"]
S --> O2["Outgoing B3 header with sampled 1"]
The “debug” idea and why it gets lost
Some older formats have a special debug value, meaning “keep this trace no matter what the sampler would say.” Engineers use it to force a trace of a specific request while investigating a problem. The W3C standard has no debug flag. When a debug request is translated into the W3C format, the information that it was a forced trace is reduced to the plain sampled flag. Downstream services that follow the W3C format will see “sampled,” but they will not know it was forced. This can matter if a downstream sampler would override the parent’s decision.
When you must support several formats, configure the composite propagator with all the formats you need, and list your preferred one first. During a migration, run both old and new formats for a while, and watch the traces to make sure the sampling decision survives each hop.
Production Example: The Lost Debug Request
A support engineer used a debug header to force a trace of one customer’s failing request. The edge service honoured it, but the trace was cut off after the second service, because a newer service only understood the W3C format and dropped the older debug headers. Adding the older format to that service’s propagator list allowed the forced trace to continue to the end.
11Trace Flags, Trace State and Baggage: Three Carriers
Three different ways to carry information with a request. Each has its own purpose.
| Feature | Trace Flags | Trace State | Baggage |
|---|---|---|---|
| Purpose | Standard switches about the trace | Vendor notes that travel with the trace identity | Your own notes for downstream services |
| Size | One byte | Small list, limited in size | Can grow, so keep it small |
| Who defines the content? | The standard | Vendors and tools, within the standard’s rules | You |
| Part of the span context? | Yes | Yes | No, it travels separately |
| Typical example | Sampled on | A note about how a trace was sampled | A customer tier or an experiment name |
| Everyday picture | Tick boxes on a permission slip | Stamps from different offices | A sticky note on the parcel |
How they cooperate in advanced sampling
Some newer sampling designs use the trace state to record information about the sampling probability, such as a threshold value, and use the trace flags to indicate whether the trace ID is random. Together they allow later services to know both whether a trace was sampled and how likely it was to be sampled, so that counts can be scaled back up. The flags give the yes-or-no answer, and the state gives extra detail.
Do not try to use trace flags, or the reserved bits, as a place for your own information. Use baggage for your own notes, and attributes for details of a single span. Flags are a standard, shared language, and changing their meaning breaks it for everyone.
12Flags and Sampling Strategies
How the flag fits with head sampling, parent-based sampling and tail sampling.
Head sampling and the flag
In head sampling, the decision is made at the start of the trace by the sampler in the SDK. The flag is the vehicle that carries the decision. It is the core of how head sampling stays consistent across services.
Parent-based sampling and the flag
The parent-based sampler reads the incoming flag and chooses what to do for the new span. It has separate rules for whether the parent was sampled or not, and whether the parent was local or remote. This means you can control how much you trust incoming flags: follow them fully inside your own network, and treat them as hints at a public edge.
Tail sampling and the flag
Tail sampling is different. In tail sampling, the decision is made later in the Collector, after the trace is complete. The SDKs usually record and send everything, so they behave as if the sampled flag were on. The real decision is made in the Collector, which looks at the whole trace. In this design, the flag in the data may not reflect the final decision, since the decision came after. Do not use the flag on a stored span to find out whether a trace “was sampled by tail sampling”. By definition, any trace in storage was kept.
flowchart TD
A["Application SDK"] --> B{"Which strategy?"}
B -->|Head sampling| C["Sampler decides at start and sets the flag"]
B -->|Tail sampling| D["SDK keeps everything and sends it, flag usually on"]
C --> E["Dropped traces never leave the application"]
C --> F["Kept traces go to the Collector"]
D --> G["Collector holds each trace and decides at the end"]
F --> H["Backend stores kept traces"]
G --> H
The arithmetic across both layers
If you use both head sampling and tail sampling, remember that the head decision comes first. A trace that was dropped at the start, with the flag off, is never sent to the Collector, so the Collector cannot rescue it, even if it later turns out to contain an error. If you want the Collector to keep every error, the head sampler must not drop that trace before the error happens.
Think of a photo booth at a fair that has a rule: “only take photos of people wearing a blue badge.” A manager later reviews all the photos to keep the best ones. The manager can only choose among photos that were taken. A person without a badge was never photographed, so the manager never sees them, however photogenic they were.
13Flags, Logs, Metrics and Exemplars
The flag is a small bridge between traces and the other signals.
Logs
When a log record is written while a span is active, it can carry the trace ID, the span ID and the trace flags. The flags tell a logging pipeline whether the linked trace was kept. This is useful in several ways.
- Link checking: a tool can show a “view trace” button only for logs whose trace was sampled, since the trace for other logs does not exist.
- Cost control: a pipeline can keep the logs of sampled traces in full, and keep fewer of the rest, matching the trace policy.
- Error protection: a pipeline can keep all error logs regardless of the flag, so that important evidence is not lost.
flowchart TD
L["Log record with trace ID and flags"] --> Q{"Is the sampled flag on?"}
Q -->|Yes| K["Keep and link to the stored trace"]
Q -->|No| E{"Is it an error or warning?"}
E -->|Yes| K2["Keep, but note that no trace exists"]
E -->|No| D["Drop or keep at a low rate"]
Metrics and exemplars
Metrics recorded through the metrics API do not depend on the sampled flag, so counts stay accurate. Exemplars are different. An exemplar is an example trace ID attached to a metric measurement, so that you can jump from a graph spike to a real trace. By default, SDKs usually attach exemplars only from spans whose flag says “sampled,” because linking to a trace that was never stored would lead to nothing. So the sampled flag also decides which metric points can carry a useful link.
Production Example: Graph to Trace in One Click
An engineer sees a latency spike on a chart. A few of the points carry exemplars, because the traces behind them were sampled. A click opens one such trace, where a database lock stands out. The points without exemplars come from traces that were not sampled, so there was nothing to open. The team then raises the sampling rate for the checkout route, so that more points on its charts can lead to traces.
14Data Flow and Lifecycle: A Flag From Birth to Dashboard
Follow one flag from the moment it is created to the moment an engineer sees its effect.
Birth at the Front Door
A root span is created and the sampler turns the sampled switch on or leaves it off.
Inheritance Inside the Program
Child spans copy the flags from their parent as they are created.
Travel Across the Network
The propagator writes the flags into outgoing request or message headers.
Interpretation by the Next Service
The next service reads the flags and its sampler decides, usually following the parent.
Export With the Span
Sampled spans are exported with their flags, together with extra details about whether the parent was remote.
Use by Logs and Metrics
Log records and exemplars use the flag to decide how to link or what to keep.
Display in the Backend
The tracing tool stores the spans, and engineers use the traces, which exist because the flag said “keep.”
15Trust at the Edge: Validating Incoming Flags
Anything in a request header was written by someone. At the public edge, that someone may not be friendly.
Inside your own network, trusting the incoming flags is usually fine. At the public edge, where requests come from the open internet, the trace header may have been written by a stranger. Strangers can write anything, including flags you did not expect.
Three kinds of problems
Forced Recording
A caller sets the sampled flag on every request, trying to make you record and store far more than planned, raising your costs.
Broken Headers
A caller sends a header with invalid characters, a wrong length, or a version your software does not know. Software must handle these safely.
Unknown Flags
A caller sets reserved bits on. Software that does not understand them must ignore them and not crash or misbehave.
flowchart TD
A["Request arrives at public edge"] --> B{"Is the traceparent header valid?"}
B -->|No| C["Ignore it and start a new trace"]
B -->|Yes| D{"Do we trust this caller?"}
D -->|Yes| E["Follow the incoming flags"]
D -->|No| F["Treat the sampled flag as a hint and apply our own policy"]
F --> G["Optionally start a new trace and link to the caller"]
The practical controls
- Validate the format. The standard requires specific lengths and lowercase hexadecimal characters. Software should ignore headers that do not follow the rules, and begin a new trace instead.
- Use the remote-parent rules of the sampler. At a public edge, configure what happens when the remote parent is sampled, so a stranger’s “keep” is checked against a ratio or a rate limit rather than obeyed without question.
- Rate limit recording. Put a cap on the number of traces recorded per second at the edge, no matter what the flags say.
- Consider fresh traces with links. Start your own trace for requests from outsiders, and record the caller’s trace as a link, so that cross-company debugging is still possible.
Production Example: The Flag Flood
A public API’s tracing bill suddenly tripled. Investigation showed that a partner’s library always set the sampled flag on every request, and the gateway followed it blindly. Changing the gateway’s rule for remote sampled parents from “always follow” to “apply a ratio” returned the bill to normal, with no change on the partner’s side.
16Advantages, Disadvantages and Trade-offs
An honest look at what trace flags give and what they cost.
Advantages
- Tiny: one byte adds almost nothing to each request
- Standard: understood by many languages, vendors and tools
- No meetings needed: one decision reaches every service on its own
- Keeps traces whole: everyone follows the same keep or drop choice
- Works through uninstrumented hops: the flag passes through unchanged
- Room to grow: reserved bits allow future switches without breaking old software
Disadvantages
- Only a hint: receivers may follow or override it, so behaviour varies
- Easy to lose: a proxy or library that drops or rewrites headers breaks the chain
- Blind to outcome: set at the start, so it cannot depend on errors or slowness
- Untrusted at the edge: outsiders can set it to abuse your system
- Format mixing: translating between formats can lose details like debug
- Newer flags are still maturing: support for the random flag differs between tools
Key trade-offs
| Decision | Option A | Option B |
|---|---|---|
| Trust incoming flags | Follow them fully, for whole end-to-end traces | Treat them as hints, for cost safety at public edges |
| Header formats | Only W3C, simple and consistent | Several formats, to bridge old systems, with more to maintain |
| Use of newer flags | Adopt early for better sampling math | Wait for broad support to avoid mismatched behaviour |
| Log policy | Tie logs to the sampled flag, saving cost | Keep all logs, losing the saving but keeping all evidence |
| Forced traces | Support a debug-style override for investigations | No override, safer but harder to investigate one request |
17Security and Privacy Considerations
The flags themselves hold no personal data, but how you treat them still matters.
Flags Are Visible
They travel in plain headers. Anyone who can see the request can read them. Never use them as secrets or tokens.
No Proof of Who You Are
A request with the sampled flag on has not proved anything about the caller. Do not grant special treatment because of it.
Forced Sampling
Callers who force recording can raise your storage and network costs. Add limits at public edges.
Guard Debug Switches
A header that forces tracing on is useful for engineers, but outsiders must not be able to use it. Accept it only from trusted callers.
Headers Reach Others
When you call outside services, your trace headers go with the request. This is usually harmless, but you may prefer not to send them to untrusted parties.
Sampled Traces Still Need Cleaning
The flag decides what is kept, not what is inside. Redact personal data and secrets in the traces that are kept.
Never let the flags drive any security decision, such as who may see data or which requests are allowed. They are a convenience for telemetry only, and any caller can set them to any valid value.
18Monitoring, Logging and Troubleshooting
When flags go wrong, the symptoms are visible if you look for them.
- Share of requests arriving with the flag on: compare with the share you expect. A jump may mean a partner is forcing recording.
- Share of spans recorded versus flag on: if many requests arrive with the flag on but few spans are exported, exporters or processors may be failing.
- Traces with only one or two spans: suggest that flags or headers are lost between services.
- Roots in the middle of the system: a trace whose first span is deep inside the system suggests a hop that drops the header.
- Invalid header counts: count malformed or unreadable headers at your gateways.
- Per-service keep rates: each service should keep about the same share. One that differs may not follow the parent.
- Log link success: measure how often a log’s trace link leads to a stored trace.
- Format mismatches: during migrations, watch for traces that break at the boundary between systems that use different formats.
Common symptoms and likely causes
| Symptom | Likely Cause | What to Try |
|---|---|---|
| Traces have missing middle steps | A service makes its own decision and ignores the incoming flag | Use a parent-based sampler in every service |
| Traces start in the middle | A proxy or library drops the header when the flag is off | Make every hop pass the header unchanged |
| Tracing bill suddenly rises | Callers force the flag on, and the edge follows blindly | Apply a ratio or rate limit for remote sampled parents at the edge |
| Debug override stops working partway | A service understands only W3C and drops older debug headers | Add the older format to the propagator list |
| Logs show trace links that lead nowhere | The trace was not sampled, so none is stored | Check the flag on the log, and consider keeping more traces for key routes |
| Flag seems to show on although traces are missing | Spans were created but exporters or Collector steps dropped them | Check exporters, queues and Collector filters |
| Header ignored by one service | Header is malformed, such as uppercase characters or wrong length | Check the format rules, and fix whichever component writes the header |
19Design Patterns and Anti-Patterns
Habits worth copying and traps worth avoiding.
Good design patterns
Decide Once, Carry Everywhere
Let one front door make the keep or drop decision, and let all other services follow the flag.
Always Pass the Context
Pass trace headers on every hop, even when the flag is off, so the decision is never lost.
Validate at the Edge
Check incoming headers for format and trust, and apply your own policy for outsiders.
Let Libraries Write Headers
Use propagators and instrumentation libraries, instead of building header text by hand.
Flag-Aware Logging
Use the flag on log records to align log keeping with trace sampling, and keep all errors.
End-to-End Propagation Test
Run a simple test through all key services that checks the flag stays the same from first to last.
Anti-patterns
The Mistake
Dropping or stripping trace headers when the sampled flag is off.
Why It Hurts
The next service believes a new journey has started and makes its own decision, producing traces with roots in the middle.
Better Approach
Always pass the header on unchanged, whatever the flag says.
The Mistake
Hard-coding the flags value, such as always writing “01” in a hand-built header.
Why It Hurts
Every trace appears sampled, whatever the real decision, and downstream services record data that should have been dropped.
Better Approach
Use the standard propagator to write the real flags from the real span context.
The Mistake
Using the reserved bits or the trace flags for your own purposes.
Why It Hurts
Other systems may read the same bits in a different way, and a future standard may assign them a meaning that clashes with yours.
Better Approach
Use baggage for your own notes and span attributes for details.
The Mistake
Trusting the sampled flag from the public internet without limits.
Why It Hurts
Anyone can force you to record, store and pay for as many traces as they like.
Better Approach
Apply a ratio or rate limit for remote sampled parents at the edge, or start fresh traces and link.
The Mistake
Reading the flags to decide whether a span is recording.
Why It Hurts
Recording and sampled are different. The flag may be on for a non-recording pass-through span, or off for a record-only span.
Better Approach
Use the span’s own “is recording” check for local decisions.
The Mistake
Using the sampled flag as a security signal, such as “sampled requests are from trusted users”.
Why It Hurts
Anyone can set the flag. Treating it as proof of anything invites abuse.
Better Approach
Base security decisions on real authentication and authorisation, never on telemetry headers.
20Best Practices and Common Mistakes
A practical checklist for flags you can trust.
Best practices
- Use the standard format. Make W3C Trace Context your default, and add other formats only where old systems need them.
- Check every hop. Gateways, proxies, load balancers and service meshes must pass the trace headers through, with the flags unchanged.
- Use parent-based sampling everywhere. Let the front door decide, and let everyone else follow.
- Treat the edge differently. Decide how much to trust incoming flags from outsiders, and apply limits.
- Let libraries handle headers. Do not build or parse trace headers by hand.
- Respect reserved bits. Ignore what you do not understand, and never assign your own meaning.
- Mind the debug gap. If you rely on forced traces, test that they survive across formats and hops.
- Use flags on logs. Align log keeping with trace sampling, but always keep errors.
- Watch the numbers. Track the share of flags on, the share of traces kept and the count of broken or invalid headers.
- Test propagation end to end. Add a simple check that the flag arrives unchanged at the last service.
- Read the standard when adopting newer flags. Check tool support before you rely on the random flag.
Common mistakes beginners make
- Thinking trace flags are the same as the trace state, or the baggage.
- Believing the sampled flag is a command that every service must obey.
- Confusing “sampled” with “recording”.
- Reading the two hex characters as a number rather than as switches. “01” means the lowest switch is on, not “one percent”.
- Dropping the headers for unsampled traces.
- Hard-coding flag values in manual headers.
- Expecting the flag on stored spans to show whether tail sampling kept them.
- Using flags for security decisions.
When a trace looks broken, the first question to ask is, “Did the header with the flags arrive unchanged at each hop?” A quick test is to log the trace header at the entry and exit of each service for a single test request, and compare.
21Real-World and Industry Examples
How trace flags appear in tools and platforms you may already know.
W3C Trace Context
The standard that defines the trace parent header and its flags was developed by the World Wide Web Consortium, with input from major tracing vendors and cloud providers. Its goal was to replace many incompatible headers with one that all products could read, so that a trace can cross company and product boundaries without breaking.
Zipkin and B3
Zipkin popularised passing a sampled value, and a debug value, in request headers, in the format known as B3. Many companies still run services that use it. OpenTelemetry’s B3 propagator translates the sampled idea into the common span context, so mixed systems work together.
Jaeger and Debug Traces
Jaeger, which began at Uber and is now a CNCF project, uses a flags field with a sampled bit and a debug bit, and supports a special header to force a trace for troubleshooting. This is a well-known example of the debug idea that the W3C format does not include.
AWS X-Ray and Google Cloud
AWS X-Ray carries a sampled value in its own trace header, with a special value meaning that the receiver should decide. Google Cloud uses an options value at the end of its own header, where one means the request is traced. OpenTelemetry propagators for both let applications on those clouds take part in traces that cross into other systems.
Proxies and Service Meshes
Proxies such as Envoy, and service meshes built on them, read and write trace headers and can make or follow sampling decisions at the network edge. When they follow the W3C standard, the sampled flag they set is understood by the applications behind them, giving one consistent decision for the whole request path.
A typical journey for a growing company
Turn On Default Propagation
Use W3C Trace Context in all services, so flags travel with every request.
Fix the Gaps
Find hops that drop or rewrite headers, such as proxies and old libraries, and make them pass headers unchanged.
Use Parent-Based Sampling
Let one front door decide and everyone else follow the flag, so traces stay whole.
Protect the Edge
Validate incoming headers and limit how far outsiders can force recording.
Connect the Signals
Use flags on logs and exemplars to link logs, metrics and traces, and monitor the numbers regularly.
22Frequently Asked Questions
Quick answers to questions beginners ask most.
23Summary and Key Takeaways
If you remember only a few things, remember these.
Key Takeaways
- Trace flags are the tick boxes on the trace’s permission slip. They are one byte of on and off switches inside the span context, written as the last two hex characters of the trace parent header.
- The sampled flag is the one that matters most. Bit zero says whether the trace is being kept, so a single decision at the front door reaches every service without any meeting.
- It is a strong hint, not a command. Parent-based sampling normally follows it, but receivers can apply their own policy, especially at public edges.
- Sampled is not the same as recording. Recording is a local fact about a span, and sampled is a shared fact about the trace. Always pass the context on, even when the flag is off.
- Other bits are for the future. A newer random flag marks trustworthy trace IDs, and the rest are reserved. Ignore unknown bits and never invent your own meanings.
- The flags connect the signals. They appear on exported spans and on log records, and decide which metric points can carry exemplars that lead to real traces.
- Protect and verify. Treat incoming flags from strangers as untrusted, validate headers, limit forced recording, test propagation end to end, and never use flags for security decisions.