diff --git a/apps/capi/src/capi_otel_middleware.erl b/apps/capi/src/capi_otel_middleware.erl index 8d20e69..39bf860 100644 --- a/apps/capi/src/capi_otel_middleware.erl +++ b/apps/capi/src/capi_otel_middleware.erl @@ -1,6 +1,7 @@ -module(capi_otel_middleware). %% TODO Adopt https://github.com/cogini/opentelemetry_xray +-include_lib("opentelemetry_api/include/opentelemetry.hrl"). -behaviour(cowboy_middleware). @@ -55,35 +56,27 @@ extract(Ctx, Carrier, _CarrierKeysFun, CarrierGet, _Options) -> undefined -> Ctx; XRayTrace -> - case decode(string:trim(XRayTrace)) of + SpanCtx0 = otel_tracer:from_remote_span(0, 0, 0), + SpanCtx1 = lists:foldl(fun decode/2, SpanCtx0, string:split(string:trim(XRayTrace), ";", all)), + case SpanCtx1 of undefined -> Ctx; - SpanCtx -> - otel_tracer:set_current_span(Ctx, SpanCtx) + _ -> + otel_tracer:set_current_span(Ctx, SpanCtx1) end end. %% -decode( - %% NOTE Version is expected to be always single char "1" - <<"Root=", _Version:1/binary, "-", Timestamp:8/binary, "-", RootId:24/binary, ";Parent=", ParentId:16/binary, - ";Sampled=", Sampled:1/binary, _/binary>> -) -> - try - TraceId = binary_to_integer(<>, 16), - SpanId = binary_to_integer(ParentId, 16), - TraceFlags = - case Sampled of - <<"1">> -> 1; - <<"0">> -> 0; - _ -> error(badarg) - end, - otel_tracer:from_remote_span(TraceId, SpanId, TraceFlags) - catch - %% to integer from base 16 string failed - error:badarg -> - undefined - end; -decode(_) -> - undefined. +decode(<<"Root=1-", Timestamp:8/binary, "-", RootId:24/binary>>, SpanCtx) -> + TraceId = binary_to_integer(<>, 16), + SpanCtx#span_ctx{trace_id = TraceId, hex_trace_id = otel_utils:encode_hex(<>)}; +decode(<<"Parent=", ParentId:16/binary>>, SpanCtx) -> + SpanId = binary_to_integer(ParentId, 16), + SpanCtx#span_ctx{span_id = SpanId, hex_span_id = otel_utils:encode_hex(<>)}; +decode(<<"Sampled=0">>, SpanCtx) -> + SpanCtx#span_ctx{trace_flags = 0}; +decode(<<"Sampled=1">>, SpanCtx) -> + SpanCtx#span_ctx{trace_flags = 1}; +decode(_Value, SpanCtx) -> + SpanCtx. diff --git a/apps/capi/test/capi_self_tests_SUITE.erl b/apps/capi/test/capi_self_tests_SUITE.erl index c13cb7f..c7baa82 100644 --- a/apps/capi/test/capi_self_tests_SUITE.erl +++ b/apps/capi/test/capi_self_tests_SUITE.erl @@ -43,7 +43,8 @@ init([]) -> all() -> [ {group, stream_handler_tests}, - {group, validation_tests} + {group, validation_tests}, + {group, middleware_tests} ]. -spec groups() -> [{group_name(), list(), [test_case_name()]}]. @@ -96,41 +97,10 @@ end_per_group(_Group, C) -> ok. -spec init_per_testcase(test_case_name(), config()) -> config(). -init_per_testcase(amzn_xray_header_for_otel_ctx, C) -> - AmznXrayTraceId = <<"1-67891233-abcdef012345678912345678">>, - ExpectedTraceId = "67891233abcdef012345678912345678", - meck:expect(capi_client_lib, make_request, fun(Context, Params) -> - {Url, PreparedParams, Opts} = meck:passthrough([Context, Params]), - UpdFun = fun(Headers) -> - Headers#{ - <<"X-Amzn-Trace-Id">> => - <<"Root=", AmznXrayTraceId/binary, ";Parent=53995c3f42cd8ad8;Sampled=1">> - } - end, - {Url, maps:update_with(header, UpdFun, PreparedParams), Opts} - end), - TestProcess = self(), - meck:expect(otel_ctx, get_current, fun() -> - OtelCtx = meck:passthrough([]), - SpanCtx = otel_tracer:current_span_ctx(OtelCtx), - TestProcess ! {otel_span_info, otel_span:hex_span_ctx(SpanCtx)}, - OtelCtx - end), - [ - {amzn_xray_trace_id, AmznXrayTraceId}, - {expected_trace_id, ExpectedTraceId}, - {test_sup, capi_ct_helper:start_mocked_service_sup(?MODULE)} - | C - ]; init_per_testcase(_Name, C) -> [{test_sup, capi_ct_helper:start_mocked_service_sup(?MODULE)} | C]. -spec end_per_testcase(test_case_name(), config()) -> _. -end_per_testcase(amzn_xray_header_for_otel_ctx, C) -> - _ = capi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)), - meck:unload(otel_ctx), - meck:unload(capi_client_lib), - ok; end_per_testcase(_Name, C) -> _ = capi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)), ok. @@ -184,9 +154,56 @@ query_param_validation(Config) -> -spec amzn_xray_header_for_otel_ctx(config()) -> _. amzn_xray_header_for_otel_ctx(Config) -> - _ = oops_body_test(Config), - ExpectedTraceId = ?config(expected_trace_id, Config), - ?assertMatch(#{otel_trace_id := ExpectedTraceId}, await_latest_otel_span_info(1_000, #{})). + _ = capi_ct_helper:mock_services([{invoicing, fun('Get', _) -> {ok, "spanish inquisition"} end}], Config), + Fixtures = [ + { + "Root=1-67891233-abcdef012345678912345678;Parent=53995c3f42cd8ad8;Sampled=1", + "67891233abcdef012345678912345678" + }, + + { + "Sampled=1;Root=1-67891233-abcdef012345678912345678;Parent=53995c3f42cd8ad8", + "67891233abcdef012345678912345678" + }, + { + "Root=1-67891233-abcdef012345678912345678;Sampled=1", + "67891233abcdef012345678912345678" + }, + { + "Root=1-67891233-abcdef012345678912345678;whatever", + "67891233abcdef012345678912345678" + }, + { + "Root=1-67891233-abcdef012345678912345678", + "67891233abcdef012345678912345678" + } + ], + Fun = fun() -> + capi_client_invoices:get_invoice_events(?config(context, Config), ?STRING, 1) + end, + [assert_trace_based_on_header(Header, TraceId, Fun) || {Header, TraceId} <- Fixtures]. + +assert_trace_based_on_header(AmznTraceIdHeader0, ExpectedTraceId0, Fun) -> + AmznTraceIdHeader = list_to_binary(AmznTraceIdHeader0), + ExpectedTraceId = list_to_binary(ExpectedTraceId0), + meck:expect(capi_client_lib, make_request, fun(Context, Params) -> + {Url, PreparedParams, Opts} = meck:passthrough([Context, Params]), + UpdFun = fun(Headers) -> + Headers#{<<"X-Amzn-Trace-Id">> => AmznTraceIdHeader} + end, + {Url, maps:update_with(header, UpdFun, PreparedParams), Opts} + end), + TestProcess = self(), + meck:expect(otel_ctx, get_current, fun() -> + OtelCtx = meck:passthrough([]), + SpanCtx = otel_tracer:current_span_ctx(OtelCtx), + TestProcess ! {otel_span_info, otel_span:hex_span_ctx(SpanCtx)}, + OtelCtx + end), + _ = Fun(), + meck:unload(otel_ctx), + meck:unload(capi_client_lib), + ?assertMatch(#{otel_trace_id := ExpectedTraceId}, await_latest_otel_span_info(100, #{})). await_latest_otel_span_info(Timeout, LastValue) -> receive diff --git a/rebar.config b/rebar.config index fef8388..ff543dc 100644 --- a/rebar.config +++ b/rebar.config @@ -56,9 +56,9 @@ {swag_client, {git, "https://github.com/valitydev/swag-payments", {branch, "release/erlang/client/v3"}}}, {fraudbusters_proto, {git, "https://github.com/valitydev/fraudbusters-proto.git", {branch, "master"}}}, %% OpenTelemetry deps - {opentelemetry_api, "1.4.0"}, - {opentelemetry, "1.5.0"}, - {opentelemetry_exporter, "1.8.0"} + {opentelemetry_api, "1.5.0"}, + {opentelemetry, "1.7.0"}, + {opentelemetry_exporter, "1.10.0"} ]}. %% XRef checks diff --git a/rebar.lock b/rebar.lock index 1481352..b0d3e6f 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,6 +1,6 @@ {"1.2.0", [{<<"accept">>,{pkg,<<"accept">>,<<"0.3.7">>},2}, - {<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},2}, + {<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.1">>},2}, {<<"bender_client">>, {git,"https://github.com/valitydev/bender-client-erlang.git", {ref,"2fceffde5deac85d3bd3f071187041d84c8c3af4"}}, @@ -92,10 +92,10 @@ {git,"https://github.com/valitydev/msgpack-proto.git", {ref,"7e447496aa5df4a5f1ace7ef2e3c31248b2a3ed0"}}, 1}, - {<<"opentelemetry">>,{pkg,<<"opentelemetry">>,<<"1.5.0">>},0}, - {<<"opentelemetry_api">>,{pkg,<<"opentelemetry_api">>,<<"1.4.0">>},0}, + {<<"opentelemetry">>,{pkg,<<"opentelemetry">>,<<"1.7.0">>},0}, + {<<"opentelemetry_api">>,{pkg,<<"opentelemetry_api">>,<<"1.5.0">>},0}, {<<"opentelemetry_exporter">>, - {pkg,<<"opentelemetry_exporter">>,<<"1.8.0">>}, + {pkg,<<"opentelemetry_exporter">>,<<"1.10.0">>}, 0}, {<<"org_management_proto">>, {git,"https://github.com/valitydev/org-management-proto", @@ -137,7 +137,7 @@ {ref,"3a60e5dc5bbd709495024f26e100b041c3547fd9"}}, 1}, {<<"tls_certificate_check">>, - {pkg,<<"tls_certificate_check">>,<<"1.29.0">>}, + {pkg,<<"tls_certificate_check">>,<<"1.31.0">>}, 1}, {<<"token_keeper_client">>, {git,"https://github.com/valitydev/token-keeper-client.git", @@ -159,7 +159,7 @@ [ {pkg_hash,[ {<<"accept">>, <<"CD6E34A2D7E28CA38B2D3CB233734CA0C221EFBC1F171F91FEC5F162CC2D18DA">>}, - {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, + {<<"acceptor_pool">>, <<"D88C2E8A0BE9216CF513FBCD3E5A4BEB36BEE3FF4168E85D6152C6F899359CDB">>}, {<<"cache">>, <<"B23A5FE7095445A88412A6E614C933377E0137B44FFED77C9B3FEF1A731A20B2">>}, {<<"certifi">>, <<"DBAB8E5E155A0763EEA978C913CA280A6B544BFA115633FA20249C3D396D9493">>}, {<<"chatterbox">>, <<"5CAC4D15DD7AD61FC3C4415CE4826FC563D4643DEE897A558EC4EA0B1C835C9C">>}, @@ -175,9 +175,9 @@ {<<"jsx">>, <<"D12516BAA0BB23A59BB35DCCAF02A1BD08243FCBB9EFE24F2D9D056CCFF71268">>}, {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, {<<"mimerl">>, <<"3882A5CA67FBBE7117BA8947F27643557ADEC38FA2307490C4C4207624CB213B">>}, - {<<"opentelemetry">>, <<"7DDA6551EDFC3050EA4B0B40C0D2570423D6372B97E9C60793263EF62C53C3C2">>}, - {<<"opentelemetry_api">>, <<"63CA1742F92F00059298F478048DFB826F4B20D49534493D6919A0DB39B6DB04">>}, - {<<"opentelemetry_exporter">>, <<"5D546123230771EF4174E37BEDFD77E3374913304CD6EA3CA82A2ADD49CD5D56">>}, + {<<"opentelemetry">>, <<"20D0F12D3D1C398D3670FD44FD1A7C495DD748AB3E5B692A7906662E2FB1A38A">>}, + {<<"opentelemetry_api">>, <<"1A676F3E3340CAB81C763E939A42E11A70C22863F645AA06AAFEFC689B5550CF">>}, + {<<"opentelemetry_exporter">>, <<"972E142392DBFA679EC959914664ADEFEA38399E4F56CEBA5C473E1CABDBAD79">>}, {<<"parse_trans">>, <<"6E6AA8167CB44CC8F39441D05193BE6E6F4E7C2946CB2759F015F8C56B76E5FF">>}, {<<"prometheus">>, <<"B95F8DE8530F541BD95951E18E355A840003672E5EDA4788C5FA6183406BA29A">>}, {<<"prometheus_cowboy">>, <<"D9D5B300516A61ED5AE31391F8EEEEB202230081D32A1813F2D78772B6F274E1">>}, @@ -185,11 +185,11 @@ {<<"quantile_estimator">>, <<"EF50A361F11B5F26B5F16D0696E46A9E4661756492C981F7B2229EF42FF1CD15">>}, {<<"ranch">>, <<"8C7A100A139FD57F17327B6413E4167AC559FBC04CA7448E9BE9057311597A1D">>}, {<<"ssl_verify_fun">>, <<"354C321CF377240C7B8716899E182CE4890C5938111A1296ADD3EC74CF1715DF">>}, - {<<"tls_certificate_check">>, <<"4473005EB0BBDAD215D7083A230E2E076F538D9EA472C8009FD22006A4CFC5F6">>}, + {<<"tls_certificate_check">>, <<"9A910B54D8CB96CC810CABF4C0129F21360F82022B20180849F1442A25CCBB04">>}, {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}]}, {pkg_hash_ext,[ {<<"accept">>, <<"CA69388943F5DAD2E7232A5478F16086E3C872F48E32B88B378E1885A59F5649">>}, - {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, + {<<"acceptor_pool">>, <<"F172F3D74513E8EDD445C257D596FC84DBDD56D2C6FA287434269648AE5A421E">>}, {<<"cache">>, <<"44516CE6FA03594D3A2AF025DD3A87BFE711000EB730219E1DDEFC816E0AA2F4">>}, {<<"certifi">>, <<"524C97B4991B3849DD5C17A631223896272C6B0AF446778BA4675A1DFF53BB7E">>}, {<<"chatterbox">>, <<"4F75B91451338BC0DA5F52F3480FA6EF6E3A2AEECFC33686D6B3D0A0948F31AA">>}, @@ -205,9 +205,9 @@ {<<"jsx">>, <<"0C5CC8FDC11B53CC25CF65AC6705AD39E54ECC56D1C22E4ADB8F5A53FB9427F3">>}, {<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>}, {<<"mimerl">>, <<"13AF15F9F68C65884ECCA3A3891D50A7B57D82152792F3E19D88650AA126B144">>}, - {<<"opentelemetry">>, <<"CDF4F51D17B592FC592B9A75F86A6F808C23044BA7CF7B9534DEBBCC5C23B0EE">>}, - {<<"opentelemetry_api">>, <<"3DFBBFAA2C2ED3121C5C483162836C4F9027DEF469C41578AF5EF32589FCFC58">>}, - {<<"opentelemetry_exporter">>, <<"A1F9F271F8D3B02B81462A6BFEF7075FD8457FDB06ADFF5D2537DF5E2264D9AF">>}, + {<<"opentelemetry">>, <<"A9173B058C4549BF824CBC2F1D2FA2ADC5CDEDC22AA3F0F826951187BBD53131">>}, + {<<"opentelemetry_api">>, <<"F53EC8A1337AE4A487D43AC89DA4BD3A3C99DDF576655D071DEED8B56A2D5DDA">>}, + {<<"opentelemetry_exporter">>, <<"33A116ED7304CB91783F779DEC02478F887C87988077BFD72840F760B8D4B952">>}, {<<"parse_trans">>, <<"620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A">>}, {<<"prometheus">>, <<"719862351AABF4DF7079B05DC085D2BBCBE3AC0AC3009E956671B1D5AB88247D">>}, {<<"prometheus_cowboy">>, <<"5F71C039DEB9E9FF9DD6366BC74C907A463872B85286E619EFF0BDA15111695A">>}, @@ -215,6 +215,6 @@ {<<"quantile_estimator">>, <<"282A8A323CA2A845C9E6F787D166348F776C1D4A41EDE63046D72D422E3DA946">>}, {<<"ranch">>, <<"49FBCFD3682FAB1F5D109351B61257676DA1A2FDBE295904176D5E521A2DDFE5">>}, {<<"ssl_verify_fun">>, <<"FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8">>}, - {<<"tls_certificate_check">>, <<"5B0D0E5CB0F928BC4F210DF667304ED91C5BFF2A391CE6BDEDFBFE70A8F096C5">>}, + {<<"tls_certificate_check">>, <<"9D2B41B128D5507BD8AD93E1A998E06D0AB2F9A772AF343F4C00BF76C6BE1532">>}, {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}]} ].