Current section
Files
Jump to
Current section
Files
src/otters.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
%%% otters API module. Functions have no effect when
%%% <em>undefined</em> is passed as a spawn.
%%%
%%% This API functions with passing around the Span in the function calls
%%% All of them return a Span structure.
%%% @end
%%%-------------------------------------------------------------------
-module(otters).
-include("otters.hrl").
-export([start/1, start/2, start/3,
start_child/2,
tag/3, tag/4,
log/2, log/3,
finish/1,
ids/1
]).
-export_type([info/0, service/0, trace_id/0, span_id/0,
span/0, maybe_span/0, tags/0]).
-type info() :: binary() | iolist() | atom() | integer().
-type ip4() :: {0..255, 0..255, 0..255, 0..255}.
-type service() :: binary() | list() | default | undefined |
{binary() | list(), ip4(), integer()}.
-type trace_id() :: non_neg_integer().
-type span_id() :: non_neg_integer().
-type tag() :: {info(), service()}
| binary() | string() | atom().
-type tags() :: #{binary() => tag()}.
-type span() :: #span{}.
-type maybe_span() :: span() | undefined.
%% timestamp in microseconds
-type time_us() :: non_neg_integer().
%% ==================== SPAN function API ======================
%%--------------------------------------------------------------------
%% @doc
%% Starts a new span with a given name and a generated trace id.
%% @end
%%--------------------------------------------------------------------
-spec start(info()) -> span().
start(Name) ->
start(Name, otters_lib:id()).
%%--------------------------------------------------------------------
%% @doc
%% Starts a new span with a given Trace ID.
%% @end
%%--------------------------------------------------------------------
-spec start(info(), integer() | undefined) ->
maybe_span().
start(_Name, undefined) ->
undefined;
start(Name, TraceId)
when is_integer(TraceId) ->
start(Name, TraceId, undefined).
%%--------------------------------------------------------------------
%% @doc
%% Starts a new span with a given Trace ID and Parent ID.
%% @end
%%--------------------------------------------------------------------
-spec start(info(), integer() | undefined, integer() | undefined) ->
maybe_span().
start(_Name, undefined, undefined) ->
undefined;
start(Name, TraceId, ParentId)
when is_integer(TraceId), (is_integer(ParentId) orelse
ParentId =:= undefined) ->
#span{
timestamp = otters_lib:timestamp(),
trace_id = TraceId,
id = otters_lib:id(),
parent_id = ParentId,
name = Name
}.
%%--------------------------------------------------------------------
%% @doc
%% Starts a new span as a child of a existing span, using the parents
%% Trace ID or a `{trace_id, parent_id}` tuple and setting the childs
%% parent to the parents Span ID
%% @end
%%--------------------------------------------------------------------
-spec start_child(info(), maybe_span() |
{TraceID::trace_id(), SpanID::span_id()}) -> maybe_span().
start_child(_Name, undefined) ->
undefined;
start_child(Name, #span{trace_id = TraceId, parent_id = ParentId}) ->
start(Name, TraceId, ParentId);
start_child(Name, {TraceId, ParentId}) ->
start(Name, TraceId, ParentId).
%%--------------------------------------------------------------------
%% @doc
%% Adds a tag to a span, possibly overwriting the existing value.
%% @end
%%--------------------------------------------------------------------
-spec tag(maybe_span(), info(), info()) -> maybe_span().
tag(undefined, _Key, _Value) ->
undefined;
tag(Span = #span{}, Key, Value) ->
tag(Span, Key, Value, undefined).
%%--------------------------------------------------------------------
%% @doc
%% Adds a tag to a span with a given service, possibly overwriting
%% the existing value.
%% @end
%%--------------------------------------------------------------------
-spec tag(maybe_span(), info(), info(), service() | undefined) -> maybe_span().
tag(undefined, _Key, _Value, _Service) ->
undefined;
tag(Span= #span{}, Key, Value, Service) ->
KeyBin = otters_lib:to_bin(Key),
Span#span{
tags = maps:put(KeyBin, {v(Value), Service}, Span#span.tags)
}.
%%--------------------------------------------------------------------
%% @doc
%% Adds a log to a span.
%% @end
%%--------------------------------------------------------------------
-spec log(maybe_span(), info()) -> maybe_span().
log(undefined, _Text) ->
undefined;
log(Span = #span{logs = Logs}, Text) ->
Span#span{
logs = [{otters_lib:timestamp(), otters_lib:to_bin(Text), undefined} | Logs]
}.
%%--------------------------------------------------------------------
%% @doc
%% Adds a log to a span with a given service.
%% @end
%%--------------------------------------------------------------------
-spec log(maybe_span(), info(), service()) -> maybe_span().
log(undefined, _Text, _Service) ->
undefined;
log(Span = #span{logs = Logs}, Text, Service) ->
Span#span{
logs = [{otters_lib:timestamp(), otters_lib:to_bin(Text), Service} | Logs]
}.
%%--------------------------------------------------------------------
%% @doc
%% Ends a span and prepares queues it to be dispatched to the trace
%% server. This is also where filtering happens, it's the most
%% expensive part of tracing.
%% @end
%%--------------------------------------------------------------------
-spec finish(maybe_span()) -> ok.
finish(undefined) ->
ok;
finish(Span = #span{logs = Logs, timestamp = Start}) ->
ol:span(
Span#span{
duration = otters_lib:timestamp() - Start,
logs = lists:reverse(Logs)
}),
ok.
%%--------------------------------------------------------------------
%% @doc
%% Retrives the Trace ID and the Span ID from a span. This can
%% be used for start_child/2
%% @end
%%--------------------------------------------------------------------
-spec ids(maybe_span()) -> {TraceID::trace_id(), SpanID::span_id()} | undefined.
ids(undefined) ->
undefined;
ids(#span{trace_id = TraceId, id = Id}) ->
{TraceId, Id}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
v(I) when is_integer(I) ->
I;
v(B) when is_binary(B) ->
B;
v(O) ->
otters_lib:to_bin(O).