|
2 | 2 |
|
3 | 3 | -include_lib("opentelemetry_api/include/opentelemetry.hrl"). |
4 | 4 |
|
5 | | --export([pack_otel_stub/1]). |
6 | | --export([restore_otel_stub/2]). |
7 | | --export([maybe_attach_otel_ctx/1]). |
8 | | - |
9 | 5 | -export([span_start/3]). |
10 | 6 | -export([span_end/1]). |
11 | 7 | -export([record_current_span_ctx/3]). |
|
27 | 23 |
|
28 | 24 | %% |
29 | 25 |
|
30 | | -%% @doc Packs OTEL context for storage. |
31 | | --spec pack_otel_stub(otel_ctx:t()) -> packed_otel_stub(). |
32 | | -pack_otel_stub(Ctx) -> |
33 | | - case otel_tracer:current_span_ctx(Ctx) of |
34 | | - undefined -> |
35 | | - []; |
36 | | - #span_ctx{trace_id = TraceID, span_id = SpanID, trace_flags = TraceFlags} -> |
37 | | - [trace_id_to_binary(TraceID), span_id_to_binary(SpanID), TraceFlags] |
38 | | - end. |
39 | | - |
40 | | -%% @doc Restores OTEL context with current span. Restored span context |
41 | | -%% status is nor actual nor have according data in OTEL storage |
42 | | -%% backend. Its only purpose is to preserve ability to start new child |
43 | | -%% spans in compliance with OTEL tracer API. |
44 | | -%% |
45 | | -%% Restored otel span can be unfinished if machine is interrupted |
46 | | -%% with node stop, thus span data is lost anyway. |
47 | | -%% |
48 | | -%% We can't get around this issue without implementing our own |
49 | | -%% tracer with distributed storage with write order guarantee. |
50 | | -%% |
51 | | -%% However we can start new span for 'resumption' signal. And set |
52 | | -%% original machine start call as its parent. Same goes for 'timeouts' |
53 | | -%% and 'retries' signals. |
54 | | --spec restore_otel_stub(otel_ctx:t(), packed_otel_stub()) -> otel_ctx:t(). |
55 | | -restore_otel_stub(Ctx, [TraceID, SpanID, TraceFlags]) -> |
56 | | - SpanCtx = otel_tracer:from_remote_span(binary_to_id(TraceID), binary_to_id(SpanID), TraceFlags), |
57 | | - %% NOTE Thus restored span context is considered being remote and not recording. |
58 | | - otel_tracer:set_current_span(Ctx, SpanCtx); |
59 | | -restore_otel_stub(Ctx, _Other) -> |
60 | | - Ctx. |
61 | | - |
62 | | --spec maybe_attach_otel_ctx(otel_ctx:t()) -> ok. |
63 | | -maybe_attach_otel_ctx(NewCtx) when map_size(NewCtx) =:= 0 -> |
64 | | - %% Don't attach empty context |
65 | | - ok; |
66 | | -maybe_attach_otel_ctx(NewCtx) -> |
67 | | - _ = otel_ctx:attach(choose_viable_otel_ctx(NewCtx, otel_ctx:get_current())), |
68 | | - ok. |
69 | | - |
70 | | -%% lowest bit is if it is sampled |
71 | | --define(IS_NOT_SAMPLED(SpanCtx), SpanCtx#span_ctx.trace_flags band 2#1 =/= 1). |
72 | | - |
73 | | --spec choose_viable_otel_ctx(T, T) -> T when T :: otel_ctx:t(). |
74 | | -choose_viable_otel_ctx(NewCtx, CurrentCtx) -> |
75 | | - case {otel_tracer:current_span_ctx(NewCtx), otel_tracer:current_span_ctx(CurrentCtx)} of |
76 | | - %% Don't attach if new context is without sampled span and old |
77 | | - %% context has span defined |
78 | | - {SpanCtx = #span_ctx{}, #span_ctx{}} when ?IS_NOT_SAMPLED(SpanCtx) -> CurrentCtx; |
79 | | - {undefined, #span_ctx{}} -> CurrentCtx; |
80 | | - {_, _} -> NewCtx |
81 | | - end. |
82 | | - |
83 | 26 | -spec span_start(term(), opentelemetry:span_name(), otel_span:start_opts()) -> ok. |
84 | 27 | span_start(Key, SpanName, Opts) -> |
85 | 28 | Tracer = opentelemetry:get_application_tracer(?MODULE), |
@@ -166,53 +109,3 @@ span_id(#span_ctx{span_id = SpanID}) -> |
166 | 109 | SpanID; |
167 | 110 | span_id(_) -> |
168 | 111 | undefined. |
169 | | - |
170 | | --spec trace_id_to_binary(opentelemetry:trace_id()) -> binary(). |
171 | | -trace_id_to_binary(TraceID) -> |
172 | | - {ok, EncodedTraceID} = otel_utils:format_binary_string("~32.16.0b", [TraceID]), |
173 | | - EncodedTraceID. |
174 | | - |
175 | | --spec span_id_to_binary(opentelemetry:span_id()) -> binary(). |
176 | | -span_id_to_binary(SpanID) -> |
177 | | - {ok, EncodedSpanID} = otel_utils:format_binary_string("~16.16.0b", [SpanID]), |
178 | | - EncodedSpanID. |
179 | | - |
180 | | --spec binary_to_id(binary()) -> non_neg_integer(). |
181 | | -binary_to_id(Opaque) when is_binary(Opaque) -> |
182 | | - binary_to_integer(Opaque, 16). |
183 | | - |
184 | | --ifdef(TEST). |
185 | | --include_lib("eunit/include/eunit.hrl"). |
186 | | - |
187 | | --type testgen() :: {_ID, fun(() -> _)}. |
188 | | --spec test() -> _. |
189 | | - |
190 | | --define(IS_SAMPLED, 1). |
191 | | --define(NOT_SAMPLED, 0). |
192 | | --define(OTEL_CTX(IsSampled), |
193 | | - otel_tracer:set_current_span( |
194 | | - otel_ctx:new(), |
195 | | - (otel_tracer_noop:noop_span_ctx())#span_ctx{ |
196 | | - trace_id = otel_id_generator:generate_trace_id(), |
197 | | - span_id = otel_id_generator:generate_span_id(), |
198 | | - is_valid = true, |
199 | | - is_remote = true, |
200 | | - is_recording = false, |
201 | | - trace_flags = IsSampled |
202 | | - } |
203 | | - ) |
204 | | -). |
205 | | - |
206 | | --spec choose_viable_otel_ctx_test_() -> [testgen()]. |
207 | | -choose_viable_otel_ctx_test_() -> |
208 | | - A = ?OTEL_CTX(?IS_SAMPLED), |
209 | | - B = ?OTEL_CTX(?NOT_SAMPLED), |
210 | | - [ |
211 | | - ?_assertEqual(A, choose_viable_otel_ctx(A, B)), |
212 | | - ?_assertEqual(A, choose_viable_otel_ctx(B, A)), |
213 | | - ?_assertEqual(A, choose_viable_otel_ctx(A, otel_ctx:new())), |
214 | | - ?_assertEqual(B, choose_viable_otel_ctx(otel_ctx:new(), B)), |
215 | | - ?_assertEqual(otel_ctx:new(), choose_viable_otel_ctx(otel_ctx:new(), otel_ctx:new())) |
216 | | - ]. |
217 | | - |
218 | | --endif. |
0 commit comments