diff --git a/.circleci/config.yml b/.circleci/config.yml index 8e39b6da..04c496c3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - rebar3: tsloughter/rebar3@0.7.0 + rebar3: tsloughter/rebar3@0.8.1 codecov: circleci/codecov@0.0.3 jobs: @@ -18,33 +18,22 @@ jobs: - codecov/upload: path: _build/test/covertool/opentelemetry.covertool.xml -erlang21: &erlang21 +erlang22: &erlang22 executor: name: rebar3/erlang - tag: "21" + tag: "22" -erlang22: &erlang22 +erlang21: &erlang21 executor: name: rebar3/erlang - tag: "22" + tag: "21" workflows: otp21: jobs: - rebar3/compile: <<: *erlang21 - - rebar3/xref: - <<: *erlang21 - requires: - - rebar3/compile - - rebar3/dialyzer: - <<: *erlang21 - requires: - - rebar3/compile - - rebar3/ct: - <<: *erlang21 - requires: - - rebar3/compile + otp22: jobs: - rebar3/compile: diff --git a/README.md b/README.md index c9ccdc35..5bf09ba3 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ Requires OTP 21.3 of above. ## Design +The [OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification) defines a language library as having 2 components, the API and the SDK. The API must not only define the interfaces of any implementation in that language but also be able to function as a minimal implementation. The SDK is the default implementation of the API that must be optional. + +In this library the API is defined as the behaviours `ot_tracer`, `ot_span` and `ot_ctx`. It is not a separate Erlang application from the SDK, nor are there subdirectories following the [layout described in the spec](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/library-layout.md). Not using subdirectories as the spec's layout describes is simply because the OTP application structure has all source files directly under `src/`. There is no separation of API and SDK into distinct libraries because a) there is not yet support in rebar3 for using git repos with multiple applications as dependencies b) since Erlang has a flat namespace the same module naming strategy would be used whether they are separate applications or not. Additionally, a release can be configured to drop any part of an application the user chooses. So in the unlikely event there are users who wish to not include the SDK's default implementation it is possible. + +### Default Tracer + ## Benchmarks Running benchmarks is done with [benchee](https://github.com/bencheeorg/benchee). Benchmark functions are in modules under `samples/`. To run them open a rebar3 shell in the `bench` profile: diff --git a/src/opentelemetry.app.src b/src/opentelemetry.app.src index 32906a0f..13c9e5f1 100644 --- a/src/opentelemetry.app.src +++ b/src/opentelemetry.app.src @@ -8,7 +8,8 @@ stdlib, wts ]}, - {env,[]}, + {env, [{tracer, {ot_tracer_default, #{span => {ot_span_ets, []}, + ctx => {ot_ctx_pdict, []}}}}]}, {modules, []}, {licenses, ["Apache 2.0"]}, diff --git a/src/opentelemetry_app.erl b/src/opentelemetry_app.erl index 8084b128..06b3d4ec 100644 --- a/src/opentelemetry_app.erl +++ b/src/opentelemetry_app.erl @@ -23,7 +23,13 @@ start(_StartType, _StartArgs) -> Opts = application:get_all_env(opentelemetry), - opentelemetry_sup:start_link(Opts). + + %% if the span impl needs to have a process supervised it must be + %% setup after the supervision tree has started. + {tracer, {Tracer, TracerOpts}} = lists:keyfind(tracer, 1, Opts), + Children = ot_tracer:setup(Tracer, TracerOpts), + + opentelemetry_sup:start_link(Children, Opts). stop(_State) -> ok. diff --git a/src/opentelemetry_sup.erl b/src/opentelemetry_sup.erl index 9a1f5b33..60098742 100644 --- a/src/opentelemetry_sup.erl +++ b/src/opentelemetry_sup.erl @@ -19,22 +19,22 @@ -behaviour(supervisor). --export([start_link/1]). +-export([start_link/2]). -export([init/1]). -define(SERVER, ?MODULE). -start_link(Opts) -> - supervisor:start_link({local, ?SERVER}, ?MODULE, [Opts]). +start_link(Children, Opts) -> + supervisor:start_link({local, ?SERVER}, ?MODULE, [Children, Opts]). -init([Opts]) -> +init([Children, Opts]) -> SupFlags = #{strategy => one_for_one, intensity => 0, period => 1}, ChildSpecs = [#{id => ot_span_sup, start => {ot_span_sup, start_link, [Opts]}, - type => supervisor}], + type => supervisor} | Children], {ok, {SupFlags, ChildSpecs}}. %% internal functions diff --git a/src/ot_ctx.erl b/src/ot_ctx.erl index 39937e9e..ed098122 100644 --- a/src/ot_ctx.erl +++ b/src/ot_ctx.erl @@ -17,30 +17,7 @@ %%%------------------------------------------------------------------------- -module(ot_ctx). --export([with_value/2, - with_value/3, - get/1, - get/2]). - -callback get(term()) -> term(). -callback get(term(), term()) -> term(). -callback with_value(term(), term()) -> ok. -callback with_value(term(), term(), fun()) -> ok. - --define(ctx, (persistent_term:get({opentelemetry, ctx}, ot_ctx_pdict))). - --spec get(term()) -> term(). -get(Key) -> - ?ctx:get(Key). - --spec get(term(), term()) -> term(). -get(Key, Default) -> - ?ctx:get(Key, Default). - --spec with_value(term(), term()) -> ok. -with_value(Key, Value) -> - ?ctx:with_value(Key, Value). - --spec with_value(term(), term(), fun()) -> ok. -with_value(Key, Value, Fun) -> - ?ctx:with_value(Key, Value, Fun). diff --git a/src/ot_span.erl b/src/ot_span.erl index 251e33e9..cb9f52dd 100644 --- a/src/ot_span.erl +++ b/src/ot_span.erl @@ -13,18 +13,11 @@ %% limitations under the License. %% %% @doc -%% Functional interface for span record and callbacks for implementations of -%% span storage to follow. +%% Span behaviour. %% @end %%%------------------------------------------------------------------------- -module(ot_span). --export([start_span/1, - start_span/2, - end_span/1]). - --include("opentelemetry.hrl"). - -type start_opts() :: #{parent => undefined | opentelemetry:span() | opentelemetry:span_ctx(), sampler => module(), links => opentelemetry:links(), @@ -33,7 +26,8 @@ -export_type([start_opts/0]). --callback start_span(opentelemetry:span_name(), start_opts()) -> opentelemetry:span(). +-callback start_span(opentelemetry:span_name(), start_opts()) -> opentelemetry:span_ctx(). +-callback finish_span(opentelemetry:span_ctx()) -> ok. -callback get_ctx(opentelemetry:span()) -> opentelemetry:span_ctx(). -callback is_recording_events(opentelemetry:span_ctx()) -> boolean(). -callback set_attributes(opentelemetry:span_ctx(), opentelemetry:attributes()) -> ok. @@ -41,82 +35,4 @@ -callback add_links(opentelemetry:span_ctx(), opentelemetry:links()) -> ok. -callback set_status(opentelemetry:span_ctx(), opentelemetry:status()) -> ok. -callback update_name(opentelemetry:span_ctx(), opentelemetry:span_name()) -> ok. --callback finish_span(opentelemetry:span_ctx()) -> ok. - -%% sampling bit is the first bit in 8-bit trace options --define(IS_ENABLED(X), (X band 1) =/= 0). - --spec start_span(opentelemetry:span_name()) -> {opentelemetry:span_ctx(), opentelemetry:span()}. -start_span(Name) -> - start_span(Name, #{}). - --spec start_span(opentelemetry:span_name(), start_opts()) - -> {opentelemetry:span_ctx(), opentelemetry:span() | undefined}. -start_span(Name, Opts) -> - Parent = maps:get(parent, Opts, undefined), - Attributes = maps:get(attributes, Opts, []), - Links = maps:get(links, Opts, []), - Kind = maps:get(kind, Opts, ?SPAN_KIND_UNSPECIFIED), - %% TODO: support overriding the sampler - _Sampler = maps:get(sampler, Opts, undefined), - - new_span_(Name, Parent, Kind, Attributes, Links). - -%% if parent is undefined, first run sampler -new_span_(Name, undefined, Kind, Attributes, Links) -> - TraceId = opentelemetry:generate_trace_id(), - Span = #span_ctx{trace_id=TraceId, - trace_options=0}, - TraceOptions = update_trace_options(should_sample, Span), - new_span_(Name, Span#span_ctx{trace_options=TraceOptions}, Kind, Attributes, Links); -%% if parent is remote, first run sampler -%% new_span_(Name, Span=#span_ctx{}, Kind, Attributes) %% when RemoteParent =:= true -%% -> -%% TraceOptions = update_trace_options(should_sample, Span), -%% new_span_(Name, Span#span_ctx{trace_options=TraceOptions}, Kind, Attributes); -new_span_(Name, Parent=#span_ctx{trace_id=TraceId, - trace_options=TraceOptions, - tracestate=Tracestate, - span_id=ParentSpanId}, Kind, Attributes, Links) - when ?IS_ENABLED(TraceOptions) -> - SpanId = opentelemetry:generate_span_id(), - Span = #span{trace_id=TraceId, - span_id=SpanId, - tracestate=Tracestate, - start_time=wts:timestamp(), - parent_span_id=ParentSpanId, - kind=Kind, - name=Name, - attributes=Attributes, - links=Links}, - {Parent#span_ctx{span_id=SpanId}, Span}; -new_span_(_Name, Parent, _Kind, _, _) -> - SpanId = opentelemetry:generate_span_id(), - %% since discarded by sampler, create no span - {Parent#span_ctx{span_id=SpanId}, undefined}. - -%% -update_trace_options(should_sample, #span_ctx{trace_id=_TraceId, - span_id=_ParentSpanId, - trace_options=_ParentTraceOptions}) -> - 1. - %% case oc_sampler:should_sample(TraceId, ParentSpanId, ?IS_ENABLED(ParentTraceOptions)) of - %% true -> - %% 1; - %% false -> - %% 0 - %% end. - -%%-------------------------------------------------------------------- -%% @doc -%% Set the end time for a span if it hasn't been set before. -%% @end -%%-------------------------------------------------------------------- --spec end_span(opentelemetry:span()) -> opentelemetry:span(). -end_span(Span=#span{end_time=undefined, - trace_options=TraceOptions}) when ?IS_ENABLED(TraceOptions) -> - EndTime = wts:timestamp(), - Span#span{end_time=EndTime}; -end_span(Span) -> - Span. diff --git a/src/ot_span_ets.erl b/src/ot_span_ets.erl index fb1ef8b4..2d17c5df 100644 --- a/src/ot_span_ets.erl +++ b/src/ot_span_ets.erl @@ -18,6 +18,7 @@ %%%------------------------------------------------------------------------- -module(ot_span_ets). +-behaviour(ot_span). -behaviour(gen_server). -export([start_link/1, @@ -25,9 +26,15 @@ handle_call/3, handle_cast/2]). --export([start_span/1, - start_span/2, - finish_span/1]). +-export([start_span/2, + finish_span/1, + get_ctx/1, + is_recording_events/1, + set_attributes/2, + add_events/2, + add_links/2, + set_status/2, + update_name/2]). -include("opentelemetry.hrl"). @@ -39,16 +46,10 @@ start_link(Opts) -> gen_server:start_link(?MODULE, Opts, []). -%% @equiv start_span(Name, #{}) --spec start_span(opentelemetry:span_name()) -> opentelemetry:span_ctx(). -start_span(Name) -> - start_span(Name, #{}). - - %% @doc Start a span and insert into the active span ets table. -spec start_span(opentelemetry:span_name(), ot_span:start_opts()) -> opentelemetry:span_ctx(). start_span(Name, Opts) -> - {SpanCtx, Span} = ot_span:start_span(Name, Opts), + {SpanCtx, Span} = ot_span_utils:start_span(Name, Opts), _ = storage_insert(Span), SpanCtx. @@ -59,7 +60,7 @@ finish_span(#span_ctx{span_id=SpanId, trace_options=TraceOptions}) when ?IS_SPAN_ENABLED(TraceOptions) -> case ets:take(?SPAN_TAB, SpanId) of [Span] -> - _Span1 = ot_span:end_span(Span#span{tracestate=Tracestate}), + _Span1 = ot_span_utils:end_span(Span#span{tracestate=Tracestate}), %% oc_reporter:store_span(Span1), ok; _ -> @@ -68,6 +69,34 @@ finish_span(#span_ctx{span_id=SpanId, finish_span(_) -> ok. +-spec get_ctx(opentelemetry:span()) -> opentelemetry:span_ctx(). +get_ctx(_Span) -> + #span_ctx{}. + +-spec is_recording_events(opentelemetry:span_ctx()) -> boolean(). +is_recording_events(_SpanCtx) -> + false. + +-spec set_attributes(opentelemetry:span_ctx(), opentelemetry:attributes()) -> ok. +set_attributes(_SpanCtx, _Attributes) -> + ok. + +-spec add_events(opentelemetry:span_ctx(), opentelemetry:time_events()) -> ok. +add_events(_SpanCtx, _TimeEvents) -> + ok. + +-spec add_links(opentelemetry:span_ctx(), opentelemetry:links()) -> ok. +add_links(_SpanCtx, _Links) -> + ok. + +-spec set_status(opentelemetry:span_ctx(), opentelemetry:status()) -> ok. +set_status(_SpanCtx, _Status) -> + ok. + +-spec update_name(opentelemetry:span_ctx(), opentelemetry:span_name()) -> ok. +update_name(_SpanCtx, _SpanName) -> + ok. + %% storage_insert(undefined) -> diff --git a/src/ot_span_sup.erl b/src/ot_span_sup.erl index f6cbe4e0..58222f6e 100644 --- a/src/ot_span_sup.erl +++ b/src/ot_span_sup.erl @@ -19,7 +19,8 @@ -behaviour(supervisor). --export([start_link/1]). +-export([start_link/1, + start_child/1]). -export([init/1]). @@ -28,12 +29,14 @@ start_link(Opts) -> supervisor:start_link({local, ?SERVER}, ?MODULE, [Opts]). -init([Opts]) -> +start_child(ChildSpec) -> + supervisor:start_child(?SERVER, ChildSpec). + +init([_Opts]) -> SupFlags = #{strategy => one_for_one, intensity => 0, period => 1}, - ChildSpecs = [#{id => ot_span_ets, - start => {ot_span_ets, start_link, [Opts]}}], + ChildSpecs = [], {ok, {SupFlags, ChildSpecs}}. %% internal functions diff --git a/src/ot_span_utils.erl b/src/ot_span_utils.erl new file mode 100644 index 00000000..faa18c84 --- /dev/null +++ b/src/ot_span_utils.erl @@ -0,0 +1,111 @@ +%%%------------------------------------------------------------------------ +%% Copyright 2019, OpenTelemetry Authors +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% @doc +%% Functional interface for span_ctx and span records. +%% @end +%%%------------------------------------------------------------------------- +-module(ot_span_utils). + +-export([start_span/1, + start_span/2, + end_span/1]). + +-include("opentelemetry.hrl"). + +-type start_opts() :: #{parent => undefined | opentelemetry:span() | opentelemetry:span_ctx(), + sampler => module(), + links => opentelemetry:links(), + is_recorded => boolean(), + kind => opentelemetry:span_kind()}. + +-export_type([start_opts/0]). + +%% sampling bit is the first bit in 8-bit trace options +-define(IS_ENABLED(X), (X band 1) =/= 0). + +-spec start_span(opentelemetry:span_name()) -> {opentelemetry:span_ctx(), opentelemetry:span()}. +start_span(Name) -> + start_span(Name, #{}). + +-spec start_span(opentelemetry:span_name(), start_opts()) + -> {opentelemetry:span_ctx(), opentelemetry:span() | undefined}. +start_span(Name, Opts) -> + Parent = maps:get(parent, Opts, undefined), + Attributes = maps:get(attributes, Opts, []), + Links = maps:get(links, Opts, []), + Kind = maps:get(kind, Opts, ?SPAN_KIND_UNSPECIFIED), + + %% TODO: support overriding the sampler + _Sampler = maps:get(sampler, Opts, undefined), + + new_span(Name, Parent, Kind, Attributes, Links). + +%% if parent is undefined, first run sampler +new_span(Name, undefined, Kind, Attributes, Links) -> + TraceId = opentelemetry:generate_trace_id(), + Span = #span_ctx{trace_id=TraceId, + trace_options=0}, + TraceOptions = update_trace_options(should_sample, Span), + new_span(Name, Span#span_ctx{trace_options=TraceOptions}, Kind, Attributes, Links); +%% if parent is remote, first run sampler +%% new_span_(Name, Span=#span_ctx{}, Kind, Attributes) %% when RemoteParent =:= true +%% -> +%% TraceOptions = update_trace_options(should_sample, Span), +%% new_span_(Name, Span#span_ctx{trace_options=TraceOptions}, Kind, Attributes); +new_span(Name, Parent=#span_ctx{trace_id=TraceId, + trace_options=TraceOptions, + tracestate=Tracestate, + span_id=ParentSpanId}, Kind, Attributes, Links) + when ?IS_ENABLED(TraceOptions) -> + SpanId = opentelemetry:generate_span_id(), + Span = #span{trace_id=TraceId, + span_id=SpanId, + tracestate=Tracestate, + start_time=wts:timestamp(), + parent_span_id=ParentSpanId, + kind=Kind, + name=Name, + attributes=Attributes, + links=Links}, + {Parent#span_ctx{span_id=SpanId}, Span}; +new_span(_Name, Parent, _Kind, _, _) -> + SpanId = opentelemetry:generate_span_id(), + %% since discarded by sampler, create no span + {Parent#span_ctx{span_id=SpanId}, undefined}. + +%% +update_trace_options(should_sample, #span_ctx{trace_id=_TraceId, + span_id=_ParentSpanId, + trace_options=_ParentTraceOptions}) -> + 1. + %% case oc_sampler:should_sample(TraceId, ParentSpanId, ?IS_ENABLED(ParentTraceOptions)) of + %% true -> + %% 1; + %% false -> + %% 0 + %% end. + +%%-------------------------------------------------------------------- +%% @doc +%% Set the end time for a span if it hasn't been set before. +%% @end +%%-------------------------------------------------------------------- +-spec end_span(opentelemetry:span()) -> opentelemetry:span(). +end_span(Span=#span{end_time=undefined, + trace_options=TraceOptions}) when ?IS_ENABLED(TraceOptions) -> + EndTime = wts:timestamp(), + Span#span{end_time=EndTime}; +end_span(Span) -> + Span. diff --git a/src/ot_tracer.erl b/src/ot_tracer.erl index adf27e14..4b6f9e14 100644 --- a/src/ot_tracer.erl +++ b/src/ot_tracer.erl @@ -17,7 +17,8 @@ %%%------------------------------------------------------------------------- -module(ot_tracer). --export([start_span/1, +-export([setup/2, + start_span/1, start_span/2, start_span/3, with_span/1, @@ -29,7 +30,7 @@ -include("opentelemetry.hrl"). --define(tracer, (persistent_term:get({opentelemetry, tracer}, ot_tracer_sdk))). +-define(tracer, (persistent_term:get({?MODULE, tracer}))). -define(CURRENT_TRACER, {?MODULE, current_tracer}). -callback start_span(opentelemetry:span_name(), ot_span:start_opts()) -> opentelemetry:span_ctx(). @@ -40,6 +41,11 @@ -callback get_binary_format() -> binary(). -callback get_http_text_format() -> opentelemetry:http_headers(). +-spec setup(module(), map()) -> [supervisor:child_spec()]. +setup(Tracer, TracerOpts) -> + persistent_term:put({?MODULE, tracer}, Tracer), + Tracer:setup(TracerOpts). + -spec start_span(opentelemetry:span_name()) -> opentelemetry:span_ctx(). start_span(Name) -> start_span(Name, #{}). diff --git a/src/ot_tracer_sdk.erl b/src/ot_tracer_default.erl similarity index 65% rename from src/ot_tracer_sdk.erl rename to src/ot_tracer_default.erl index 4d2546e8..358b4dc6 100644 --- a/src/ot_tracer_sdk.erl +++ b/src/ot_tracer_default.erl @@ -15,11 +15,12 @@ %% @doc %% @end %%%------------------------------------------------------------------------- --module(ot_tracer_sdk). +-module(ot_tracer_default). -behaviour(ot_tracer). --export([start_span/2, +-export([setup/1, + start_span/2, with_span/1, with_span/2, finish/0, @@ -27,36 +28,54 @@ get_binary_format/0, get_http_text_format/0]). --define(span, (persistent_term:get({opentelemetry, span}, ot_span_ets))). +-define(SPAN_CTX, {?MODULE, span_ctx}). +-define(CTX_IMPL_KEY, {?MODULE, ctx}). +-define(SPAN_IMPL_KEY, {?MODULE, span}). --define(SPAN_CTX, ot_tracer_dyn_span_ctx_key). +-define(ctx, (persistent_term:get(?CTX_IMPL_KEY))). +-define(span, (persistent_term:get(?SPAN_IMPL_KEY))). -type pdict_trace_ctx() :: {opentelemetry:span_ctx(), pdict_trace_ctx() | undefined}. +-spec setup(map()) -> [supervisor:child_spec()]. +setup(Opts) -> + lists:filtermap(fun({ConfigKey, PersistentKey}) -> + {Module, Args} = maps:get(ConfigKey, Opts), + persistent_term:put(PersistentKey, Module), + case erlang:function_exported(Module, start_link, 1) of + true -> + {true, #{id => Module, + start => {Module, start_link, [Args]}}}; + false -> + false + end + end, [{ctx, ?CTX_IMPL_KEY}, + {span, ?SPAN_IMPL_KEY}]). + -spec start_span(opentelemetry:span_name(), ot_span:start_opts()) -> opentelemetry:span_ctx(). start_span(Name, Opts) -> - case ot_ctx:get(?SPAN_CTX) of + case ?ctx:get(?SPAN_CTX) of {SpanCtx, _}=Ctx -> SpanCtx1 = ?span:start_span(Name, Opts#{parent => SpanCtx}), - ot_ctx:with_value(?SPAN_CTX, {SpanCtx1, Ctx}), + ?ctx:with_value(?SPAN_CTX, {SpanCtx1, Ctx}), SpanCtx1; _ -> SpanCtx = ?span:start_span(Name, Opts#{parent => undefined}), - ot_ctx:with_value(?SPAN_CTX, {SpanCtx, undefined}), + ?ctx:with_value(?SPAN_CTX, {SpanCtx, undefined}), SpanCtx end. -spec with_span(opentelemetry:span_ctx()) -> ok. with_span(SpanCtx) -> - ot_ctx:with_value(?SPAN_CTX, {SpanCtx, undefined}). + ?ctx:with_value(?SPAN_CTX, {SpanCtx, undefined}). -spec with_span(opentelemetry:span_ctx(), fun()) -> ok. with_span(SpanCtx, Fun) -> - ot_ctx:with_value(?SPAN_CTX, {SpanCtx, undefined}, Fun). + ?ctx:with_value(?SPAN_CTX, {SpanCtx, undefined}, Fun). -spec current_span_ctx() -> opentelemetry:span_ctx(). current_span_ctx() -> - case ot_ctx:get(?SPAN_CTX) of + case ?ctx:get(?SPAN_CTX) of {SpanCtx, _ParentPdictSpanCtx} -> SpanCtx; _ -> @@ -68,7 +87,7 @@ current_span_ctx() -> %% parent trace context, which contains its parent and so on. -spec current_ctx() -> pdict_trace_ctx(). current_ctx() -> - ot_ctx:get(?SPAN_CTX). + ?ctx:get(?SPAN_CTX). %%-------------------------------------------------------------------- %% @doc @@ -80,7 +99,7 @@ current_ctx() -> finish() -> {SpanCtx, ParentCtx} = current_ctx(), ?span:finish_span(SpanCtx), - ot_ctx:with_value(?SPAN_CTX, ParentCtx), + ?ctx:with_value(?SPAN_CTX, ParentCtx), ok. -spec get_binary_format() -> binary(). diff --git a/src/ot_tracer_noop.erl b/src/ot_tracer_noop.erl index 7a957d30..d2fa92b9 100644 --- a/src/ot_tracer_noop.erl +++ b/src/ot_tracer_noop.erl @@ -29,13 +29,15 @@ -include("opentelemetry.hrl"). +-define(NOOP_SPAN_CTX, #span_ctx{trace_id=0, + span_id=0, + trace_options=0, + tracestate=[], + is_valid=false}). + -spec start_span(opentelemetry:span_name(), ot_span:start_opts()) -> opentelemetry:span_ctx(). start_span(_Name, _) -> - #span_ctx{trace_id=0, - span_id=0, - trace_options=0, - tracestate=[], - is_valid=false}. + ?NOOP_SPAN_CTX. -spec with_span(opentelemetry:span_ctx()) -> ok. with_span(_SpanCtx) -> @@ -47,10 +49,7 @@ with_span(_SpanCtx, _) -> -spec current_span_ctx() -> opentelemetry:span_ctx(). current_span_ctx() -> - #span_ctx{trace_id=0, - span_id=0, - trace_options=0, - tracestate=[]}. + ?NOOP_SPAN_CTX. -spec finish() -> ok. finish() -> diff --git a/test/opentelemetry_SUITE.erl b/test/opentelemetry_SUITE.erl index d2b784de..87aaa76a 100644 --- a/test/opentelemetry_SUITE.erl +++ b/test/opentelemetry_SUITE.erl @@ -5,8 +5,10 @@ -include_lib("stdlib/include/assert.hrl"). -include_lib("common_test/include/ct.hrl"). +-include("opentelemetry.hrl"). + all() -> - [child_spans]. + [child_spans, non_default_tracer]. init_per_suite(Config) -> application:load(opentelemetry), @@ -51,3 +53,15 @@ child_spans(_Config) -> %% finish first and no span should be current ctx ot_tracer:finish(), ?assertMatch(undefined, ot_tracer:current_span_ctx()). + +non_default_tracer(_Config) -> + SpanCtx1 = ot_tracer:start_span(<<"span-1">>), + ?assertNotMatch(#span_ctx{trace_id=0, + span_id=0}, SpanCtx1), + ot_tracer:finish(), + + SpanCtx2 = ot_tracer:start_span(ot_tracer_noop, <<"span-2">>, #{}), + ?assertMatch(#span_ctx{trace_id=0, + span_id=0}, SpanCtx2), + ?assertMatch(SpanCtx2, ot_tracer:current_span_ctx()), + ot_tracer:finish().