Packages

Elli instrumentation for OpenCensus stats and tracing

Current section

Files

Jump to
opencensus_elli src oc_elli_middleware.erl
Raw

src/oc_elli_middleware.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 Elli middleware for tracing requests and recording stats.
%% @end
%%%-------------------------------------------------------------------------
-module(oc_elli_middleware).
-behaviour(elli_handler).
-include_lib("elli/include/elli.hrl").
-include_lib("opencensus/include/opencensus.hrl").
-export([preprocess/2,
handle/2,
handle_event/3]).
preprocess(Req=#req{raw_path=RawPath, method=Method}, _) ->
SpanCtxHeader = elli_request:get_header(oc_span_ctx_header:field_name(), Req, undefined),
ParentSpanCtx = oc_span_ctx_header:decode(SpanCtxHeader),
%% update tags from header and new request tags
BinMethod = to_binary(Method),
UserAgent = elli_request:get_header(<<"User-Agent">>, Req, <<>>),
Host = elli_request:get_header(<<"Host">>, Req, <<>>),
TagMap = #{http_server_method => BinMethod,
http_server_path => RawPath,
http_server_host => Host},
TagsHeader = elli_request:get_header(oc_tag_ctx_header:field_name(), Req, <<>>),
case oc_tag_ctx_header:decode(TagsHeader) of
{ok, ReqTags} ->
ocp:with_tags(oc_tags:update(ReqTags, TagMap));
{error, {_Module, _Error}} ->
%% ?LOG_INFO(Module:format_error(Error))
ocp:with_tags(oc_tags:new(TagMap))
end,
%% start child
_ = ocp:with_span_ctx(oc_trace:start_span(RawPath, ParentSpanCtx,
#{remote_parent => true,
kind => ?SPAN_KIND_SERVER,
attributes => #{<<"http.url">> => RawPath,
<<"http.host">> => Host,
<<"http.user_agent">> => UserAgent,
<<"http.method">> => BinMethod}})),
Req.
handle(_Req, _Config) ->
ignore.
handle_event(elli_startup, _Args, _Config) ->
%% register measures, if not already registered
oc_elli:register_measures(),
ok;
handle_event(request_complete, Args, Config) ->
handle_full_response(request_complete, Args, Config);
handle_event(chunk_complete, Args, Config) ->
handle_full_response(chunk_complete, Args, Config);
handle_event(request_timeout, _, _Config) ->
finish_exception(request_timeout, request_timeout);
handle_event(request_parse_error, [Reason], _Config) ->
finish_exception(request_parse_error, Reason);
handle_event(client_closed, [RequestPart], _Config) ->
finish_exception(client_closed, {request_part, RequestPart});
handle_event(client_timeout, [RequestPart], _Config) ->
finish_exception(client_timeout, {request_part, RequestPart});
handle_event(bad_request, [Reason], _Config) ->
finish_exception(bad_request, Reason);
handle_event(request_error, [_Req, Exception, Stacktrace], _Config) ->
finish_exception(Exception, Stacktrace);
handle_event(request_throw, [_Req, Exception, Stacktrace], _Config) ->
finish_exception(Exception, Stacktrace);
handle_event(request_exit, [_Req, Exception, Stacktrace], _Config) ->
finish_exception(Exception, Stacktrace);
handle_event(_Event, _Args, _Config) ->
ok.
%%
handle_full_response(_Type, [_Req, Code, _Hs, _B, {Timings, Sizes}], _Config) ->
ocp:update_tags(#{http_server_status => integer_to_list(Code)}),
case proplists:get_value(req_body, Sizes) of
undefined ->
ok;
UncompressedReqSize ->
ocp:record('opencensus.io/http/server/received_bytes', UncompressedReqSize),
ocp:add_time_event(oc_trace:message_event(?MESSAGE_EVENT_TYPE_RECEIVED, 0,
UncompressedReqSize, UncompressedReqSize))
end,
ServerLatency = proplists:get_value(request_end, Timings) - proplists:get_value(headers_start, Timings),
ocp:record('opencensus.io/http/server/server_latency', ServerLatency),
UncompressedRespSize = size(Sizes, response_body),
ocp:record('opencensus.io/http/server/sent_bytes', UncompressedRespSize),
ocp:add_time_event(oc_trace:message_event(?MESSAGE_EVENT_TYPE_SENT, 0, UncompressedRespSize, UncompressedRespSize)),
ocp:put_attribute(<<"http.status_code">>, Code),
ocp:set_status(opencensus:http_status_to_trace_status(Code), <<>>),
ocp:finish_span().
size(Sizes, response_body) ->
case proplists:get_value(chunks, Sizes) of
undefined ->
case proplists:get_value(file, Sizes) of
undefined ->
proplists:get_value(resp_body, Sizes);
FileSize -> FileSize
end;
ChunksSize -> ChunksSize
end.
finish_exception(Exception, Stacktrace) ->
ocp:put_attributes(#{<<"stacktrace">> => term_to_string(Stacktrace),
<<"error.message">> => term_to_string(Exception)}).
to_binary(Method) when is_atom(Method) ->
atom_to_binary(Method, utf8);
to_binary(Method) ->
Method.
term_to_string(Term) ->
list_to_binary(io_lib:format("~p", [Term])).