Skip to content

feat(trace): add allocation-free to_hex() for TraceId and SpanId#3557

Open
BRGOVIND wants to merge 2 commits into
open-telemetry:mainfrom
BRGOVIND:feat/allocation-free-hex-encoding
Open

feat(trace): add allocation-free to_hex() for TraceId and SpanId#3557
BRGOVIND wants to merge 2 commits into
open-telemetry:mainfrom
BRGOVIND:feat/allocation-free-hex-encoding

Conversation

@BRGOVIND

Copy link
Copy Markdown

Closes #3544 — adds allocation-free hex encoding methods for TraceId and SpanId.

Problem

Exporters that need the hex representation of a trace/span ID today call .to_string(), which heap-allocates a String on every export event. On hot paths this is two unnecessary allocations per span record.

Solution

This PR adds:

  • HexEncode<N> — a stack-allocated [u8; N] newtype that:
    • implements Deref<Target = str>
    • implements Display and Debug
    • carries zero heap state
  • TraceId::to_hex() -> HexEncode<32> — 32-char lowercase hex, no allocation
  • SpanId::to_hex() -> HexEncode<16> — 16-char lowercase hex, no allocation

HexEncode is re-exported from the crate root alongside TraceId/SpanId.

Usage

Before:

let s: String = trace_id.to_string();   // heap allocation

After:

let hex = trace_id.to_hex();   // stack only
write_field(&*hex);             // deref to &str, zero-copy

Exporters can switch from format!("{}", trace_id) to trace_id.to_hex() and eliminate both allocations per record.

Exporters that need a hex string for trace/span IDs currently have to
call .to_string() which heap-allocates a String on every export. On hot
paths this is two unnecessary allocations per record.

This commit adds:

- HexEncode<N> — a stack-allocated buffer of N ASCII bytes that derefs
  to &str and implements Display/Debug. No heap allocation, no unsafe
  visible to callers.

- TraceId::to_hex() -> HexEncode<32> — 32-char lowercase hex, no alloc.
- SpanId::to_hex()  -> HexEncode<16> — 16-char lowercase hex, no alloc.

HexEncode is re-exported from the crate root alongside TraceId/SpanId.

Exporters that currently write:
    format!("{}", trace_id)
can switch to:
    let hex = trace_id.to_hex();
    write_field(&*hex)
and avoid the allocation entirely.

Closes open-telemetry#3544

Signed-off-by: B R GOVIND <brgovind2005@gmail.com>
@BRGOVIND
BRGOVIND requested a review from a team as a code owner June 14, 2026 23:47
@linux-foundation-easycla

linux-foundation-easycla Bot commented Jun 14, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: BRGOVIND / name: B R GOVIND (c5eac18)

@codecov

codecov Bot commented Jun 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.9%. Comparing base (c14b570) to head (a8286c5).
⚠️ Report is 20 commits behind head on main.

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #3557   +/-   ##
=====================================
  Coverage   82.9%   82.9%           
=====================================
  Files        130     130           
  Lines      27484   27557   +73     
=====================================
+ Hits       22796   22869   +73     
  Misses      4688    4688           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@BRGOVIND BRGOVIND changed the title trace: add allocation-free to_hex() for TraceId and SpanId feat(trace): add allocation-free to_hex() for TraceId and SpanId Jun 15, 2026
Cover HexEncode<N>: as_str(), Deref to &str, Display, Debug, Clone,
PartialEq, and the all-bytes encoding correctness. Also test the INVALID
sentinel values for both types.

Signed-off-by: B R GOVIND <brgovind2005@gmail.com>
@BRGOVIND

Copy link
Copy Markdown
Author

Added tests in the latest commit to cover the new code paths (fixes the Codecov patch coverage):

  • TraceId::to_hex() and SpanId::to_hex() output matches Display for all test vectors
  • HexEncode deref to &str, Clone, PartialEq, Display, Debug
  • INVALID sentinel values for both types produce the expected all-zero hex strings
  • test_to_hex_all_bytes: round-trips all 256 byte values through the encoder and compares against format!("{}", id)

@BRGOVIND

Copy link
Copy Markdown
Author

Gentle ping — this adds an allocation-free to_hex() for TraceId/SpanId. Would appreciate a maintainer's eyes when there's time; happy to add benchmarks or adjust the API surface if you'd prefer a different shape.

@github-actions

Copy link
Copy Markdown

Thank you for your contribution! This PR has been automatically marked as stale because it has not had activity in the last 14 days. This may be due to a delay in review on our side or awaiting a response from you; either is fine, and we appreciate your patience.

It will be closed in 14 days if no further activity occurs. Pushing a new commit or leaving a comment will remove the stale label and keep the PR open.

@github-actions github-actions Bot added the Stale label Jul 12, 2026
@BRGOVIND

Copy link
Copy Markdown
Author

Thank you for your contribution! This PR has been automatically marked as stale because it has not had activity in the last 14 days. This may be due to a delay in review on our side or awaiting a response from you; either is fine, and we appreciate your patience.

It will be closed in 14 days if no further activity occurs. Pushing a new commit or leaving a comment will remove the stale label and keep the PR open.

Maintainers can u please take a look at the pr ?

@github-actions github-actions Bot removed the Stale label Jul 13, 2026
@cijothomas
cijothomas requested review from cijothomas and Copilot July 13, 2026 16:33
@cijothomas

Copy link
Copy Markdown
Member

Thank you for your contribution! This PR has been automatically marked as stale because it has not had activity in the last 14 days. This may be due to a delay in review on our side or awaiting a response from you; either is fine, and we appreciate your patience.
It will be closed in 14 days if no further activity occurs. Pushing a new commit or leaving a comment will remove the stale label and keep the PR open.

Maintainers can u please take a look at the pr ?

I'll review today

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds allocation-free, stack-based lowercase hex encoding for TraceId and SpanId to avoid per-export heap allocations, along with a reusable HexEncode<N> wrapper type.

Changes:

  • Introduces HexEncode<const N: usize> (stack [u8; N]) that derefs to str and implements Display/Debug.
  • Adds TraceId::to_hex() -> HexEncode<32> and SpanId::to_hex() -> HexEncode<16> using a shared encode_hex_bytes helper.
  • Re-exports HexEncode from the crate root and adds tests validating formatting behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
opentelemetry/src/trace_context.rs Adds HexEncode, to_hex() APIs, encoding helper, and new unit tests.
opentelemetry/src/lib.rs Re-exports HexEncode alongside existing trace ID types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +26 to +30
/// Return the hex string as a `&str`.
pub fn as_str(&self) -> &str {
// SAFETY: the buffer is always filled with ASCII hex digits.
unsafe { std::str::from_utf8_unchecked(&self.0) }
}
Comment on lines +374 to +385
#[test]
fn test_to_hex_all_bytes() {
// Verify every possible byte value encodes correctly.
let mut bytes = [0u8; 16];
for i in 0u8..=255 {
bytes[i as usize % 16] = i;
}
let id = TraceId::from_bytes(bytes);
let hex = id.to_hex();
assert_eq!(hex.as_str(), format!("{}", id));
assert_eq!(hex.len(), 32);
}
Comment thread opentelemetry/src/lib.rs
Comment on lines 255 to +256
mod trace_context;
pub use trace_context::{SpanId, TraceFlags, TraceId};
pub use trace_context::{HexEncode, SpanId, TraceFlags, TraceId};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API] Allocation-free hex encoding for TraceId / SpanId

3 participants