From 0564be227333e0ff0c7ec29c408c29b43a6dfd30 Mon Sep 17 00:00:00 2001 From: Konrad Zemek Date: Wed, 23 Jul 2025 11:36:23 +0200 Subject: [PATCH 1/3] Use `binary:encode_hex/2` instead of `io_lib:format/2` `io_lib:format/2` has very significant overhead over `binary:encode_hex/2`. --- apps/opentelemetry_api/src/otel_span.erl | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/apps/opentelemetry_api/src/otel_span.erl b/apps/opentelemetry_api/src/otel_span.erl index 58887635..bebb626a 100644 --- a/apps/opentelemetry_api/src/otel_span.erl +++ b/apps/opentelemetry_api/src/otel_span.erl @@ -127,21 +127,11 @@ hex_span_ctx(_) -> -spec hex_trace_id(opentelemetry:span_ctx()) -> opentelemetry:hex_trace_id(). hex_trace_id(#span_ctx{trace_id=TraceId}) -> - case otel_utils:format_binary_string("~32.16.0b", [TraceId]) of - {ok, Binary} -> - Binary; - _ -> - <<>> - end. + binary:encode_hex(<>, lowercase). -spec hex_span_id(opentelemetry:span_ctx()) -> opentelemetry:hex_span_id(). hex_span_id(#span_ctx{span_id=SpanId}) -> - case otel_utils:format_binary_string("~16.16.0b", [SpanId]) of - {ok, Binary} -> - Binary; - _ -> - <<>> - end. + binary:encode_hex(<>, lowercase). -spec tracestate(opentelemetry:span_ctx() | undefined) -> otel_tracestate:t(). tracestate(#span_ctx{tracestate=Tracestate}) -> From ca0578970b2cfcd4d3bd01d16760e18e4c0d843c Mon Sep 17 00:00:00 2001 From: Konrad Zemek Date: Fri, 25 Jul 2025 12:05:50 +0200 Subject: [PATCH 2/3] Precompute hex trace and span ids --- apps/opentelemetry/src/otel_span_utils.erl | 14 +++++++++++--- apps/opentelemetry/test/opentelemetry_SUITE.erl | 11 +++++++++++ apps/opentelemetry_api/include/opentelemetry.hrl | 4 ++++ apps/opentelemetry_api/src/otel_span.erl | 8 ++++---- apps/opentelemetry_api/src/otel_utils.erl | 15 ++++++++++++++- .../test/opentelemetry_api_SUITE.erl | 2 +- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/apps/opentelemetry/src/otel_span_utils.erl b/apps/opentelemetry/src/otel_span_utils.erl index 0cb99b27..d4eae1c4 100644 --- a/apps/opentelemetry/src/otel_span_utils.erl +++ b/apps/opentelemetry/src/otel_span_utils.erl @@ -87,12 +87,20 @@ new_span_ctx(Ctx, IdGeneratorModule) -> {root_span_ctx(IdGeneratorModule), undefined, undefined}; ParentSpanCtx=#span_ctx{span_id=ParentSpanId, is_remote=ParentIsRemote} -> %% keep the rest of the parent span ctx, simply need to update the span_id - {ParentSpanCtx#span_ctx{span_id=IdGeneratorModule:generate_span_id()}, ParentSpanId, ParentIsRemote} + SpanId = IdGeneratorModule:generate_span_id(), + HexSpanId = otel_utils:encode_hex(<>), + {ParentSpanCtx#span_ctx{span_id=SpanId, hex_span_id=HexSpanId}, ParentSpanId, ParentIsRemote} end. root_span_ctx(IdGeneratorModule) -> - #span_ctx{trace_id=IdGeneratorModule:generate_trace_id(), - span_id=IdGeneratorModule:generate_span_id(), + TraceId = IdGeneratorModule:generate_trace_id(), + HexTraceId = otel_utils:encode_hex(<>), + SpanId = IdGeneratorModule:generate_span_id(), + HexSpanId = otel_utils:encode_hex(<>), + #span_ctx{trace_id=TraceId, + hex_trace_id=HexTraceId, + span_id=SpanId, + hex_span_id=HexSpanId, is_valid=true, trace_flags=0}. diff --git a/apps/opentelemetry/test/opentelemetry_SUITE.erl b/apps/opentelemetry/test/opentelemetry_SUITE.erl index 5a7deb95..58d14d95 100644 --- a/apps/opentelemetry/test/opentelemetry_SUITE.erl +++ b/apps/opentelemetry/test/opentelemetry_SUITE.erl @@ -1101,6 +1101,17 @@ no_exporter(_Config) -> ok. + +generate_trace_id() -> 41394. +generate_span_id() -> 50132. +pregenerate_hex_ids(_Config) -> + HexTraceId = <<"0000000000000000000000000000a1b2">>, + HexSpanId = <<"000000000000c3d4">>, + SpanCtx = otel_span_utils:root_span_ctx(?MODULE), + ?assertEqual(HexTraceId, SpanCtx#span_ctx.hex_trace_id), + ?assertEqual(HexSpanId, SpanCtx#span_ctx.hex_span_id), + ok. + %% assert_all_exported(Tid, SpanCtxs) -> diff --git a/apps/opentelemetry_api/include/opentelemetry.hrl b/apps/opentelemetry_api/include/opentelemetry.hrl index cd59d3aa..eaf036a7 100644 --- a/apps/opentelemetry_api/include/opentelemetry.hrl +++ b/apps/opentelemetry_api/include/opentelemetry.hrl @@ -49,8 +49,12 @@ -record(span_ctx, { %% 128 bit int trace id trace_id :: opentelemetry:trace_id(), + %% 128 bit int trace id encoded as hex binary string + hex_trace_id :: opentelemetry:hex_trace_id(), %% 64 bit int span id span_id :: opentelemetry:span_id(), + %% 64 bit int span id encoded as hex binary string + hex_span_id :: opentelemetry:hex_span_id(), %% 8-bit integer, lowest bit is if it is sampled trace_flags = 0 :: integer(), %% Tracestate represents tracing-system specific context in a list of key-value pairs. diff --git a/apps/opentelemetry_api/src/otel_span.erl b/apps/opentelemetry_api/src/otel_span.erl index bebb626a..3513e827 100644 --- a/apps/opentelemetry_api/src/otel_span.erl +++ b/apps/opentelemetry_api/src/otel_span.erl @@ -126,12 +126,12 @@ hex_span_ctx(_) -> #{}. -spec hex_trace_id(opentelemetry:span_ctx()) -> opentelemetry:hex_trace_id(). -hex_trace_id(#span_ctx{trace_id=TraceId}) -> - binary:encode_hex(<>, lowercase). +hex_trace_id(#span_ctx{hex_trace_id=HexTraceId}) -> + HexTraceId. -spec hex_span_id(opentelemetry:span_ctx()) -> opentelemetry:hex_span_id(). -hex_span_id(#span_ctx{span_id=SpanId}) -> - binary:encode_hex(<>, lowercase). +hex_span_id(#span_ctx{hex_span_id=HexSpanId}) -> + HexSpanId. -spec tracestate(opentelemetry:span_ctx() | undefined) -> otel_tracestate:t(). tracestate(#span_ctx{tracestate=Tracestate}) -> diff --git a/apps/opentelemetry_api/src/otel_utils.erl b/apps/opentelemetry_api/src/otel_utils.erl index 307e92c9..3b84ba28 100644 --- a/apps/opentelemetry_api/src/otel_utils.erl +++ b/apps/opentelemetry_api/src/otel_utils.erl @@ -20,7 +20,8 @@ format_binary_string/2, format_binary_string/3, assert_to_binary/1, - unicode_to_binary/1]). + unicode_to_binary/1, + encode_hex/1]). -if(?OTP_RELEASE >= 24). format_exception(Kind, Reason, StackTrace) -> @@ -55,3 +56,15 @@ unicode_to_binary(String) -> _ -> {error, bad_binary_conversion} end. + +-spec encode_hex(binary()) -> binary(). +-if(?OTP_RELEASE >= 26). +encode_hex(Bin) -> + binary:encode_hex(Bin, lowercase). +-elif(?OTP_RELEASE >= 24). +encode_hex(Bin) -> + string:lowercase(binary:encode_hex(Bin)). +-else. +encode_hex(Bin) -> + string:lowercase(<< <> || <> <= Bin, Enc <- integer_to_list(HByte, 16) >>). +-endif. diff --git a/apps/opentelemetry_api/test/opentelemetry_api_SUITE.erl b/apps/opentelemetry_api/test/opentelemetry_api_SUITE.erl index a86dc268..d16386d2 100644 --- a/apps/opentelemetry_api/test/opentelemetry_api_SUITE.erl +++ b/apps/opentelemetry_api/test/opentelemetry_api_SUITE.erl @@ -249,7 +249,7 @@ noop_with_span(_Config) -> hex_trace_ids(_Config) -> HexTraceId = <<"0000000000000000000000000000a1b2">>, HexSpanId = <<"000000000000c3d4">>, - SpanCtx=#span_ctx{trace_id=41394, span_id=50132}, + SpanCtx=#span_ctx{trace_id=41394, hex_trace_id=HexTraceId, span_id=50132, hex_span_id=HexSpanId}, ?assertEqual(HexTraceId, otel_span:hex_trace_id(SpanCtx)), ?assertEqual(HexSpanId, otel_span:hex_span_id(SpanCtx)), ?assertEqual(#{otel_trace_id => HexTraceId, From 7fb96fbc3fb345dd096eb77e0a29c5be0bd1b4cf Mon Sep 17 00:00:00 2001 From: Konrad Zemek Date: Thu, 31 Jul 2025 19:51:04 +0200 Subject: [PATCH 3/3] Prefill hex IDs in remaining span_ctx constructors --- apps/opentelemetry/src/otel_span_ets.erl | 2 ++ apps/opentelemetry_api/src/otel_tracer.erl | 4 ++++ apps/opentelemetry_api/src/otel_tracer_noop.erl | 2 ++ apps/opentelemetry_api/test/otel_propagators_SUITE.erl | 6 ++++++ 4 files changed, 14 insertions(+) diff --git a/apps/opentelemetry/src/otel_span_ets.erl b/apps/opentelemetry/src/otel_span_ets.erl index 94208c27..720ebaac 100644 --- a/apps/opentelemetry/src/otel_span_ets.erl +++ b/apps/opentelemetry/src/otel_span_ets.erl @@ -102,7 +102,9 @@ get_ctx(#span{trace_id=TraceId, tracestate=TraceState, is_recording=IsRecording}) -> #span_ctx{trace_id=TraceId, + hex_trace_id=otel_utils:encode_hex(<>), span_id=SpanId, + hex_span_id=otel_utils:encode_hex(<>), tracestate=TraceState, is_recording=IsRecording}. diff --git a/apps/opentelemetry_api/src/otel_tracer.erl b/apps/opentelemetry_api/src/otel_tracer.erl index eccd2b08..7751d934 100644 --- a/apps/opentelemetry_api/src/otel_tracer.erl +++ b/apps/opentelemetry_api/src/otel_tracer.erl @@ -90,7 +90,9 @@ with_span(Ctx, Tracer={Module, _}, SpanName, Opts, Fun) when is_atom(Module) -> -> opentelemetry:span_ctx(). non_recording_span(TraceId, SpanId, Traceflags) -> #span_ctx{trace_id=TraceId, + hex_trace_id=otel_utils:encode_hex(<>), span_id=SpanId, + hex_span_id=otel_utils:encode_hex(<>), is_recording=false, trace_flags=Traceflags}. @@ -100,7 +102,9 @@ non_recording_span(TraceId, SpanId, Traceflags) -> -> opentelemetry:span_ctx(). from_remote_span(TraceId, SpanId, Traceflags) -> #span_ctx{trace_id=TraceId, + hex_trace_id=otel_utils:encode_hex(<>), span_id=SpanId, + hex_span_id=otel_utils:encode_hex(<>), is_valid=true, is_recording=false, is_remote=true, diff --git a/apps/opentelemetry_api/src/otel_tracer_noop.erl b/apps/opentelemetry_api/src/otel_tracer_noop.erl index 4a805ce2..e96a2b9f 100644 --- a/apps/opentelemetry_api/src/otel_tracer_noop.erl +++ b/apps/opentelemetry_api/src/otel_tracer_noop.erl @@ -28,7 +28,9 @@ -include("otel_tracer.hrl"). -define(NOOP_SPAN_CTX, #span_ctx{trace_id=0, + hex_trace_id= <<"00000000000000000000000000000000">>, span_id=0, + hex_span_id= <<"0000000000000000">>, trace_flags=0, tracestate=otel_tracestate:new(), is_valid=false, diff --git a/apps/opentelemetry_api/test/otel_propagators_SUITE.erl b/apps/opentelemetry_api/test/otel_propagators_SUITE.erl index 6f7b3c00..994c0bca 100644 --- a/apps/opentelemetry_api/test/otel_propagators_SUITE.erl +++ b/apps/opentelemetry_api/test/otel_propagators_SUITE.erl @@ -70,7 +70,9 @@ rewrite(_Config) -> otel_ctx:clear(), RecordingSpanCtx = #span_ctx{trace_id=21267647932558653966460912964485513216, + hex_trace_id= <<"10000000000000000000000000000000">>, span_id=1152921504606846976, + hex_span_id= <<"1000000000000000">>, is_valid=true, is_recording=true}, otel_tracer:set_current_span(RecordingSpanCtx), @@ -117,7 +119,9 @@ invalid_span_no_sdk_propagation(_Config) -> otel_ctx:clear(), InvalidSpanCtx = #span_ctx{trace_id=0, + hex_trace_id= <<"00000000000000000000000000000000">>, span_id=0, + hex_span_id= <<"0000000000000000">>, trace_flags=0, is_valid=false, is_recording=false}, @@ -142,7 +146,9 @@ nonrecording_no_sdk_propagation(_Config) -> otel_ctx:clear(), NonRecordingSpanCtx = #span_ctx{trace_id=21267647932558653966460912964485513216, + hex_trace_id= <<"10000000000000000000000000000000">>, span_id=1152921504606846976, + hex_span_id= <<"1000000000000000">>, is_valid=true, is_recording=false}, ?set_current_span(NonRecordingSpanCtx),