mirror of
https://github.com/valitydev/woody_erlang.git
synced 2024-11-06 02:15:19 +00:00
98 lines
2.5 KiB
Erlang
98 lines
2.5 KiB
Erlang
|
-module(woody_ct_otel_collector).
|
||
|
|
||
|
-behaviour(gen_server).
|
||
|
|
||
|
-export([
|
||
|
start_link/0,
|
||
|
get_trace/1,
|
||
|
get_traces/0
|
||
|
]).
|
||
|
|
||
|
-export([
|
||
|
init/1,
|
||
|
handle_call/3,
|
||
|
handle_cast/2,
|
||
|
handle_info/2
|
||
|
]).
|
||
|
|
||
|
-include_lib("opentelemetry/include/otel_span.hrl").
|
||
|
|
||
|
-type span() :: #span{}.
|
||
|
|
||
|
-type span_node() :: #{span := span(), children := [span_node()]}.
|
||
|
|
||
|
-type trace() :: #{
|
||
|
id := opentelemetry:trace_id(),
|
||
|
node := span_node()
|
||
|
}.
|
||
|
|
||
|
%
|
||
|
|
||
|
-spec start_link() -> {ok, pid()}.
|
||
|
start_link() ->
|
||
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||
|
|
||
|
-spec get_trace(opentelemetry:trace_id()) -> {ok, trace()} | {error, notfound}.
|
||
|
get_trace(TraceId) ->
|
||
|
gen_server:call(?MODULE, {trace, TraceId}).
|
||
|
|
||
|
-spec get_traces() -> {ok, [trace()]}.
|
||
|
get_traces() ->
|
||
|
gen_server:call(?MODULE, traces).
|
||
|
|
||
|
-spec init(_) -> {ok, _}.
|
||
|
init(_Opts) ->
|
||
|
{ok, #{}}.
|
||
|
|
||
|
-spec handle_info(_, T) -> {noreply, T}.
|
||
|
handle_info({span, Span}, State0) ->
|
||
|
State1 = maps:update_with(Span#span.trace_id, fun(V) -> [Span | V] end, [Span], State0),
|
||
|
{noreply, State1};
|
||
|
handle_info(_Msg, State) ->
|
||
|
{noreply, State}.
|
||
|
|
||
|
-spec handle_call(_, _, T) -> {noreply, T}.
|
||
|
handle_call(traces, _From, State) ->
|
||
|
Result = maps:map(fun(TraceId, Spans) -> build_trace(TraceId, Spans) end, State),
|
||
|
{reply, maps:values(Result), State};
|
||
|
handle_call({trace, TraceId}, _From, State) ->
|
||
|
Result =
|
||
|
case maps:get(TraceId, State, undefined) of
|
||
|
undefined -> {error, notfound};
|
||
|
Spans -> {ok, build_trace(TraceId, Spans)}
|
||
|
end,
|
||
|
{reply, Result, State};
|
||
|
handle_call(_Msg, _From, State) ->
|
||
|
{noreply, State}.
|
||
|
|
||
|
-spec handle_cast(_, T) -> {noreply, T}.
|
||
|
handle_cast(_Msg, State) ->
|
||
|
{noreply, State}.
|
||
|
|
||
|
%
|
||
|
|
||
|
build_trace(TraceId, Spans0) ->
|
||
|
Spans1 = lists:sort(fun(#span{start_time = A}, #span{start_time = B}) -> A < B end, Spans0),
|
||
|
[RootSpan | _] = lists:filter(
|
||
|
fun
|
||
|
(#span{parent_span_id = undefined}) -> true;
|
||
|
(_) -> false
|
||
|
end,
|
||
|
Spans1
|
||
|
),
|
||
|
#{
|
||
|
id => TraceId,
|
||
|
node => lists:foldl(fun(Span, RootNode) -> update_node(Span, RootNode) end, new_span_node(RootSpan), Spans1)
|
||
|
}.
|
||
|
|
||
|
update_node(
|
||
|
Span = #span{parent_span_id = ParentId},
|
||
|
SpanNode = #{span := #span{span_id = ParentId}, children := Children}
|
||
|
) ->
|
||
|
SpanNode#{children => [new_span_node(Span) | Children]};
|
||
|
update_node(Span, SpanNode = #{children := Children}) ->
|
||
|
SpanNode#{children => lists:map(fun(Child) -> update_node(Span, Child) end, Children)}.
|
||
|
|
||
|
new_span_node(Span) ->
|
||
|
#{span => Span, children => []}.
|