From 59664d87c8a6887690c54854fc49e7474036b3dd Mon Sep 17 00:00:00 2001 From: Ygor Castor Date: Thu, 21 Jul 2022 19:40:47 +0200 Subject: [PATCH] Implemented AWS X-Ray TraceId Generator (#95) --- .github/labeler.yml | 3 ++ .github/workflows/erlang.yml | 31 ++++++++++++++++ rebar.lock | 1 + utilities/opentelemetry_aws_xray/README.md | 36 +++++++++++++++++++ utilities/opentelemetry_aws_xray/rebar.config | 15 ++++++++ utilities/opentelemetry_aws_xray/rebar.lock | 11 ++++++ .../src/opentelemetry_aws_xray.app.src | 15 ++++++++ .../src/otel_aws_xray_id_generator.erl | 29 +++++++++++++++ .../test/opentelemetry_aws_xray_SUITE.erl | 33 +++++++++++++++++ 9 files changed, 174 insertions(+) create mode 100644 rebar.lock create mode 100644 utilities/opentelemetry_aws_xray/README.md create mode 100644 utilities/opentelemetry_aws_xray/rebar.config create mode 100644 utilities/opentelemetry_aws_xray/rebar.lock create mode 100644 utilities/opentelemetry_aws_xray/src/opentelemetry_aws_xray.app.src create mode 100644 utilities/opentelemetry_aws_xray/src/otel_aws_xray_id_generator.erl create mode 100644 utilities/opentelemetry_aws_xray/test/opentelemetry_aws_xray_SUITE.erl diff --git a/.github/labeler.yml b/.github/labeler.yml index 66c232a..c0b1d1e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -67,3 +67,6 @@ opentelemetry_redix: opentelemetry_telemetry: - utilities/opentelemetry_telemetry/**/* + +opentelemetry_xray: + - utilities/opentelemetry_xray/**/* diff --git a/.github/workflows/erlang.yml b/.github/workflows/erlang.yml index 2686795..39eaf24 100644 --- a/.github/workflows/erlang.yml +++ b/.github/workflows/erlang.yml @@ -82,3 +82,34 @@ jobs: run: rebar3 get-deps - name: Test run: rebar3 ct + + opentelemetry-aws-xray: + needs: [test-matrix] + if: (contains(github.event.pull_request.labels.*.name, 'erlang') && contains(github.event.pull_request.labels.*.name, 'opentelemetry_aws_xray')) + env: + app: 'opentelemetry_aws_xray' + defaults: + run: + working-directory: utilities/${{ env.app }} + runs-on: ubuntu-18.04 + name: Opentelemetry AWS X-Ray test on OTP ${{ matrix.otp_version }} with Rebar3 ${{ matrix.rebar3_version }} + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.test-matrix.outputs.matrix) }} + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ matrix.otp_version }} + rebar3-version: ${{ matrix.rebar3_version }} + - name: Cache + uses: actions/cache@v2 + with: + path: | + ~/_build + key: ${{ runner.os }}-build-${{ matrix.otp_version }}-${{ matrix.rebar3_version }}-v3-${{ hashFiles('**/rebar.lock') }} + - name: Fetch deps + if: steps.deps-cache.outputs.cache-hit != 'true' + run: rebar3 get-deps + - name: Test + run: rebar3 ct diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/utilities/opentelemetry_aws_xray/README.md b/utilities/opentelemetry_aws_xray/README.md new file mode 100644 index 0000000..892c3e4 --- /dev/null +++ b/utilities/opentelemetry_aws_xray/README.md @@ -0,0 +1,36 @@ +# OpentelemetryXray + +Implements an Id Generator compatible with [AWS X-Ray TraceId](https://docs.aws.amazon.com/xray/latest/devguide/xray-api-sendingdata.html#xray-api-traceids) + +## Installation + +The package can be installed by adding `opentelemetry_xray` to your list of +dependencies in `mix.exs` for elixir and `rebar.config` for erlang : + +```erlang +{deps, [opentelemetry_xray]}. +``` + +```elixir +def deps do + [ + {:opentelemetry_xray, "~> 0.1"} + ] +end +``` + +## Usage + +Configure the OpenTelemetry to use the X-Ray Id Generator + +```elixir + config :opentelemetry, + id_generator: :otel_aws_xray_id_generator +``` + +```erlang +[ + {opentelemetry, + [{id_generator, otel_aws_xray_id_generator}]} +]. +``` diff --git a/utilities/opentelemetry_aws_xray/rebar.config b/utilities/opentelemetry_aws_xray/rebar.config new file mode 100644 index 0000000..d899733 --- /dev/null +++ b/utilities/opentelemetry_aws_xray/rebar.config @@ -0,0 +1,15 @@ +{erl_opts, [debug_info]}. + +{deps, [{opentelemetry, "~> 1.0"}]}. + +{project_plugins, [{rebar_covertool, "1.1.0"}]}. + +{profiles, [{test, [{erl_opts, [nowarn_export_all]}, + {deps, [{opentelemetry, "~> 1.0"}]}]}]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. +{cover_export_enabled, true}. +{covertool, [{coverdata_files, ["ct.coverdata"]}]}. + +{ct_opts, [{ct_hooks, [cth_surefire]}]}. diff --git a/utilities/opentelemetry_aws_xray/rebar.lock b/utilities/opentelemetry_aws_xray/rebar.lock new file mode 100644 index 0000000..d0cf5a7 --- /dev/null +++ b/utilities/opentelemetry_aws_xray/rebar.lock @@ -0,0 +1,11 @@ +{"1.2.0", +[{<<"opentelemetry">>,{pkg,<<"opentelemetry">>,<<"1.0.5">>},0}, + {<<"opentelemetry_api">>,{pkg,<<"opentelemetry_api">>,<<"1.0.3">>},1}]}. +[ +{pkg_hash,[ + {<<"opentelemetry">>, <<"F0CD36AC8B30B68E8D70CEC5BB88801ED7F3FE79AAC67597054ED5490542E810">>}, + {<<"opentelemetry_api">>, <<"77F9644C42340CD8B18C728CDE4822ED55AE136F0D07761B78E8C54DA46AF93A">>}]}, +{pkg_hash_ext,[ + {<<"opentelemetry">>, <<"3B17F8933A58E1246F42A0C215840FD8218AEBBCABDB0AAC62B0C766FE85542E">>}, + {<<"opentelemetry_api">>, <<"4293E06BD369BC004E6FAD5EDBB56456D891F14BD3F9F1772B18F1923E0678EA">>}]} +]. diff --git a/utilities/opentelemetry_aws_xray/src/opentelemetry_aws_xray.app.src b/utilities/opentelemetry_aws_xray/src/opentelemetry_aws_xray.app.src new file mode 100644 index 0000000..82a78ee --- /dev/null +++ b/utilities/opentelemetry_aws_xray/src/opentelemetry_aws_xray.app.src @@ -0,0 +1,15 @@ +{application, opentelemetry_aws_xray, [ + {description, "OpenTelemetry AWS X-Ray TraceId Generator"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, [ + kernel, + stdlib, + opentelemetry + ]}, + {env, []}, + {modules, []}, + + {licenses, ["Apache-2.0"]}, + {links, [{"GitHub", "https://github.com/open-telemetry/opentelemetry-erlang-contrib"}]} +]}. diff --git a/utilities/opentelemetry_aws_xray/src/otel_aws_xray_id_generator.erl b/utilities/opentelemetry_aws_xray/src/otel_aws_xray_id_generator.erl new file mode 100644 index 0000000..80f7f03 --- /dev/null +++ b/utilities/opentelemetry_aws_xray/src/otel_aws_xray_id_generator.erl @@ -0,0 +1,29 @@ +-module(otel_aws_xray_id_generator). + +-behavior(otel_id_generator). + +-export([ + generate_trace_id/0, + generate_span_id/0 +]). + +%% Generates a trace_id compatible with AWS X-Ray +%% +%% See More: https://docs.aws.amazon.com/xray/latest/devguide/xray-api-sendingdata.html#xray-api-traceids +%% +%% The first 32 bits is the timestamp, the remaining 96 bits are random +-spec generate_trace_id() -> opentelemetry:trace_id(). +generate_trace_id() -> + Random = rand:uniform(2 bsl (95 - 1)), + TimeStamp = os:system_time(second), + + HiBytes = binary:encode_unsigned(TimeStamp), + LoBytes = binary:encode_unsigned(Random), + + TraceIdBytes = <>, + + binary:decode_unsigned(TraceIdBytes). + +-spec generate_span_id() -> opentelemetry:span_id(). +generate_span_id() -> + rand:uniform(2 bsl 63 - 1). diff --git a/utilities/opentelemetry_aws_xray/test/opentelemetry_aws_xray_SUITE.erl b/utilities/opentelemetry_aws_xray/test/opentelemetry_aws_xray_SUITE.erl new file mode 100644 index 0000000..ea306fd --- /dev/null +++ b/utilities/opentelemetry_aws_xray/test/opentelemetry_aws_xray_SUITE.erl @@ -0,0 +1,33 @@ +-module(opentelemetry_aws_xray_SUITE). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("stdlib/include/assert.hrl"). + +all() -> + [generate_trace_id_should_be_xray_compatible, generate_span_id_should_be_default]. + +generate_trace_id_should_be_xray_compatible(_Config) -> + MaxAge = 60 * 60 * 24 * 28, + MaxSkew = 60 * 5, + + TraceId = otel_aws_xray_id_generator:generate_trace_id(), + EncodedTraceId = binary:encode_unsigned(TraceId), + + EpochNow = os:system_time(second), + + % The Trace id should have 128 bits (16 bytes) + ?assertEqual(16, byte_size(EncodedTraceId)), + + % The first 4 bytes is the epoch + Epoch = binary:decode_unsigned(binary:part(EncodedTraceId, 0, 4)), + Delta = EpochNow - Epoch, + + ?assertEqual(false, (Delta > MaxAge) or (Delta < -MaxSkew)). + +generate_span_id_should_be_default(_Config) -> + SpanId = otel_aws_xray_id_generator:generate_span_id(), + + ?assertEqual(8, byte_size(binary:encode_unsigned(SpanId))).