Add documentation
diff --git a/doc/overview.edoc b/doc/overview.edoc
new file mode 100644
index 0000000..e486117
--- /dev/null
+++ b/doc/overview.edoc
@@ -0,0 +1,42 @@
+@copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+
+@doc Jaeger client library for Erlang
+
+This is an extention of <a href="https://github.com/sile/passage">passage</a>(An OpenTracing library).
+
+=== Examples ===
+
+```
+// Starts Jaeger in the background
+$ docker run -d -p6831:6831/udp -p6832:6832/udp -p16686:16686 jaegertracing/all-in-one:latest
+
+// Starts Erlang Shell
+$ rebar3 shell
+> Context = jaeger_passage_span_context.
+> Sampler = passage_sampler_all:new().
+> {ok, Reporter} = jaeger_passage_reporter:start(example_repoter).
+> ok = passage_tracer_registry:register(example_tracer, Context, Sampler, Reporter).
+
+%% Starts a root span.
+> RootSpan = passage:start_root_span(example_root, example_tracer).
+
+%% Starts a child span.
+> ChildSpan = passage:start_span(example_child, {child_of, RootSpan}).
+
+%% Finishes the spans
+> passage:finish_span(ChildSpan).
+> passage:finish_span(RootSpan).
+
+> q().
+
+// Browses the tracing result
+$ firefox localhost:16686
+'''
+
+=== References ===
+
+<ul>
+ <li><a href="http://opentracing.io/">OpenTracing</a></li>
+ <li><a href="https://uber.github.io/jaeger/">Jaeger</a></li>
+ <li><a href="https://github.com/jaegertracing/jaeger-client-go/blob/v2.9.0/README.md">jaeger-client-go/README.md</a></li>
+</ul>
diff --git a/src/jaeger_passage_app.erl b/src/jaeger_passage_app.erl
index 0e9b763..a07ce4a 100644
--- a/src/jaeger_passage_app.erl
+++ b/src/jaeger_passage_app.erl
@@ -1,9 +1,18 @@
+%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+%%
+%% @private
-module(jaeger_passage_app).
-behaviour(application).
+%%------------------------------------------------------------------------------
+%% 'application' Callback API
+%%------------------------------------------------------------------------------
-export([start/2, stop/1]).
+%%------------------------------------------------------------------------------
+%% 'application' Callback Functions
+%%------------------------------------------------------------------------------
start(_StartType, _StartArgs) ->
jaeger_passage_sup:start_link().
diff --git a/src/jaeger_passage_local_ns.erl b/src/jaeger_passage_local_ns.erl
index 22d4b62..48cd22f 100644
--- a/src/jaeger_passage_local_ns.erl
+++ b/src/jaeger_passage_local_ns.erl
@@ -1,8 +1,19 @@
+%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+%%
+%% @doc Application Local Name Server
+%%
+%% @private
-module(jaeger_passage_local_ns).
+%%------------------------------------------------------------------------------
+%% Application Internal API
+%%------------------------------------------------------------------------------
-export([child_spec/0]).
-export([reporter_name/1]).
+%%------------------------------------------------------------------------------
+%% Application Internal Functions
+%%------------------------------------------------------------------------------
-spec child_spec() -> supervisor:child_spec().
child_spec() ->
local:name_server_child_spec(?MODULE).
diff --git a/src/jaeger_passage_reporter.erl b/src/jaeger_passage_reporter.erl
index 8019937..adf97e1 100644
--- a/src/jaeger_passage_reporter.erl
+++ b/src/jaeger_passage_reporter.erl
@@ -1,3 +1,31 @@
+%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+%%
+%% @doc A reporter that sends the spans to an jaeger agent
+%%
+%% === Examples ===
+%%
+%% ```
+%% %% Starts `example_reporter'
+%% {ok, Reporter} = jaeger_passage_reporter:start(example_reporter).
+%% [example_reporter] = jaeger_passage_reporter:which_reporters().
+%%
+%% %% Registers `example_tracer'
+%% Context = jaeger_passage_span_context.
+%% Sampler = passage_sampler_all:new().
+%% ok = passage_tracer_registry:register(example_tracer, Context, Sampler, Reporter).
+%%
+%% %% Starts and finishes a span
+%% Span = passage:start_root_span(example, example_tracer).
+%%
+%% passage:finish_span(Span). % The span will send to the jaeger agent on the localhost
+%% '''
+%%
+%% === Refereces ===
+%%
+%% <ul>
+%% <li><a href="http://jaeger.readthedocs.io/en/latest/architecture/#agent">Jaeger - Architecture - Agent</a></li>
+%% <li><a href="http://jaeger.readthedocs.io/en/latest/deployment/#agent">Jaeger - Deployment - Agent</a></li>
+%% </ul>
-module(jaeger_passage_reporter).
-behaviour(passage_reporter).
@@ -49,18 +77,28 @@
%% Exported Types
%%------------------------------------------------------------------------------
-type reporter_id() :: atom().
+%% Reporter identifier.
-type start_options() :: [start_option()].
+%% Options for {@link start/2}.
-type start_option() :: {thrift_format, thrift_protocol:format()}
| {agent_host, inet:hostname()}
| {agent_port, inet:port_number()}
| {service_name, atom()}
| {service_tags, passage:tags()}.
+%% <ul>
+%% <li><b>thrift_format</b>: The format for encoding thrift messages. The default value is `compact'.</li>
+%% <li><b>agent_host</b>: The hostname of the jaeger agent. The default value is `"127.0.0.1"'.</li>
+%% <li><b>agent_port</b>: The port of the jaeger agent. The default values for the thrift format `compact' and `binary' are `6831' and `6832' respectively.</li>
+%% <li><b>service_name</b>: The name of the service which reports the spans. The default value is `ReporterId'.</li>
+%% <li><b>service_tags</b>: The tags of the service. The default value is `#{}'.</li>
+%% </ul>
%%------------------------------------------------------------------------------
%% Application Internal Functions
%%------------------------------------------------------------------------------
+%% @private
-spec start_link(reporter_id(), start_options()) -> {ok, pid()} | {error, Reason} when
Reason :: {already_started, pid()} | term().
start_link(ReporterId, Options) ->
@@ -76,18 +114,29 @@
start(ReporterId) ->
start(ReporterId, []).
--spec start(reporter_id(), start_options()) -> {ok, passage_reporter:reporter()} | {error, Reason} when
+%% @doc Starts a reporter process.
+-spec start(reporter_id(), start_options()) -> {ok, Reporter} | {error, Reason} when
+ Reporter :: passage_reporter:reporter(),
Reason :: {already_started, pid()} | term().
start(ReporterId, Options) ->
+ Args = [ReporterId, Options],
+ is_atom(ReporterId) orelse error(badarg, Args),
+ is_list(Options) orelse error(badarg, Args),
+
case jaeger_passage_reporter_sup:start_child(ReporterId, Options) of
{error, Reason} -> {error, Reason};
{ok, _Pid} -> {ok, passage_reporter:new(?MODULE, ReporterId)}
end.
+%% @doc Stops the reporter process.
+%%
+%% If the reporter which has the identifier `ReporterId' has not been started,
+%% it will be simply ignored.
-spec stop(reporter_id()) -> ok.
stop(ReporterId) ->
jaeger_passage_reporter_sup:stop_child(ReporterId).
+%% @doc Returns the list of the running reporters.
-spec which_reporters() -> [reporter_id()].
which_reporters() ->
jaeger_passage_reporter_sup:which_children().
diff --git a/src/jaeger_passage_reporter_sup.erl b/src/jaeger_passage_reporter_sup.erl
index f60fdb7..529fde3 100644
--- a/src/jaeger_passage_reporter_sup.erl
+++ b/src/jaeger_passage_reporter_sup.erl
@@ -1,21 +1,33 @@
+%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+%%
+%% @private
-module(jaeger_passage_reporter_sup).
-behaviour(supervisor).
+%%------------------------------------------------------------------------------
+%% Application Internal API
+%%------------------------------------------------------------------------------
-export([start_link/0]).
-export([start_child/2]).
-export([stop_child/1]).
-export([which_children/0]).
+%%------------------------------------------------------------------------------
+%% 'supervisor' Callback API
+%%------------------------------------------------------------------------------
-export([init/1]).
--define(SERVER, ?MODULE).
-
+%%------------------------------------------------------------------------------
+%% Application Internal Functions
+%%------------------------------------------------------------------------------
+-spec start_link() -> {ok, pid()} | {error, Reason :: term()}.
start_link() ->
- supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
--spec start_child(jaeger_passage_reporter:reporter_id(), jaeger_passage_reporter:start_options()) ->
- {ok, pid()} | {error, Reason} when
+-spec start_child(ReporterId, Options) -> {ok, pid()} | {error, Reason} when
+ ReporterId :: jaeger_passage_reporter:reporter_id(),
+ Options :: jaeger_passage_reporter:start_options(),
Reason :: {already_started, pid()} | term().
start_child(ReporterId, Options) ->
Child = #{
@@ -35,5 +47,8 @@
which_children() ->
[Id || {Id, _, _, _} <- supervisor:which_children(?MODULE)].
+%%------------------------------------------------------------------------------
+%% 'supervisor' Callback Functions
+%%------------------------------------------------------------------------------
init([]) ->
{ok, {#{}, []}}.
diff --git a/src/jaeger_passage_span_context.erl b/src/jaeger_passage_span_context.erl
index ec84381..0835f77 100644
--- a/src/jaeger_passage_span_context.erl
+++ b/src/jaeger_passage_span_context.erl
@@ -1,3 +1,15 @@
+%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+%%
+%% @doc Span context for Jaeger
+%%
+%% === References ===
+%%
+%% <ul>
+%% <li><a href="https://github.com/jaegertracing/jaeger-client-go/blob/v2.9.0/README.md">jaeger-client-go/README.md</a></li>
+%% <li><a href="https://github.com/uber/jaeger-client-go/tree/v2.9.0/context.go">context.go</a></li>
+%% <li><a href="https://github.com/uber/jaeger-client-go/tree/v2.9.0/propagation.go">propagation.go</a></li>
+%% </ul>
+
-module(jaeger_passage_span_context).
-behaviour(passage_span_context).
@@ -7,15 +19,16 @@
%%------------------------------------------------------------------------------
%% Exported API
%%------------------------------------------------------------------------------
+-export_type([state/0]).
+
+%%------------------------------------------------------------------------------
+%% Application Internal API
+%%------------------------------------------------------------------------------
-export([get_trace_id/1]).
-export([get_span_id/1]).
-export([get_debug_id/1]).
-export([get_flags/1]).
--export_type([state/0]).
--export_type([trace_id/0]).
--export_type([span_id/0]).
-
%%------------------------------------------------------------------------------
%% 'passage_span_context' Callback API
%%------------------------------------------------------------------------------
@@ -41,22 +54,39 @@
%% Exported Types
%%------------------------------------------------------------------------------
-opaque state() :: #?STATE{}.
-
--type trace_id() :: 0..16#FFFFFFFFFFFFFFFF.
-
--type span_id() :: 0..16#FFFFFFFF.
+%% The state of a jaeger span context.
%%------------------------------------------------------------------------------
-%% Exported Functions
+%% Types
%%------------------------------------------------------------------------------
+-type trace_id() :: 0..16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.
+%% Tracer identifier (128-bit unsigned integer).
+%%
+%% This represents globally unique ID of the trace.
+%% Usually generated as a random number.
+
+-type span_id() :: 0..16#FFFFFFFFFFFFFFFF.
+%% Span identifier (64-bit unsigned integer).
+%%
+%% This represents span ID that must be unique within its trace,
+%% but does not have to be globally unique.
+%%
+%% Note that the ID `0' is used for representing invalid spans.
+
+%%------------------------------------------------------------------------------
+%% Application Internal Functions
+%%------------------------------------------------------------------------------
+%% @private
-spec get_trace_id(passage_span_context:context()) -> trace_id().
get_trace_id(Context) ->
(passage_span_context:get_state(Context))#?STATE.trace_id.
+%% @private
-spec get_span_id(passage_span_context:context()) -> span_id().
get_span_id(Context) ->
(passage_span_context:get_state(Context))#?STATE.span_id.
+%% @private
-spec get_debug_id(passage_span_context:context()) -> {ok, binary()} | eror.
get_debug_id(Context) ->
State = passage_span_context:get_state(Context),
@@ -65,6 +95,7 @@
DebugId -> {ok, DebugId}
end.
+%% @private
-spec get_flags(passage_span_context:context()) -> 0..16#FFFFFFFF.
get_flags(Context) ->
State = passage_span_context:get_state(Context),
@@ -76,15 +107,15 @@
%% @private
make_span_context_state([]) ->
#?STATE{
- trace_id = rand:uniform(16#FFFFFFFFFFFFFFFF),
- span_id = rand:uniform(16#FFFFFFFF),
+ trace_id = rand:uniform(16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF),
+ span_id = rand:uniform(16#FFFFFFFFFFFFFFFF),
is_sampled = true
};
make_span_context_state([{_, Ref} | _]) ->
#?STATE{trace_id = TraceId} = passage_span_context:get_state(passage_span:get_context(Ref)),
#?STATE{
trace_id = TraceId,
- span_id = rand:uniform(16#FFFFFFFF),
+ span_id = rand:uniform(16#FFFFFFFFFFFFFFFF),
is_sampled = true
}.
diff --git a/src/jaeger_passage_sup.erl b/src/jaeger_passage_sup.erl
index 9d51cf1..bbcf339 100644
--- a/src/jaeger_passage_sup.erl
+++ b/src/jaeger_passage_sup.erl
@@ -1,16 +1,30 @@
+%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+%%
+%% @private
-module(jaeger_passage_sup).
-behaviour(supervisor).
+%%------------------------------------------------------------------------------
+%% Application Internal API
+%%------------------------------------------------------------------------------
-export([start_link/0]).
+%%------------------------------------------------------------------------------
+%% 'supervisor' Callback API
+%%------------------------------------------------------------------------------
-export([init/1]).
--define(SERVER, ?MODULE).
-
+%%------------------------------------------------------------------------------
+%% Application Internal Functions
+%%-------------------------------------------------------------------------------
+-spec start_link() -> {ok, pid()} | {error, Reason :: term()}.
start_link() ->
- supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+%%------------------------------------------------------------------------------
+%% 'supervisor' Callback Functions
+%%------------------------------------------------------------------------------
init([]) ->
NameServer = jaeger_passage_local_ns:child_spec(),
ReporterSup = #{
diff --git a/src/jaeger_passage_thrift.erl b/src/jaeger_passage_thrift.erl
index 04d53cb..ad71b6c 100644
--- a/src/jaeger_passage_thrift.erl
+++ b/src/jaeger_passage_thrift.erl
@@ -1,12 +1,19 @@
-%% https://github.com/jaegertracing/jaeger-idl/blob/master/thrift/agent.thrift
-%% https://github.com/jaegertracing/jaeger-idl/blob/master/thrift/jaeger.thrift
+%% @copyright 2017 Takeru Ohta <phjgt308@gmail.com>
+%%
+%% @doc Thrift messages for jaeger
+%%
+%% IDL:
+%% - https://github.com/jaegertracing/jaeger-idl/blob/84a83104/thrift/agent.thrift
+%% - https://github.com/jaegertracing/jaeger-idl/blob/84a83104/thrift/jaeger.thrift
+%%
+%% @private
-module(jaeger_passage_thrift).
-include_lib("thrift_protocol/include/thrift_protocol.hrl").
-include("constants.hrl").
%%------------------------------------------------------------------------------
-%% Exported API
+%% Application Internal API
%%------------------------------------------------------------------------------
-export([make_emit_batch_message/3]).
@@ -22,25 +29,24 @@
-define(REF_TYPE_CHILD_OF, 0).
-define(REF_TYPE_FOLLOWS_FROM, 1).
+-define(STRUCT(F1), #thrift_protocol_struct{fields = #{1 => F1}}).
+-define(STRUCT(F1, F2), #thrift_protocol_struct{fields = #{1 => F1, 2 => F2}}).
+
+-define(LIST(Es), #thrift_protocol_list{element_type = struct, elements = Es}).
+
%%------------------------------------------------------------------------------
-%% Exported Functions
+%% Application Internal API
%%------------------------------------------------------------------------------
-spec make_emit_batch_message(atom(), passage:tags(), [passage_span:span()]) ->
thrift_protocol:message().
make_emit_batch_message(ServiceName, ServiceTags, Spans) ->
Process = make_process(ServiceName, ServiceTags),
- Batch =
- #thrift_protocol_struct{
- fields = #{
- 1 => Process,
- 2 => make_spans(Spans)
- }
- },
+ Batch = ?STRUCT(Process, make_spans(Spans)),
#thrift_protocol_message{
- method_name = <<"emitBatch">>,
+ method_name = <<"emitBatch">>,
message_type = oneway,
- sequence_id = 0,
- body = #thrift_protocol_struct{fields = #{ 1 => Batch }}
+ sequence_id = 0,
+ body = ?STRUCT(Batch)
}.
%%------------------------------------------------------------------------------
@@ -48,20 +54,12 @@
%%------------------------------------------------------------------------------
-spec make_process(atom(), passage:tags()) -> thrift_protocol:struct().
make_process(ServiceName, ServiceTags) ->
- #thrift_protocol_struct{
- fields = #{
- 1 => atom_to_binary(ServiceName, utf8),
- 2 => make_tags(ServiceTags)
- }
- }.
+ ?STRUCT(atom_to_binary(ServiceName, utf8), make_tags(ServiceTags)).
-spec make_tags(Tags) -> thrift_protocol:thrift_list() when
Tags :: #{atom() => term()}.
make_tags(Tags) ->
- #thrift_protocol_list{
- element_type = struct,
- elements = maps:fold(fun (K, V, Acc) -> [make_tag(K, V) | Acc] end, [], Tags)
- }.
+ ?LIST(maps:fold(fun (K, V, Acc) -> [make_tag(K, V) | Acc] end, [], Tags)).
-spec make_tag(passage:tag_name(), passage:tag_value()) -> thrift_protocol:struct().
make_tag(Key, Value) ->
@@ -74,12 +72,15 @@
}
}.
--spec make_tag_value(term()) -> {non_neg_integer(), thrift_protocol:field_id(), thrift_protocol:data()}.
-make_tag_value(X) when is_boolean(X) -> {?TAG_TYPE_BOOL, 5, X};
-make_tag_value(X) when is_atom(X) -> {?TAG_TYPE_STRING, 3, atom_to_binary(X, utf8)};
-make_tag_value(X) when is_binary(X) -> {?TAG_TYPE_STRING, 3, X};
-make_tag_value(X) when is_float(X) -> {?TAG_TYPE_DOUBLE, 4, X};
-make_tag_value(X) when is_integer(X) -> {?TAG_TYPE_LONG, 6, {i64, X}};
+-spec make_tag_value(term()) -> {ValueType, FieldId, Value} when
+ ValueType :: non_neg_integer(),
+ FieldId :: thrift_protocol:field_id(),
+ Value :: thrift_protocol:data().
+make_tag_value(X) when is_boolean(X) -> {?TAG_TYPE_BOOL, 5, X};
+make_tag_value(X) when is_atom(X) -> {?TAG_TYPE_STRING, 3, atom_to_binary(X, utf8)};
+make_tag_value(X) when is_binary(X) -> {?TAG_TYPE_STRING, 3, X};
+make_tag_value(X) when is_float(X) -> {?TAG_TYPE_DOUBLE, 4, X};
+make_tag_value(X) when is_integer(X) -> {?TAG_TYPE_LONG, 6, {i64, X}};
make_tag_value(X) ->
try list_to_binary(X) of
Binary -> {?TAG_TYPE_STRING, 3, Binary}
@@ -91,19 +92,18 @@
-spec make_spans([passage_span:span()]) -> thrift_protocol:thrift_list().
make_spans(Spans) ->
- #thrift_protocol_list{
- element_type = struct,
- elements = lists:map(fun make_span/1, Spans)
- }.
+ ?LIST(lists:map(fun make_span/1, Spans)).
-spec make_span(passage_span:span()) -> thrift_protocol:struct().
make_span(Span) ->
Context = passage_span:get_context(Span),
TraceId = jaeger_passage_span_context:get_trace_id(Context),
ParentSpanId =
- case lists:filter(fun ({Type, _}) -> Type =:= child_of end, passage_span:get_refs(Span)) of
- [] -> 0;
- [{_, Ref} | _] -> jaeger_passage_span_context:get_span_id(passage_span:get_context(Ref))
+ case [Ref || {child_of, Ref} <- passage_span:get_refs(Span)] of
+ [] -> 0;
+ [Ref | _] ->
+ RefContext = passage_span:get_context(Ref),
+ jaeger_passage_span_context:get_span_id(RefContext)
end,
Tags0 = passage_span:get_tags(Span),
Tags1 =
@@ -113,8 +113,8 @@
end,
#thrift_protocol_struct{
fields = #{
- 1 => {i64, TraceId band 16#FFFFFFFF},
- 2 => {i64, TraceId bsr 32},
+ 1 => {i64, TraceId band 16#FFFFFFFFFFFFFFFF},
+ 2 => {i64, TraceId bsr 64},
3 => {i64, jaeger_passage_span_context:get_span_id(Context)},
4 => {i64, ParentSpanId},
5 => atom_to_binary(passage_span:get_operation_name(Span), utf8),
@@ -127,30 +127,19 @@
}
}.
--spec timestamp_to_us(erlang:timestamp()) -> non_neg_integer().
-timestamp_to_us(Timestamp) ->
- timer:now_diff(Timestamp, {0, 0, 0}).
-
--spec get_duration_us(passage_span:span()) -> non_neg_integer().
-get_duration_us(Span) ->
- Start = passage_span:get_start_time(Span),
- {ok, Finish} = passage_span:get_finish_time(Span),
- timer:now_diff(Finish, Start).
-
-spec make_references(passage_span:normalized_refs()) -> thrift_protocol:thrift_list().
make_references(Refs) ->
- #thrift_protocol_list{
- element_type = struct,
- elements =
- lists:map(
- fun make_reference/1,
- lists:filter(
- fun ({_, Span}) ->
- Context = passage_span:get_context(Span),
- jaeger_passage_span_context:get_trace_id(Context) =/= 0
- end,
- Refs))
- }.
+ Elements =
+ lists:filtermap(
+ fun (Ref = {_, Span}) ->
+ Context = passage_span:get_context(Span),
+ case jaeger_passage_span_context:get_span_id(Context) of
+ 0 -> false;
+ _ -> {true, make_reference(Ref)}
+ end
+ end,
+ Refs),
+ ?LIST(Elements).
-spec make_reference(passage_span:normalized_ref()) -> thrift_protocol:struct().
make_reference(Ref) ->
@@ -165,24 +154,26 @@
#thrift_protocol_struct{
fields = #{
1 => {i32, RefType},
- 2 => {i64, TraceId band 16#FFFFFFFF},
- 3 => {i64, TraceId bsr 32},
+ 2 => {i64, TraceId band 16#FFFFFFFFFFFFFFFF},
+ 3 => {i64, TraceId bsr 64},
4 => {i64, SpanId}
}
}.
-spec make_logs([passage_span:log()]) -> thrift_protocol:thrift_list().
make_logs(Logs) ->
- #thrift_protocol_list{
- element_type = struct,
- elements = lists:map(fun make_log/1, Logs)
- }.
+ ?LIST(lists:map(fun make_log/1, Logs)).
-spec make_log(passage_span:log()) -> thrift_protocol:struct().
make_log({Fields, Time}) ->
- #thrift_protocol_struct{
- fields = #{
- 1 => {i64, timestamp_to_us(Time)},
- 2 => make_tags(Fields)
- }
- }.
+ ?STRUCT({i64, timestamp_to_us(Time)}, make_tags(Fields)).
+
+-spec timestamp_to_us(erlang:timestamp()) -> non_neg_integer().
+timestamp_to_us(Timestamp) ->
+ timer:now_diff(Timestamp, {0, 0, 0}).
+
+-spec get_duration_us(passage_span:span()) -> non_neg_integer().
+get_duration_us(Span) ->
+ Start = passage_span:get_start_time(Span),
+ {ok, Finish} = passage_span:get_finish_time(Span),
+ timer:now_diff(Finish, Start).