Skip to content

Commit

Permalink
make trace/span id generation customizable
Browse files Browse the repository at this point in the history
A behaviour, otel_id_generator, is added which has callbacks for
generating trace and span ids, along with the default versions
of those functions.

The implementation to use is kept in the tracer record.
  • Loading branch information
Tristan Sloughter committed Nov 12, 2021
1 parent 590d87f commit dfb2c82
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 58 deletions.
54 changes: 54 additions & 0 deletions apps/opentelemetry/src/otel_id_generator.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
%%%------------------------------------------------------------------------
%% Copyright 2021, 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 This module provides the behaviour to implement for custom trace
%% and span id generation and the default implementation of the
%% generators which produces random 128 bit and 64 bit integers for the
%% trace id and span id.
%% @end
%%%-----------------------------------------------------------------------
-module(otel_id_generator).

-export([generate_trace_id/0,
generate_trace_id/1,
generate_span_id/0,
generate_span_id/1]).

-callback generate_trace_id() -> opentelemetry:trace_id().

-callback generate_span_id() -> opentelemetry:span_id().

-type t() :: module().

-export_type([t/0]).

%% @doc Calls a module implementing the `otel_id_generator' behaviour to generate a trace id
-spec generate_trace_id(t()) -> opentelemetry:trace_id().
generate_trace_id(Module) ->
Module:generate_trace_id().

%% @doc Calls a module implementing the `otel_id_generator' behaviour to generate a span id
-spec generate_span_id(t()) -> opentelemetry:span_id().
generate_span_id(Module) ->
Module:generate_span_id().

%% @doc Generates a 128 bit random integer to use as a trace id.
-spec generate_trace_id() -> opentelemetry:trace_id().
generate_trace_id() ->
rand:uniform(2 bsl 127 - 1). %% 2 shifted left by 127 == 2 ^ 128

%% @doc Generates a 64 bit random integer to use as a span id.
-spec generate_span_id() -> opentelemetry:span_id().
generate_span_id() ->
rand:uniform(2 bsl 63 - 1). %% 2 shifted left by 63 == 2 ^ 64
11 changes: 6 additions & 5 deletions apps/opentelemetry/src/otel_span_ets.erl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
handle_call/3,
handle_cast/2]).

-export([start_span/6,
-export([start_span/7,
end_span/1,
end_span/2,
get_ctx/1,
Expand All @@ -46,10 +46,11 @@ start_link(Opts) ->
gen_server:start_link(?MODULE, Opts, []).

%% @doc Start a span and insert into the active span ets table.
-spec start_span(otel_ctx:t(), opentelemetry:span_name(), otel_sampler:t(), otel_span:start_opts(),
fun(), otel_tracer_server:instrumentation_library()) -> opentelemetry:span_ctx().
start_span(Ctx, Name, Sampler, Opts, Processors, InstrumentationLibrary) ->
case otel_span_utils:start_span(Ctx, Name, Sampler, Opts) of
-spec start_span(otel_ctx:t(), opentelemetry:span_name(), otel_sampler:t(), otel_id_generator:t(),
otel_span:start_opts(), fun(), otel_tracer_server:instrumentation_library())
-> opentelemetry:span_ctx().
start_span(Ctx, Name, Sampler, IdGeneratorModule, Opts, Processors, InstrumentationLibrary) ->
case otel_span_utils:start_span(Ctx, Name, Sampler, IdGeneratorModule, Opts) of
{SpanCtx=#span_ctx{is_recording=true}, Span=#span{}} ->
Span1 = Span#span{instrumentation_library=InstrumentationLibrary},
Span2 = Processors(Ctx, Span1),
Expand Down
30 changes: 15 additions & 15 deletions apps/opentelemetry/src/otel_span_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@
%%%-------------------------------------------------------------------------
-module(otel_span_utils).

-export([start_span/4,
-export([start_span/5,
end_span/1,
end_span/2]).

-include_lib("opentelemetry_api/include/opentelemetry.hrl").
-include("otel_sampler.hrl").
-include("otel_span.hrl").

-spec start_span(otel_ctx:t(), opentelemetry:span_name(), otel_sampler:t(), otel_span:start_opts())
-> {opentelemetry:span_ctx(), opentelemetry:span() | undefined}.
start_span(Ctx, Name, Sampler, Opts) ->
-spec start_span(otel_ctx:t(), opentelemetry:span_name(), otel_sampler:t(), otel_id_generator:t(),
otel_span:start_opts()) -> {opentelemetry:span_ctx(), opentelemetry:span() | undefined}.
start_span(Ctx, Name, Sampler, IdGenerator, Opts) ->
Attributes = maps:get(attributes, Opts, []),
Links = maps:get(links, Opts, []),
Kind = maps:get(kind, Opts, ?SPAN_KIND_INTERNAL),
StartTime = maps:get(start_time, Opts, opentelemetry:timestamp()),
new_span(Ctx, Name, Sampler, StartTime, Kind, Attributes, Links).
new_span(Ctx, Name, Sampler, IdGenerator, StartTime, Kind, Attributes, Links).

new_span(Ctx, Name, Sampler, StartTime, Kind, Attributes, Links) ->
{NewSpanCtx, ParentSpanId} = new_span_ctx(Ctx),
new_span(Ctx, Name, Sampler, IdGeneratorModule, StartTime, Kind, Attributes, Links) ->
{NewSpanCtx, ParentSpanId} = new_span_ctx(Ctx, IdGeneratorModule),

TraceId = NewSpanCtx#span_ctx.trace_id,
SpanId = NewSpanCtx#span_ctx.span_id,
Expand All @@ -60,21 +60,21 @@ new_span(Ctx, Name, Sampler, StartTime, Kind, Attributes, Links) ->
is_valid=true,
is_recording=IsRecording}, Span}.

-spec new_span_ctx(otel_ctx:t()) -> {opentelemetry:span_ctx(), opentelemetry:span_id()}.
new_span_ctx(Ctx) ->
-spec new_span_ctx(otel_ctx:t(), otel_id_generator:t()) -> {opentelemetry:span_ctx(), opentelemetry:span_id()}.
new_span_ctx(Ctx, IdGeneratorModule) ->
case otel_tracer:current_span_ctx(Ctx) of
undefined ->
{root_span_ctx(), undefined};
{root_span_ctx(IdGeneratorModule), undefined};
#span_ctx{is_valid=false} ->
{root_span_ctx(), undefined};
{root_span_ctx(IdGeneratorModule), undefined};
ParentSpanCtx=#span_ctx{span_id=ParentSpanId} ->
%% keep the rest of the parent span ctx, simply need to update the span_id
{ParentSpanCtx#span_ctx{span_id=opentelemetry:generate_span_id()}, ParentSpanId}
{ParentSpanCtx#span_ctx{span_id=IdGeneratorModule:generate_span_id()}, ParentSpanId}
end.

root_span_ctx() ->
#span_ctx{trace_id=opentelemetry:generate_trace_id(),
span_id=opentelemetry:generate_span_id(),
root_span_ctx(IdGeneratorModule) ->
#span_ctx{trace_id=IdGeneratorModule:generate_trace_id(),
span_id=IdGeneratorModule:generate_span_id(),
is_valid=true,
trace_flags=0}.

Expand Down
1 change: 1 addition & 0 deletions apps/opentelemetry/src/otel_tracer.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
on_start_processors :: fun((otel_ctx:t(), opentelemetry:span()) -> opentelemetry:span()),
on_end_processors :: fun((opentelemetry:span()) -> boolean() | {error, term()}),
sampler :: otel_sampler:t(),
id_generator :: otel_id_generator:t(),
instrumentation_library :: otel_tracer_server:instrumentation_library() | undefined,
telemetry_library :: otel_tracer_server:telemetry_library() | undefined,
resource :: otel_resource:t() | undefined
Expand Down
3 changes: 2 additions & 1 deletion apps/opentelemetry/src/otel_tracer_default.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@
start_span(Ctx, {_, #tracer{on_start_processors=Processors,
on_end_processors=OnEndProcessors,
sampler=Sampler,
id_generator=IdGeneratorModule,
instrumentation_library=InstrumentationLibrary}}, Name, Opts) ->
SpanCtx = otel_span_ets:start_span(Ctx, Name, Sampler, Opts, Processors, InstrumentationLibrary),
SpanCtx = otel_span_ets:start_span(Ctx, Name, Sampler, IdGeneratorModule, Opts, Processors, InstrumentationLibrary),
SpanCtx#span_ctx{span_sdk={otel_span_ets, OnEndProcessors}}.

-spec with_span(otel_ctx:t(), opentelemetry:tracer(), opentelemetry:span_name(),
Expand Down
3 changes: 3 additions & 0 deletions apps/opentelemetry/src/otel_tracer_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ start_link(Opts) ->
init(Opts) ->
Resource = otel_resource_detector:get_resource(),

IdGeneratorModule = proplists:get_value(id_generator, Opts, otel_id_generator),

SamplerSpec = proplists:get_value(
sampler, Opts, {parent_based, #{root => always_on}}
),
Expand All @@ -80,6 +82,7 @@ init(Opts) ->
sampler=Sampler,
on_start_processors=on_start(Processors),
on_end_processors=on_end(Processors),
id_generator=IdGeneratorModule,
resource=Resource,
telemetry_library=TelemetryLibrary},
opentelemetry:set_default_tracer({otel_tracer_default, Tracer}),
Expand Down
4 changes: 2 additions & 2 deletions apps/opentelemetry/test/otel_samplers_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ custom_sampler_module(_Config) ->
{?DROP, [], []},
Sampler:should_sample(
otel_ctx:new(),
opentelemetry:generate_trace_id(),
otel_id_generator:generate_trace_id(),
[],
SpanName,
undefined,
Expand All @@ -288,7 +288,7 @@ should_sample(_Config) ->
otel_samplers:should_sample(
Sampler,
otel_ctx:new(),
opentelemetry:generate_trace_id(),
otel_id_generator:generate_trace_id(),
[],
<<"span-name">>,
undefined,
Expand Down
25 changes: 1 addition & 24 deletions apps/opentelemetry_api/src/opentelemetry.erl
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@
event/3,
events/1,
status/2,
verify_and_set_term/3,
generate_trace_id/0,
generate_span_id/0]).
verify_and_set_term/3]).

-include("opentelemetry.hrl").
-include_lib("kernel/include/logger.hrl").
Expand Down Expand Up @@ -349,27 +347,6 @@ status(?OTEL_STATUS_UNSET, _Message) ->
status(_, _) ->
undefined.

%%--------------------------------------------------------------------
%% @doc
%% Generates a 128 bit random integer to use as a trace id.
%% @end
%%--------------------------------------------------------------------
-spec generate_trace_id() -> trace_id().
generate_trace_id() ->
uniform(2 bsl 127 - 1). %% 2 shifted left by 127 == 2 ^ 128

%%--------------------------------------------------------------------
%% @doc
%% Generates a 64 bit random integer to use as a span id.
%% @end
%%--------------------------------------------------------------------
-spec generate_span_id() -> span_id().
generate_span_id() ->
uniform(2 bsl 63 - 1). %% 2 shifted left by 63 == 2 ^ 64

uniform(X) ->
rand:uniform(X).

%% internal functions

-spec verify_and_set_term(module() | {module(), term()}, term(), atom()) -> boolean().
Expand Down
7 changes: 4 additions & 3 deletions apps/opentelemetry_api/src/otel_tracer_noop.erl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
update_name/3]).

-include("opentelemetry.hrl").
-include("otel_tracer.hrl").

-define(NOOP_SPAN_CTX, #span_ctx{trace_id=0,
span_id=0,
Expand All @@ -40,8 +41,8 @@
span_sdk=undefined}).
-define(NOOP_TRACER_CTX, []).

-spec start_span(otel_ctx:t(), opentelemetry:tracer(), opentelemetry:span_name(), otel_span:start_opts())
-> opentelemetry:span_ctx().
-spec start_span(otel_ctx:t(), opentelemetry:tracer(), opentelemetry:span_name(),
otel_span:start_opts()) -> opentelemetry:span_ctx().
start_span(Ctx, _, _SpanName, _) ->
%% Spec: Behavior of the API in the absence of an installed SDK
case otel_tracer:current_span_ctx(Ctx) of
Expand All @@ -51,7 +52,7 @@ start_span(Ctx, _, _SpanName, _) ->
Parent;
Parent=#span_ctx{trace_id=TraceId} when TraceId =/= 0 ->
%% API MUST create a non-recording Span with the SpanContext in the parent Context
SpanCtx = Parent#span_ctx{span_id=opentelemetry:generate_span_id(),
SpanCtx = Parent#span_ctx{span_id=otel_id_generator:generate_span_id(),
is_recording=false},
SpanCtx;
_ ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ configuration(_Config) ->
ets_instrumentation_info(_Config) ->
Tid = ets:new(span_tab, [duplicate_bag, {keypos, #span.instrumentation_library}]),

TraceId = opentelemetry:generate_trace_id(),
SpanId = opentelemetry:generate_span_id(),
TraceId = otel_id_generator:generate_trace_id(),
SpanId = otel_id_generator:generate_span_id(),

ParentSpan =
#span{name = <<"span-1">>,
Expand All @@ -137,7 +137,7 @@ ets_instrumentation_info(_Config) ->

ChildSpan = #span{name = <<"span-2">>,
trace_id = TraceId,
span_id = opentelemetry:generate_span_id(),
span_id = otel_id_generator:generate_span_id(),
parent_span_id = SpanId,
kind = ?SPAN_KIND_SERVER,
start_time = opentelemetry:timestamp(),
Expand Down Expand Up @@ -165,8 +165,8 @@ ets_instrumentation_info(_Config) ->
ok.

span_round_trip(_Config) ->
TraceId = opentelemetry:generate_trace_id(),
SpanId = opentelemetry:generate_span_id(),
TraceId = otel_id_generator:generate_trace_id(),
SpanId = otel_id_generator:generate_span_id(),

Span =
#span{name = <<"span-1">>,
Expand Down Expand Up @@ -219,8 +219,8 @@ verify_export(Config) ->

?assertMatch(ok, opentelemetry_exporter:export(Tid, otel_resource:create([]), State)),

TraceId = opentelemetry:generate_trace_id(),
SpanId = opentelemetry:generate_span_id(),
TraceId = otel_id_generator:generate_trace_id(),
SpanId = otel_id_generator:generate_span_id(),

ParentSpan =
#span{name = <<"span-1">>,
Expand All @@ -242,7 +242,7 @@ verify_export(Config) ->

ChildSpan = #span{name = <<"span-2">>,
trace_id = TraceId,
span_id = opentelemetry:generate_span_id(),
span_id = otel_id_generator:generate_span_id(),
parent_span_id = SpanId,
kind = ?SPAN_KIND_SERVER,
start_time = opentelemetry:timestamp(),
Expand Down

0 comments on commit dfb2c82

Please sign in to comment.