blob: f7b1eb2d2608da38772dc2a206989d393574a809 [file] [log] [blame]
%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
%%
%% @doc TODO
%%
-module(passage_span).
-include("opentracing.hrl").
%%------------------------------------------------------------------------------
%% Exported API
%%------------------------------------------------------------------------------
-export([get_operation_name/1]).
-export([get_tags/1]).
-export([get_refs/1]).
-export([get_logs/1]).
-export([get_context/1]).
-export_type([span/0]).
-export_type([normalized_refs/0, normalized_ref/0]).
-export_type([log/0]).
%%------------------------------------------------------------------------------
%% Application Internal API
%%------------------------------------------------------------------------------
-export([make_extracted_span/2]).
-export([start/2, start_root/3]).
-export([finish/2]).
-export([set_operation_name/2]).
-export([set_tags/2]).
-export([log/3]).
-export([set_baggage_items/2]).
-export([get_baggage_items/1]).
-export([get_tracer/1]).
%%------------------------------------------------------------------------------
%% Macros & Records
%%------------------------------------------------------------------------------
-define(SPAN, ?MODULE).
-record(?SPAN,
{
tracer :: passage:tracer_id(),
operation_name :: passage:operation_name(),
start_time :: erlang:timestamp(),
finish_time = undefined :: erlang:timestamp() | undefined,
refs = [] :: normalized_refs(),
tags = #{} :: passage:tags(),
logs = [] :: [log()],
context :: passage_span_context:maybe_context()
}).
%%------------------------------------------------------------------------------
%% Exported Types
%%------------------------------------------------------------------------------
-opaque span() :: #?SPAN{}.
-type normalized_refs() :: [normalized_ref()].
-type normalized_ref() :: {passage:ref_type(), span()}.
-type log() :: {passage:log_fields(), erlang:timestamp()}.
%%------------------------------------------------------------------------------
%% Exported Functions
%%------------------------------------------------------------------------------
-spec get_operation_name(span()) -> passage:operation_name().
get_operation_name(Span) ->
Span#?SPAN.operation_name.
-spec get_tags(span()) -> passage:tags().
get_tags(Span) ->
Span#?SPAN.tags.
-spec get_refs(span()) -> normalized_refs().
get_refs(Span) ->
Span#?SPAN.refs.
-spec get_logs(span()) -> [log()].
get_logs(Span) ->
Span#?SPAN.logs.
-spec get_context(span()) -> passage_span_context:context().
get_context(Span) ->
Span#?SPAN.context.
%%------------------------------------------------------------------------------
%% Application Internal Functions
%%------------------------------------------------------------------------------
%% @private
-spec make_extracted_span(passage:tracer_id(), passage_span_context:context()) -> span().
make_extracted_span(Tracer, Context) ->
#?SPAN{
tracer = Tracer,
operation_name = undefined,
start_time = {0, 0, 0},
context = Context
}.
%% @private
-spec start_root(passage:tracer_id(), passage:operation_name(), Options) ->
passage:maybe_span() when
Options :: passage:start_root_span_options().
start_root(Tracer, OperationName, Options) ->
Tags = proplists:get_value(tags, Options, #{}),
case is_sampled(Tracer, OperationName, Tags) of
false -> undefined;
true ->
case make_span_context(Tracer, []) of
error -> undefined;
{ok, Context} ->
StartTime = get_time(Options),
#?SPAN{
tracer = Tracer,
operation_name = OperationName,
start_time = StartTime,
tags = Tags,
context = Context
}
end
end.
%% @private
-spec start(passage:operation_name(), passage:start_span_options()) -> passage:maybe_span().
start(OperationName, Options) ->
Refs = lists:keydelete(undefined, 2, proplists:get_value(refs, Options, [])),
case Refs of
[] -> undefined;
[{_, Primary} | _] ->
Tracer = get_tracer(Primary),
case make_span_context(Tracer, Refs) of
error -> undefined;
{ok, Context} ->
Tags = proplists:get_value(tags, Options, #{}),
StartTime = get_time(Options),
#?SPAN{
tracer = Tracer,
operation_name = OperationName,
start_time = StartTime,
refs = Refs,
tags = Tags,
context = Context
}
end
end.
%% @private
-spec finish(span(), passage:finish_span_options()) -> ok.
finish(#?SPAN{operation_name = undefined, start_time = {0, 0, 0}}, _) ->
%% This is an extracted span (ignored).
ok;
finish(Span0, Options) ->
FinishTime = get_time(Options),
Span1 = Span0#?SPAN{finish_time = FinishTime},
case passage_tracer_registry:get_reporter(Span1#?SPAN.tracer) of
error -> ok;
{ok, Reporter} -> passage_reporter:report(Reporter, Span1)
end.
%% @private
-spec set_operation_name(span(), passage:operation_name()) -> span().
set_operation_name(Span, OperationName) ->
Span#?SPAN{operation_name = OperationName}.
%% @private
-spec set_tags(span(), passage:tags()) -> span().
set_tags(Span, Tags) ->
Span#?SPAN{tags = maps:merge(Span#?SPAN.tags, Tags)}.
%% @private
-spec log(span(), passage:log_fields(), passage:log_options()) -> span().
log(Span, Fields, Options) ->
Time = get_time(Options),
Span#?SPAN{logs = [{Fields, Time} | Span#?SPAN.logs]}.
%% @private
-spec set_baggage_items(span(), passage:baggage_items()) -> span().
set_baggage_items(Span, Items) ->
Context = Span#?SPAN.context,
Span#?SPAN{context = passage_span_context:set_baggage_items(Context, Items)}.
%% @private
-spec get_baggage_items(span()) -> passage:baggage_items().
get_baggage_items(Span) ->
passage_span_context:get_baggage_items(Span#?SPAN.context).
%% @private
-spec get_tracer(span()) -> passage:tracer_id().
get_tracer(Span) ->
Span#?SPAN.tracer.
%%------------------------------------------------------------------------------
%% Internal Functions
%%------------------------------------------------------------------------------
-spec is_sampled(passage:tracer_id(), passage:operation_name(), passage:tags()) -> boolean().
is_sampled(Tracer, OperationName, Tags) ->
case maps:find(?TAG_SAMPLING_PRIORITY, Tags) of
{ok, V} -> 0 < V;
_ ->
case passage_tracer_registry:get_sampler(Tracer) of
error -> false;
{ok, Sampler} -> passage_sampler:is_sampled(Sampler, OperationName, Tags)
end
end.
-spec get_time([{time, erlang:timestamp()}]) -> erlang:timestamp().
get_time(Options) ->
case lists:keyfind(time, 1, Options) of
false -> os:timestamp();
{_, Time} -> Time
end.
-spec make_span_context(passage:tracer_id(), passage_span:normalized_refs()) ->
{ok, passage_span_context:context()} | error.
make_span_context(Tracer, Refs) ->
case passage_tracer_registry:get_span_context_module(Tracer) of
error -> error;
{ok, Module} -> {ok, passage_span_context:make(Module, Refs)}
end.