111 lines
3.9 KiB
Erlang
111 lines
3.9 KiB
Erlang
|
-module(opentelemetry_instrumentation_http).
|
||
|
|
||
|
-ifdef(TEST).
|
||
|
-include_lib("eunit/include/eunit.hrl").
|
||
|
-endif.
|
||
|
|
||
|
-export([
|
||
|
extract_headers_attributes/3,
|
||
|
normalize_header_name/1
|
||
|
]).
|
||
|
|
||
|
-spec normalize_header_name(string() | binary()) -> Result when
|
||
|
Result :: binary() | {error, binary(), RestData} | {incomplete, binary(), binary()},
|
||
|
RestData :: unicode:latin1_chardata() | unicode:chardata() | unicode:external_chardata().
|
||
|
normalize_header_name(Header) when is_binary(Header) ->
|
||
|
normalize_header_name(binary_to_list(Header));
|
||
|
normalize_header_name(Header) when is_list(Header) ->
|
||
|
unicode:characters_to_binary(string:replace(string:to_lower(Header), "-", "_", all)).
|
||
|
|
||
|
-spec extract_headers_attributes(
|
||
|
request | response,
|
||
|
Headers ::
|
||
|
#{binary() | string() => binary() | [binary()]}
|
||
|
| [{binary() | string(), binary() | [binary()]}],
|
||
|
HeadersToExtract :: [binary()]
|
||
|
) -> #{atom() => [binary()]}.
|
||
|
extract_headers_attributes(_Context, _Headers, []) ->
|
||
|
#{};
|
||
|
extract_headers_attributes(Context, Headers, HeadersToExtract) when is_list(Headers) ->
|
||
|
lists:foldr(
|
||
|
fun({HeaderName, HeaderValue}, Extracted) ->
|
||
|
extract_if_match(Context, HeaderName, HeaderValue, Extracted, HeadersToExtract)
|
||
|
end,
|
||
|
#{},
|
||
|
Headers
|
||
|
);
|
||
|
extract_headers_attributes(Context, Headers, HeadersToExtract) when is_map(Headers) ->
|
||
|
maps:fold(
|
||
|
fun(HeaderName, HeaderValue, Extracted) ->
|
||
|
extract_if_match(Context, HeaderName, HeaderValue, Extracted, HeadersToExtract)
|
||
|
end,
|
||
|
#{},
|
||
|
Headers
|
||
|
).
|
||
|
|
||
|
extract_if_match(Context, HeaderName, HeaderValue, Extracted, HeadersToExtract) ->
|
||
|
NormalizedHeaderName = normalize_header_name(HeaderName),
|
||
|
case lists:member(NormalizedHeaderName, HeadersToExtract) of
|
||
|
false -> Extracted;
|
||
|
true -> set_header_attribute(Context, NormalizedHeaderName, HeaderValue, Extracted)
|
||
|
end.
|
||
|
|
||
|
set_header_attribute(Context, Header, Value, HeadersAttributes) when is_list(Value) ->
|
||
|
maps:put(attribute_name(Context, Header), Value, HeadersAttributes);
|
||
|
set_header_attribute(Context, Header, Value, HeadersAttributes) ->
|
||
|
maps:update_with(
|
||
|
attribute_name(Context, Header), fun(V) -> [Value | V] end, [Value], HeadersAttributes
|
||
|
).
|
||
|
|
||
|
attribute_name(request, HeaderName) ->
|
||
|
binary_to_atom(<<"http.request.header.", HeaderName/binary>>);
|
||
|
attribute_name(response, HeaderName) ->
|
||
|
binary_to_atom(<<"http.response.header.", HeaderName/binary>>).
|
||
|
|
||
|
-ifdef(TEST).
|
||
|
|
||
|
normalize_header_name_test_() ->
|
||
|
[
|
||
|
?_assertEqual(normalize_header_name(<<"Content-Type">>), <<"content_type">>),
|
||
|
?_assertEqual(normalize_header_name("Some-Header-NAME"), <<"some_header_name">>)
|
||
|
].
|
||
|
|
||
|
extract_headers_attributes_test_() ->
|
||
|
[
|
||
|
?_assertEqual(extract_headers_attributes(request, [], []), #{}),
|
||
|
?_assertEqual(extract_headers_attributes(response, #{}, []), #{}),
|
||
|
?_assertEqual(
|
||
|
extract_headers_attributes(
|
||
|
request,
|
||
|
#{
|
||
|
<<"Foo">> => <<"1">>,
|
||
|
"Bar-Baz" => [<<"2">>, <<"3">>],
|
||
|
"To-Not-Extract" => <<"4">>
|
||
|
},
|
||
|
[<<"foo">>, <<"bar_baz">>]
|
||
|
),
|
||
|
#{
|
||
|
'http.request.header.foo' => [<<"1">>],
|
||
|
'http.request.header.bar_baz' => [<<"2">>, <<"3">>]
|
||
|
}
|
||
|
),
|
||
|
?_assertEqual(
|
||
|
extract_headers_attributes(
|
||
|
response,
|
||
|
[
|
||
|
{<<"Foo">>, <<"1">>},
|
||
|
{"Bar-Baz", <<"2">>},
|
||
|
{"To-Not-Extract", <<"3">>},
|
||
|
{<<"foo">>, <<"4">>}
|
||
|
],
|
||
|
[<<"foo">>, <<"bar_baz">>]
|
||
|
),
|
||
|
#{
|
||
|
'http.response.header.foo' => [<<"1">>, <<"4">>],
|
||
|
'http.response.header.bar_baz' => [<<"2">>]
|
||
|
}
|
||
|
)
|
||
|
].
|
||
|
|
||
|
-endif.
|