Packages

Library with otter common functions

Current section

Files

Jump to
otter_lib src otter_lib_zipkin_thrift.erl
Raw

src/otter_lib_zipkin_thrift.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 facilitates encoding/decoding of thrift data
%%% lists encoded with the binary protocol, suitable for
%%% sending/receiving spans on the zipkin interface.
%%% @end
%%% -------------------------------------------------------------------
-module(otter_lib_zipkin_thrift).
-export([
encode_spans/3,
decode_spans/1
]).
-include("otter.hrl").
%%--------------------------------------------------------------------
%% @doc Encodes a list of spans to a thrift (binary encoded) binary.
%% ExtraTags is a list of key-value pairs that are added for each span in
%% the list (e.g. the "lc" tag which tags the span with service information)
%% in OpenZipkin. ServiceDefaults is used to complete the service information
%% when only partial information is provided in span information e.g. default
%% or only service name.
%% @end
%%--------------------------------------------------------------------
-spec encode_spans(Spans :: [span()], ExtraTags :: list(), ServiceDefaults :: term()) -> binary().
encode_spans(Spans, ExtraTags, ServiceDefaults) ->
otter_lib_thrift:encode_implicit_list(
{struct, [span_to_struct(S, ExtraTags, ServiceDefaults) || S <- Spans]}
).
%%--------------------------------------------------------------------
%% @doc Decodes a binary to a list of spans.
%% @end
%%--------------------------------------------------------------------
-spec decode_spans(BinaryData :: binary()) -> [span()].
decode_spans(Data) ->
{
{struct, StructList},
_Rest
} = otter_lib_thrift:decode_implicit_list(Data),
[struct_to_span(S) || S <- StructList].
span_to_struct(#span{
id = Id,
trace_id = TraceId,
name = Name,
parent_id = ParentId,
logs = Logs,
tags = Tags,
timestamp = Timestamp,
duration = Duration
}, ExtraTags, ServiceDefaults) ->
[
{1, i64, TraceId},
{3, string, otter_lib:to_bin(Name)},
{4, i64, Id}
] ++
case ParentId of
undefined ->
[];
ParentId ->
[{5, i64, ParentId}]
end ++
[
{6, list, {
struct,
[log_to_annotation(Log, ServiceDefaults) || Log <- Logs]
}},
{8, list, {
struct,
[tag_to_binary_annotation(Tag, ServiceDefaults) || Tag <- ExtraTags ++ Tags]
}},
{10, i64, Timestamp},
{11, i64, Duration}
].
log_to_annotation({Timestamp, Text}, _ServiceDefaults) ->
[
{1, i64, Timestamp},
{2, string, otter_lib:to_bin(Text)}
];
log_to_annotation({Timestamp, Text, Service}, ServiceDefaults) ->
[
{1, i64, Timestamp},
{2, string, otter_lib:to_bin(Text)},
{3,struct, host_to_struct(Service, ServiceDefaults)}
].
tag_to_binary_annotation({Key, Value}, _ServiceDefaults) ->
[
{1, string, otter_lib:to_bin(Key)},
{2, string, otter_lib:to_bin(Value)},
{3,i32,6}
];
tag_to_binary_annotation({Key, Value, Service}, ServiceDefaults) ->
[
{1, string, otter_lib:to_bin(Key)},
{2, string, otter_lib:to_bin(Value)},
{3,i32,6},
{4,struct, host_to_struct(Service, ServiceDefaults)}
].
host_to_struct(default, {Service,Ip, Port}) ->
host_to_struct({Service, Ip, Port});
host_to_struct(Service, {_, Ip, Port}) ->
host_to_struct({Service, Ip, Port}).
host_to_struct({Service, Ip, Port}) ->
[
{1,i32, otter_lib:ip_to_i32(Ip)},
{2,i16, Port},
{3,string, otter_lib:to_bin(Service)}
].
struct_to_span(StructData) ->
struct_to_span(StructData, #span{}).
struct_to_span([{1, i64, TraceId}| Rest], Span) ->
struct_to_span(Rest, Span#span{trace_id = TraceId});
struct_to_span([{3, string, Name}| Rest], Span) ->
struct_to_span(Rest, Span#span{name = Name});
struct_to_span([{4, i64, Id}| Rest], Span) ->
struct_to_span(Rest, Span#span{id = Id});
struct_to_span([{5, i64, ParentId}| Rest], Span) ->
struct_to_span(Rest, Span#span{parent_id = ParentId});
struct_to_span([{6, list, {struct, Annotations}}| Rest], Span) ->
Logs = [annotation_to_log(Annotation) || Annotation <- Annotations],
struct_to_span(Rest, Span#span{logs = Logs});
struct_to_span([{8, list, {struct, BinAnnotations}}| Rest], Span) ->
Tags = [bin_annotation_to_tag(BinAnnotation) || BinAnnotation <- BinAnnotations],
struct_to_span(Rest, Span#span{tags = Tags});
struct_to_span([{10, i64, Timestamp}| Rest], Span) ->
struct_to_span(Rest, Span#span{timestamp = Timestamp});
struct_to_span([{11, i64, Duration}| Rest], Span) ->
struct_to_span(Rest, Span#span{duration = Duration});
struct_to_span([_ | Rest], Span) ->
struct_to_span(Rest, Span);
struct_to_span([], Span) ->
Span.
annotation_to_log(StructData) ->
annotation_to_log(StructData, {undefined,undefined,undefined}).
annotation_to_log([{1, i64, Timestamp} | Rest], {_, Text, Host}) ->
annotation_to_log(Rest, {Timestamp, Text, Host});
annotation_to_log([{2, string, Text} | Rest], {Timestamp, _, Host}) ->
annotation_to_log(Rest, {Timestamp, Text, Host});
annotation_to_log([{3, struct, HostStruct} | Rest], {Timestamp, Text, _}) ->
annotation_to_log(Rest, {Timestamp, Text, struct_to_host(HostStruct)});
annotation_to_log([_ | Rest], Log) ->
annotation_to_log(Rest, Log);
annotation_to_log([], {Timestamp, Text, undefined}) ->
{Timestamp, Text};
annotation_to_log([], Log) ->
Log.
bin_annotation_to_tag(StructData) ->
bin_annotation_to_tag(StructData, {undefined,undefined,undefined}).
bin_annotation_to_tag([{1, string, Key} | Rest], {_, Value, Host}) ->
bin_annotation_to_tag(Rest, {Key, Value, Host});
bin_annotation_to_tag([{2, string, Value} | Rest], {Key, _, Host}) ->
bin_annotation_to_tag(Rest, {Key, Value, Host});
bin_annotation_to_tag([{4, struct, HostStruct} | Rest], {Key, Value, _}) ->
bin_annotation_to_tag(Rest, {Key, Value, struct_to_host(HostStruct)});
bin_annotation_to_tag([_ | Rest], Tag) ->
bin_annotation_to_tag(Rest, Tag);
bin_annotation_to_tag([], {Key, Value, undefined}) ->
{Key, Value};
bin_annotation_to_tag([], Tag) ->
Tag.
struct_to_host(StructData) ->
struct_to_host(StructData, {undefined, undefined, undefined}).
struct_to_host([{1, i32, IntIp} | Rest], {Service, _, Port}) ->
<<Ip1, Ip2, Ip3, Ip4>> = <<IntIp:32>>,
struct_to_host(Rest, {Service, {Ip1, Ip2, Ip3, Ip4}, Port});
struct_to_host([{2, i16, Port} | Rest], {Service, Ip, _}) ->
struct_to_host(Rest, {Service, Ip, Port});
struct_to_host([{3, string, Service} | Rest], {_, Ip, Port}) ->
struct_to_host(Rest, {Service, Ip, Port});
struct_to_host([_ | Rest], Host) ->
struct_to_host(Rest, Host);
struct_to_host([], Host) ->
Host.