Current section
Files
Jump to
Current section
Files
src/otter_lib_span.erl
%%%-------------------------------------------------------------------
%%% Licensed to the Apache Software Foundation (ASF) under one
%%% or more contributor license agreements. See the NOTICE file
%%% distributed with this work for additional information
%%% regarding copyright ownership. The ASF licenses this file
%%% to you 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 is the wrapper around the span data structure and
%%% exposes functions for span conversion to/from proplist e.g. for external
%%% applications that don't want to have the dependency of the record
%%% structure.
%%% @end
%%% -------------------------------------------------------------------
-module(otter_lib_span).
-export([
start/1, start/2, start/3,
start_with_tags/2, start_with_tags/3, start_with_tags/4,
finish/1,
log/2, log/3,
tag/3, tag/4,
get_id/1,
get_ids/1,
get_tags/1,
get_logs/1,
get_timestamp/1,
get_duration/1,
get_name/1,
get_trace_id/1,
get_parent_id/1,
%% These functions are for external APIs (e.g. filter callback)
%% if/when we decide to give up on backward compatibility in
%% favor of maps, that'll be included here too
to_proplist/1,
from_proplist/1
]).
-include("otter.hrl").
%%--------------------------------------------------------------------
%% @doc Starts a span with the specified name. Automatically generates
%% a trace id.
%% @end
%%--------------------------------------------------------------------
-spec start(Name :: info()) -> span().
start(Name) ->
start_with_tags(Name, []).
%%--------------------------------------------------------------------
%% @doc Starts a span with the specified name and trace id
%% @end
%% --------------------------------------------------------------------
-spec start(Name :: info(), TraceId :: trace_id()) -> span().
start(Name, TraceId) ->
start_with_tags(Name, [], TraceId).
%%--------------------------------------------------------------------
%% @doc Starts a child span with the specified name, trace id and
%% parent id
%% @end
%% --------------------------------------------------------------------
-spec start(Name :: info(), TraceId :: trace_id(), ParentId :: span_id()) -> span().
start(Name, TraceId, ParentId) ->
start_with_tags(Name, [], TraceId, ParentId).
%%--------------------------------------------------------------------
%% @doc Starts a span with the specified name and initial tags.
%% @end
%%--------------------------------------------------------------------
-spec start_with_tags(Name :: info(), InitialTags :: [tag()]) -> span().
start_with_tags(Name, InitialTags) ->
start_with_tags(Name, InitialTags, otter_lib:id()).
%%--------------------------------------------------------------------
%% @doc Starts a span with the specified name, initial tags and trace id
%% @end
%% --------------------------------------------------------------------
-spec start_with_tags(Name :: info(), InitialTags :: [tag()], TraceId :: trace_id()) -> span().
start_with_tags(Name, InitialTags, TraceId) ->
start_with_tags(Name, InitialTags, TraceId, undefined).
%%--------------------------------------------------------------------
%% @doc Starts a child span with the specified name, trace id and
%% parent id
%% @end
%% --------------------------------------------------------------------
-spec start_with_tags(Name :: info(), InitialTags :: [tag()], TraceId :: trace_id(), ParentId :: span_id() | undefined) -> span().
start_with_tags(Name, InitialTags, TraceId, ParentId) ->
#span{
timestamp = otter_lib:timestamp(),
trace_id = TraceId,
id = otter_lib:id(),
parent_id = ParentId,
name = Name,
tags = InitialTags
}.
%%--------------------------------------------------------------------
%% @doc Adds a tag to a span. If the tag already exists, its value
%% will be overwritten.
%% @end
%% --------------------------------------------------------------------
-spec tag(Span :: span(), Key :: info(), Value :: info()) -> span().
tag(Span, Key, Value) ->
Tags = Span#span.tags,
Span#span{
tags = lists:keystore(Key, 1, Tags, {Key, Value})
}.
%%--------------------------------------------------------------------
%% @doc Adds a tag to a span with a given service. If the tag already
%% exists, its value will be overwritten.
%% @end
%% --------------------------------------------------------------------
-spec tag(Span :: span(), Key :: info(), Value :: info(), Service :: service()) -> span().
tag(Span, Key, Value, Service) ->
Tags = Span#span.tags,
Span#span{
tags = lists:keystore(Key, 1, Tags, {Key, Value, Service})
}.
%%--------------------------------------------------------------------
%% @doc Adds a log message to a span.
%% @end
%% --------------------------------------------------------------------
-spec log(Span :: span(), Text :: info()) -> span().
log(Span, Text) ->
Logs = Span#span.logs,
Span#span{
logs = [{otter_lib:timestamp(), Text} | Logs]
}.
%%--------------------------------------------------------------------
%% @doc Adds a log message to a span with the specified service information.
%% @end
%% --------------------------------------------------------------------
-spec log(Span :: span(), Text :: info(), Service :: service()) -> span().
log(Span, Text, Service) ->
Logs = Span#span.logs,
Span#span{
logs = [{otter_lib:timestamp(), Text, Service} | Logs]
}.
%%--------------------------------------------------------------------
%% @doc Finish a span : add duration (current time - timestamp)
%% @end
%% --------------------------------------------------------------------
-spec finish(Span :: span()) -> span().
finish(#span{timestamp = Timestamp, logs = Logs} = Span) ->
Span#span{
duration = otter_lib:timestamp() - Timestamp,
logs = lists:reverse(Logs)
}.
%%--------------------------------------------------------------------
%% @doc Get ids (trace id and span id) of a span
%% @end
%% --------------------------------------------------------------------
-spec get_ids(Span :: span()) -> {trace_id(), span_id()}.
get_ids(#span{trace_id = TraceId, id = Id}) -> {TraceId, Id}.
%%--------------------------------------------------------------------
%% @doc Get the id of a span
%% @end
%% --------------------------------------------------------------------
-spec get_id(Span :: span()) -> span_id().
get_id(#span{id = Id}) -> Id.
%%--------------------------------------------------------------------
%% @doc Get the trace id of a span
%% @end
%% --------------------------------------------------------------------
-spec get_trace_id(Span :: span()) -> span_id().
get_trace_id(#span{trace_id = TraceId}) -> TraceId.
%%--------------------------------------------------------------------
%% @doc Get the parent id of a span
%% @end
%% --------------------------------------------------------------------
-spec get_parent_id(Span :: span()) -> span_id()|undefined.
get_parent_id(#span{parent_id = ParentId}) -> ParentId.
%%--------------------------------------------------------------------
%% @doc Get the list of tags (key-value pairs) of a span
%% @end
%% --------------------------------------------------------------------
-spec get_tags(Span :: span()) -> [tag()].
get_tags(#span{tags = Tags}) -> Tags.
%%--------------------------------------------------------------------
%% @doc Get the list of logs (timestamp-text pairs) of a span
%% @end
%% --------------------------------------------------------------------
-spec get_logs(Span :: span()) -> [log()].
get_logs(#span{logs = Logs}) -> Logs.
%%--------------------------------------------------------------------
%% @doc Get the start timestamp of a span
%% @end
%% --------------------------------------------------------------------
-spec get_timestamp(Span :: span()) -> time_us().
get_timestamp(#span{timestamp = Timestamp}) -> Timestamp.
%%--------------------------------------------------------------------
%% @doc Get the duration of a span. Returns integer ter the span is finished
%% otherwise undefined
%% @end
%% --------------------------------------------------------------------
-spec get_duration(Span :: span()) -> time_us()|undefined.
get_duration(#span{duration = Duration}) -> Duration.
%%--------------------------------------------------------------------
%% @doc Get the name of a span
%% @end
%% --------------------------------------------------------------------
-spec get_name(Span :: span()) -> info().
get_name(#span{name = Name}) -> Name.
%%--------------------------------------------------------------------
%% @doc Convert span record to a proplist
%% @end
%% --------------------------------------------------------------------
-spec to_proplist(Span :: span()) -> list().
to_proplist(
#span{
timestamp = Timestamp,
duration = Duration,
trace_id = TraceId,
id = Id,
parent_id = ParentId,
name = Name,
tags = Tags,
logs = Logs
}) ->
[
{timestamp, Timestamp},
{duration, Duration},
{id, Id},
{trace_id, TraceId},
{parent_id, ParentId},
{name, Name},
{tags, Tags},
{logs, Logs}
].
%%--------------------------------------------------------------------
%% @doc Convert a proplist to span record. The proplist is expected to
%% have keys : timestamp, duration, id, trace_id, parent_id, name, tags,
%% logs with the correct values as described in the span record in
%% otter.hrl
%% @end
%% --------------------------------------------------------------------
-spec from_proplist(Proplist :: list()) -> span().
from_proplist(Proplist) ->
from_proplist(Proplist, #span{}).
from_proplist([{timestamp, Timestamp} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{timestamp = Timestamp});
from_proplist([{duration, Duration} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{duration = Duration});
from_proplist([{id, Id} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{id = Id});
from_proplist([{trace_id, TraceId} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{trace_id = TraceId});
from_proplist([{parent_id, ParentId} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{parent_id = ParentId});
from_proplist([{name, Name} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{name = Name});
from_proplist([{tags, Tags} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{tags = Tags});
from_proplist([{logs, Logs} | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc#span{logs = Logs});
from_proplist([_ | Rest], SpanAcc) ->
from_proplist(Rest, SpanAcc);
from_proplist([], SpanAcc) ->
SpanAcc.