Current section
Files
Jump to
Current section
Files
src/oc_google_reporter.erl
%%%------------------------------------------------------------------------
%% Copyright 2017, OpenCensus Authors
%% Licensed 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 Reporter for sending trace spans to Google Stackdriver Trace.
%% @end
%%%-----------------------------------------------------------------------
-module(oc_google_reporter).
-export([init/1,
report/2]).
-include_lib("opencensus/include/opencensus.hrl").
-define(TRACE_URL(ProjectId),
<<"https://cloudtrace.googleapis.com/v1/projects/", ProjectId/binary, "/traces">>).
init(Configuration) ->
case maps:find(project_id, Configuration) of
{ok, ProjectId} ->
Source = maps:get(credentials_source, Configuration, default),
#{credentials => Source,
project_id => ProjectId};
error ->
error_logger:info_msg("no reporter: project_id missing from oc_google_reporter configuration"),
ignore
end.
report(Spans, Opts) ->
ProjectId = maps:get(project_id, Opts),
Source = maps:get(credentials, Opts),
{ok, Creds} = augle:creds_from(Source),
Headers = augle:headers(Creds),
PatchBody = jsx:encode(#{traces => format_spans(ProjectId, Spans)}),
case hackney:patch(?TRACE_URL(ProjectId), Headers, PatchBody, []) of
{ok, Status, _Headers, _Client} when Status =:= 204
; Status =:= 200 ->
ok;
{ok, _Status, _Headers, Client} ->
{ok, ErrorJson} = hackney:body(Client),
Error = jsx:decode(ErrorJson, [return_maps]),
error_logger:info_msg("failed to upload spans to google trace: ~p", [Error]),
%% try again with a backoff?
ok;
{error, timeout} ->
%% try again with a backoff?
ok;
{error, _Reason} ->
%% try again with a backoff?
ok
end.
format_spans(ProjectId, Spans) when is_list(Spans) ->
[format_span(ProjectId, Span) || Span <- Spans].
format_span(ProjectId, #span{name=Name,
trace_id=TraceId,
span_id=SpanId,
parent_span_id=ParentSpanId,
start_time=StartTime,
end_time=EndTime,
attributes=Attributes}) ->
EncodedTraceId = list_to_binary(io_lib:format("~32.16.0b", [TraceId])),
EncodedSpanId = integer_to_binary(SpanId),
Span = case ParentSpanId of
undefined ->
#{spanId => EncodedSpanId,
name => term_to_string(Name),
startTime => wts:rfc3339(StartTime),
endTime => wts:rfc3339(EndTime),
labels => Attributes
};
_ ->
EncodedParentSpanId = integer_to_binary(ParentSpanId),
#{spanId => EncodedSpanId,
name => term_to_string(Name),
startTime => wts:rfc3339(StartTime),
endTime => wts:rfc3339(EndTime),
parentSpanId => EncodedParentSpanId,
labels => Attributes
}
end,
#{projectId => ProjectId,
traceId => EncodedTraceId,
spans => [Span]}.
term_to_string(Term) when is_binary(Term) ->
Term;
term_to_string(Term) ->
list_to_binary(io_lib:format("~p", [Term])).