Handle binary and undefined response status and rename http.status to http.status_code in `opentelemetry-cowboy` (#48)
* Handle binary resp_status from Cowboy and rename http.status to http.status_code * Fix test to use http.status_code as well * Handle resp_status could be undefined but not error - This could be due to websocket upgrade request. * Rename Status1 and transform_status to a more concise naming * Add test case for handling binary response code * Fix syntax and failing tests * Always convert cowboy status to status code * Set otel span status as error when status code >= 500
This commit is contained in:
parent
ddb2d3b963
commit
e3f0ec8ee0
|
@ -56,16 +56,24 @@ handle_event([cowboy, request, stop], Measurements, Meta, _Config) ->
|
||||||
'http.response_content_length' => maps:get(resp_body_length, Measurements)
|
'http.response_content_length' => maps:get(resp_body_length, Measurements)
|
||||||
},
|
},
|
||||||
otel_span:set_attributes(Ctx, Attributes),
|
otel_span:set_attributes(Ctx, Attributes),
|
||||||
case Status of
|
StatusCode = transform_status_to_code(Status),
|
||||||
|
case StatusCode of
|
||||||
undefined ->
|
undefined ->
|
||||||
{ErrorType, Error, Reason} = maps:get(error, Meta),
|
case maps:get(error, Meta, undefined) of
|
||||||
otel_span:add_events(Ctx, [opentelemetry:event(ErrorType, #{error => Error, reason => Reason})]),
|
{ErrorType, Error, Reason} ->
|
||||||
otel_span:set_status(Ctx, opentelemetry:status(?OTEL_STATUS_ERROR, Reason));
|
otel_span:add_events(Ctx, [opentelemetry:event(ErrorType, #{error => Error, reason => Reason})]),
|
||||||
Status when Status >= 400 ->
|
otel_span:set_status(Ctx, opentelemetry:status(?OTEL_STATUS_ERROR, Reason));
|
||||||
otel_span:set_attribute(Ctx, 'http.status', Status),
|
_ ->
|
||||||
|
% do nothing first as I'm unsure how should we handle this
|
||||||
|
ok
|
||||||
|
end;
|
||||||
|
StatusCode when StatusCode >= 500 ->
|
||||||
|
otel_span:set_attribute(Ctx, 'http.status_code', StatusCode),
|
||||||
otel_span:set_status(Ctx, opentelemetry:status(?OTEL_STATUS_ERROR, <<"">>));
|
otel_span:set_status(Ctx, opentelemetry:status(?OTEL_STATUS_ERROR, <<"">>));
|
||||||
Status when Status < 400 ->
|
StatusCode when StatusCode >= 400 ->
|
||||||
otel_span:set_attribute(Ctx, 'http.status', Status)
|
otel_span:set_attribute(Ctx, 'http.status_code', StatusCode);
|
||||||
|
StatusCode when StatusCode < 400 ->
|
||||||
|
otel_span:set_attribute(Ctx, 'http.status_code', StatusCode)
|
||||||
end,
|
end,
|
||||||
otel_telemetry:end_telemetry_span(?TRACER_ID, Meta),
|
otel_telemetry:end_telemetry_span(?TRACER_ID, Meta),
|
||||||
otel_ctx:clear();
|
otel_ctx:clear();
|
||||||
|
@ -80,8 +88,9 @@ handle_event([cowboy, request, exception], Measurements, Meta, _Config) ->
|
||||||
} = Meta,
|
} = Meta,
|
||||||
otel_span:record_exception(Ctx, Kind, Reason, Stacktrace, []),
|
otel_span:record_exception(Ctx, Kind, Reason, Stacktrace, []),
|
||||||
otel_span:set_status(Ctx, opentelemetry:status(?OTEL_STATUS_ERROR, <<"">>)),
|
otel_span:set_status(Ctx, opentelemetry:status(?OTEL_STATUS_ERROR, <<"">>)),
|
||||||
|
StatusCode = transform_status_to_code(Status),
|
||||||
otel_span:set_attributes(Ctx, #{
|
otel_span:set_attributes(Ctx, #{
|
||||||
'http.status' => Status,
|
'http.status_code' => StatusCode,
|
||||||
'http.request_content_length' => maps:get(req_body_length, Measurements),
|
'http.request_content_length' => maps:get(req_body_length, Measurements),
|
||||||
'http.response_content_length' => maps:get(resp_body_length, Measurements)
|
'http.response_content_length' => maps:get(resp_body_length, Measurements)
|
||||||
}),
|
}),
|
||||||
|
@ -93,8 +102,9 @@ handle_event([cowboy, request, early_error], Measurements, Meta, _Config) ->
|
||||||
reason := {ErrorType, Error, Reason},
|
reason := {ErrorType, Error, Reason},
|
||||||
resp_status := Status
|
resp_status := Status
|
||||||
} = Meta,
|
} = Meta,
|
||||||
|
StatusCode = transform_status_to_code(Status),
|
||||||
Attributes = #{
|
Attributes = #{
|
||||||
'http.status' => Status,
|
'http.status_code' => StatusCode,
|
||||||
'http.response_content_length' => maps:get(resp_body_length, Measurements)
|
'http.response_content_length' => maps:get(resp_body_length, Measurements)
|
||||||
},
|
},
|
||||||
Ctx = otel_telemetry:start_telemetry_span(?TRACER_ID, <<"HTTP Error">>, Meta, #{attributes => Attributes}),
|
Ctx = otel_telemetry:start_telemetry_span(?TRACER_ID, <<"HTTP Error">>, Meta, #{attributes => Attributes}),
|
||||||
|
@ -103,6 +113,13 @@ handle_event([cowboy, request, early_error], Measurements, Meta, _Config) ->
|
||||||
otel_telemetry:end_telemetry_span(?TRACER_ID, Meta),
|
otel_telemetry:end_telemetry_span(?TRACER_ID, Meta),
|
||||||
otel_ctx:clear().
|
otel_ctx:clear().
|
||||||
|
|
||||||
|
transform_status_to_code(Status) when is_binary(Status) ->
|
||||||
|
[CodeString | _Message] = string:split(Status, " "),
|
||||||
|
{Code, _Rest} = string:to_integer(CodeString),
|
||||||
|
Code;
|
||||||
|
transform_status_to_code(Status) ->
|
||||||
|
Status.
|
||||||
|
|
||||||
http_flavor(Req) ->
|
http_flavor(Req) ->
|
||||||
case maps:get(version, Req, undefined) of
|
case maps:get(version, Req, undefined) of
|
||||||
'HTTP/1.0' -> '1.0';
|
'HTTP/1.0' -> '1.0';
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
-include_lib("opentelemetry/include/otel_span.hrl").
|
-include_lib("opentelemetry/include/otel_span.hrl").
|
||||||
-include_lib("opentelemetry_api/include/otel_tracer.hrl").
|
-include_lib("opentelemetry_api/include/otel_tracer.hrl").
|
||||||
|
|
||||||
-define(assertListsMatch(List1, List2), ?assertEqual(lists:sort(List1), lists:sort(List2))).
|
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
[
|
[
|
||||||
successful_request,
|
successful_request,
|
||||||
|
@ -19,7 +17,8 @@ all() ->
|
||||||
client_timeout_request,
|
client_timeout_request,
|
||||||
idle_timeout_request,
|
idle_timeout_request,
|
||||||
chunk_timeout_request,
|
chunk_timeout_request,
|
||||||
bad_request
|
bad_request,
|
||||||
|
binary_status_code_request
|
||||||
].
|
].
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
@ -30,7 +29,9 @@ init_per_suite(Config) ->
|
||||||
{"/chunked", test_h, chunked},
|
{"/chunked", test_h, chunked},
|
||||||
{"/chunked_slow", test_h, chunked_slow},
|
{"/chunked_slow", test_h, chunked_slow},
|
||||||
{"/slow", test_h, slow},
|
{"/slow", test_h, slow},
|
||||||
{"/failure", test_h, failure}
|
{"/failure", test_h, failure},
|
||||||
|
{"/binary_status_code", test_h,
|
||||||
|
binary_status_code}
|
||||||
]}]),
|
]}]),
|
||||||
{ok, _} = cowboy:start_clear(http, [{port, 8080}], #{
|
{ok, _} = cowboy:start_clear(http, [{port, 8080}], #{
|
||||||
env => #{dispatch => Dispatch},
|
env => #{dispatch => Dispatch},
|
||||||
|
@ -84,7 +85,7 @@ successful_request(_Config) ->
|
||||||
'http.user_agent' => <<"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0">>,
|
'http.user_agent' => <<"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0">>,
|
||||||
'net.host.ip' => <<"127.0.0.1">>,
|
'net.host.ip' => <<"127.0.0.1">>,
|
||||||
'net.transport' => 'IP.TCP',
|
'net.transport' => 'IP.TCP',
|
||||||
'http.status' => 200,
|
'http.status_code' => 200,
|
||||||
'http.request_content_length' => 0,
|
'http.request_content_length' => 0,
|
||||||
'http.response_content_length' => 12},
|
'http.response_content_length' => 12},
|
||||||
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
||||||
|
@ -109,7 +110,7 @@ chunked_request(_Config) ->
|
||||||
'http.user_agent' => <<>>,
|
'http.user_agent' => <<>>,
|
||||||
'net.host.ip' => <<"127.0.0.1">>,
|
'net.host.ip' => <<"127.0.0.1">>,
|
||||||
'net.transport' => 'IP.TCP',
|
'net.transport' => 'IP.TCP',
|
||||||
'http.status' => 200,
|
'http.status_code' => 200,
|
||||||
'http.request_content_length' => 0,
|
'http.request_content_length' => 0,
|
||||||
'http.response_content_length' => 14},
|
'http.response_content_length' => 14},
|
||||||
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
||||||
|
@ -136,7 +137,7 @@ failed_request(_Config) ->
|
||||||
'http.user_agent' => <<>>,
|
'http.user_agent' => <<>>,
|
||||||
'net.host.ip' => <<"127.0.0.1">>,
|
'net.host.ip' => <<"127.0.0.1">>,
|
||||||
'net.transport' => 'IP.TCP',
|
'net.transport' => 'IP.TCP',
|
||||||
'http.status' => 500,
|
'http.status_code' => 500,
|
||||||
'http.request_content_length' => 0,
|
'http.request_content_length' => 0,
|
||||||
'http.response_content_length' => 0},
|
'http.response_content_length' => 0},
|
||||||
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
||||||
|
@ -222,7 +223,7 @@ chunk_timeout_request(_Config) ->
|
||||||
'http.user_agent' => <<>>,
|
'http.user_agent' => <<>>,
|
||||||
'net.host.ip' => <<"127.0.0.1">>,
|
'net.host.ip' => <<"127.0.0.1">>,
|
||||||
'net.transport' => 'IP.TCP',
|
'net.transport' => 'IP.TCP',
|
||||||
'http.status' => 200,
|
'http.status_code' => 200,
|
||||||
'http.request_content_length' => 0,
|
'http.request_content_length' => 0,
|
||||||
'http.response_content_length' => 0},
|
'http.response_content_length' => 0},
|
||||||
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
||||||
|
@ -248,9 +249,41 @@ bad_request(_Config) ->
|
||||||
?assertMatch(ExpectedEventAttrs, otel_attributes:map(EventAttributes)),
|
?assertMatch(ExpectedEventAttrs, otel_attributes:map(EventAttributes)),
|
||||||
?assertEqual(<<"HTTP Error">>, Name),
|
?assertEqual(<<"HTTP Error">>, Name),
|
||||||
ExpectedAttrs = #{
|
ExpectedAttrs = #{
|
||||||
'http.status' => 501,
|
'http.status_code' => 501,
|
||||||
'http.response_content_length' => 0},
|
'http.response_content_length' => 0},
|
||||||
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
||||||
after
|
after
|
||||||
1000 -> ct:fail(bad_request)
|
1000 -> ct:fail(bad_request)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
binary_status_code_request(_Config) ->
|
||||||
|
Headers = [
|
||||||
|
{"traceparent", "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"},
|
||||||
|
{"tracestate", "congo=t61rcWkgMzE"},
|
||||||
|
{"x-forwarded-for", "203.0.133.195, 70.41.3.18, 150.172.238.178"},
|
||||||
|
{"user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0"}],
|
||||||
|
{ok, {{_Version, 200, _ReasonPhrase}, _Headers, _Body}} =
|
||||||
|
httpc:request(get, {"http://localhost:8080/binary_status_code", Headers}, [], []),
|
||||||
|
receive
|
||||||
|
{span, #span{name=Name,attributes=Attributes,parent_span_id=ParentSpanId}} ->
|
||||||
|
?assertEqual(<<"HTTP GET">>, Name),
|
||||||
|
?assertEqual(13235353014750950193, ParentSpanId),
|
||||||
|
ExpectedAttrs = #{
|
||||||
|
'http.client_ip' => <<"203.0.133.195">>,
|
||||||
|
'http.flavor' => '1.1',
|
||||||
|
'http.host' => <<"localhost">>,
|
||||||
|
'http.host.port' => 8080,
|
||||||
|
'http.method' => <<"GET">>,
|
||||||
|
'http.scheme' => <<"http">>,
|
||||||
|
'http.target' => <<"/binary_status_code">>,
|
||||||
|
'http.user_agent' => <<"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0">>,
|
||||||
|
'net.host.ip' => <<"127.0.0.1">>,
|
||||||
|
'net.transport' => 'IP.TCP',
|
||||||
|
'http.status_code' => 200,
|
||||||
|
'http.request_content_length' => 0,
|
||||||
|
'http.response_content_length' => 12
|
||||||
|
},
|
||||||
|
?assertMatch(ExpectedAttrs, otel_attributes:map(Attributes))
|
||||||
|
after
|
||||||
|
1000 -> ct:fail(binary_status_code_request)
|
||||||
|
end.
|
||||||
|
|
|
@ -7,6 +7,8 @@ init(_, failure) ->
|
||||||
error(failure);
|
error(failure);
|
||||||
init(Req, success = Opts) ->
|
init(Req, success = Opts) ->
|
||||||
{ok, cowboy_req:reply(200, #{}, <<"Hello world!">>, Req), Opts};
|
{ok, cowboy_req:reply(200, #{}, <<"Hello world!">>, Req), Opts};
|
||||||
|
init(Req, binary_status_code = Opts) ->
|
||||||
|
{ok, cowboy_req:reply(<<"200 OK">>, #{}, <<"Hello world!">>, Req), Opts};
|
||||||
init(Req, slow = Opts) ->
|
init(Req, slow = Opts) ->
|
||||||
timer:sleep(200),
|
timer:sleep(200),
|
||||||
{ok, cowboy_req:reply(200, #{}, <<"I'm slow">>, Req), Opts};
|
{ok, cowboy_req:reply(200, #{}, <<"I'm slow">>, Req), Opts};
|
||||||
|
|
Loading…
Reference in New Issue