├── VERSION ├── rebar.lock ├── test ├── test_helper.exs ├── otel_metrics_SUITE.erl ├── open_telemetry_test.exs └── opentelemetry_api_SUITE.erl ├── include ├── ot_sampler.hrl ├── tracer.hrl ├── meter.hrl └── opentelemetry.hrl ├── README.md ├── .gitignore ├── src ├── opentelemetry_api.app.src ├── ot_observer.erl ├── ot_meter_noop.erl ├── ot_sum_observer.erl ├── ot_value_observer.erl ├── ot_updown_sum_observer.erl ├── ot_counter.erl ├── ot_updown_counter.erl ├── ot_value_recorder.erl ├── ot_instrument.erl ├── ot_propagation.erl ├── ot_meter_provider.erl ├── ot_baggage.erl ├── ot_tracer_noop.erl ├── ot_tracer_provider.erl ├── ot_ctx.erl ├── ot_http_status.erl ├── ot_tracer.erl ├── ot_meter.erl ├── ot_span.erl └── opentelemetry.erl ├── CODEOWNERS ├── rebar.config ├── lib ├── open_telemetry │ ├── sum_observer.ex │ ├── value_observer.ex │ ├── updown_sum_observer.ex │ ├── counter.ex │ ├── updown_counter.ex │ ├── value_recorder.ex │ ├── meter.ex │ ├── tracer.ex │ └── span.ex └── open_telemetry.ex ├── .github └── workflows │ └── main.yml ├── mix.lock ├── mix.exs ├── CONTRIBUTING.md └── LICENSE /VERSION: -------------------------------------------------------------------------------- 1 | 0.4.0 2 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /include/ot_sampler.hrl: -------------------------------------------------------------------------------- 1 | -define(NOT_RECORD, not_record). 2 | -define(RECORD, record). 3 | -define(RECORD_AND_PROPAGATE, record_and_propagate). 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED AND MOVED TO [OpenTelemetry Erlang][https://github.com/open-telemetry/opentelemetry-erlang] 2 | 3 | # Erlang/Elixir OpenTelemetry API 4 | 5 | This application now lives along side the SDK and exporter applications in a 6 | single repo, the [OpenTelemetry Erlang 7 | repo][https://github.com/open-telemetry/opentelemetry-erlang]. 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | edoc 2 | .rebar3 3 | _* 4 | .eunit 5 | *.o 6 | *.beam 7 | *.plt 8 | *.swp 9 | *.swo 10 | .erlang.cookie 11 | ebin 12 | log 13 | erl_crash.dump 14 | .rebar 15 | logs 16 | _build 17 | .idea 18 | *.iml 19 | rebar3.crashdump 20 | *~ 21 | 22 | /_build 23 | /cover 24 | /deps 25 | /doc 26 | /.fetch 27 | erl_crash.dump 28 | *.ez 29 | *.beam 30 | /config/*.secret.exs 31 | .elixir_ls/ 32 | 33 | -------------------------------------------------------------------------------- /src/opentelemetry_api.app.src: -------------------------------------------------------------------------------- 1 | {application, opentelemetry_api, 2 | [{description, "OpenTelemetry API"}, 3 | {vsn, {file, "VERSION"}}, 4 | {registered, []}, 5 | {applications, 6 | [kernel, 7 | stdlib 8 | ]}, 9 | {env,[]}, 10 | {modules, []}, 11 | 12 | {licenses, ["Apache-2.0"]}, 13 | {links, [{"GitHub", "https://github.com/open-telemetry/opentelemetry-erlang-api"}]} 14 | ]}. 15 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | ##################################################### 2 | # 3 | # List of approvers for OpenTelemetry Erlang API repository 4 | # 5 | ##################################################### 6 | # 7 | # Learn about membership in OpenTelemetry community: 8 | # https://github.com/open-telemetry/community/blob/master/community-membership.md 9 | # 10 | # 11 | # Learn about CODEOWNERS file format: 12 | # https://help.github.com/en/articles/about-code-owners 13 | # 14 | 15 | * @open-telemetry/erlang-approvers 16 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, []}. 3 | 4 | {profiles, 5 | [{docs, [{deps, [edown]}, 6 | {edoc_opts, 7 | [{doclet, edown_doclet}, 8 | {preprocess, true}, 9 | {dir, "edoc"}, 10 | {subpackages, true}]}]}, 11 | {test, [{erl_opts, [nowarn_export_all]}, 12 | {deps, []}, 13 | {ct_opts, [{ct_hooks, [cth_surefire]}]}]}]}. 14 | 15 | {xref_checks, [undefined_function_calls, undefined_functions, 16 | deprecated_function_calls, deprecated_functions]}. 17 | {xref_ignores, []}. 18 | 19 | {project_plugins, [covertool]}. 20 | {cover_enabled, true}. 21 | {cover_export_enabled, true}. 22 | {covertool, [{coverdata_files, ["ct.coverdata"]}]}. 23 | -------------------------------------------------------------------------------- /lib/open_telemetry/sum_observer.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.SumObserver do 2 | @moduledoc """ 3 | 4 | require OpenTelemetry.SumObserver 5 | 6 | OpenTelemetry.SumObserver.set_callback("some.counter", &OpenTelemetry.SumObserver.observe(&1, 33, [])) 7 | """ 8 | 9 | defmacro new(name, opts \\ %{}) do 10 | quote do 11 | :ot_sum_observer.new(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(opts)) 12 | end 13 | end 14 | 15 | defmacro set_callback(observer, callback) do 16 | quote do 17 | :ot_meter.set_observer_callback( 18 | :opentelemetry.get_meter(__MODULE__), 19 | unquote(observer), 20 | unquote(callback) 21 | ) 22 | end 23 | end 24 | 25 | defdelegate definition(name, opts), to: :ot_sum_observer 26 | defdelegate observe(observer_result, number, label_set), to: :ot_sum_observer 27 | end 28 | -------------------------------------------------------------------------------- /lib/open_telemetry/value_observer.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.ValueObserver do 2 | @moduledoc """ 3 | 4 | require OpenTelemetry.ValueObserver 5 | 6 | OpenTelemetry.ValueObserver.set_callback("some.counter", fn o -> OpenTelemetry.ValueObserver.observe(o, 33, [])) 7 | """ 8 | 9 | defmacro new(name, opts \\ %{}) do 10 | quote do 11 | :ot_value_observer.new(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(opts)) 12 | end 13 | end 14 | 15 | defmacro set_callback(observer, callback) do 16 | quote do 17 | :ot_meter.set_observer_callback( 18 | :opentelemetry.get_meter(__MODULE__), 19 | unquote(observer), 20 | unquote(callback) 21 | ) 22 | end 23 | end 24 | 25 | defdelegate definition(name, opts), to: :ot_value_observer 26 | defdelegate observe(observer_result, number, label_set), to: :ot_value_observer 27 | end 28 | -------------------------------------------------------------------------------- /lib/open_telemetry/updown_sum_observer.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.UpdownSumObserver do 2 | @moduledoc """ 3 | 4 | require OpenTelemetry.UpdownSumObserver 5 | 6 | OpenTelemetry.UpdownSumObserver.set_callback("some.counter", OpenTelemetry.UpdownSumObserver.observe(&1, -33, [])) 7 | """ 8 | 9 | defmacro new(name, opts \\ %{}) do 10 | quote do 11 | :ot_updown_sum_observer.new(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(opts)) 12 | end 13 | end 14 | 15 | defmacro set_callback(observer, callback) do 16 | quote do 17 | :ot_meter.set_observer_callback( 18 | :opentelemetry.get_meter(__MODULE__), 19 | unquote(observer), 20 | unquote(callback) 21 | ) 22 | end 23 | end 24 | 25 | defdelegate definition(name, opts), to: :ot_updown_sum_observer 26 | defdelegate observe(observer_result, number, label_set), to: :ot_updown_sum_observer 27 | end 28 | -------------------------------------------------------------------------------- /lib/open_telemetry/counter.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.Counter do 2 | @moduledoc """ 3 | 4 | require OpenTelemetry.Counter 5 | 6 | OpenTelemetry.Counter.new("some.counter") 7 | 8 | OpenTelemetry.Counter.add("some.counter", 3) 9 | """ 10 | 11 | defmacro new(name, opts \\ %{}) do 12 | quote do 13 | :ot_counter.new(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(opts)) 14 | end 15 | end 16 | 17 | defmacro add(name, number, label_set) do 18 | quote do 19 | :ot_meter.record(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(number), unquote(label_set)) 20 | end 21 | end 22 | 23 | defmacro add(bound_instrument, number) do 24 | quote do 25 | :ot_meter.record(:opentelemetry.get_meter(__MODULE__), unquote(bound_instrument), unquote(number)) 26 | end 27 | end 28 | 29 | defdelegate definition(name, opts), to: :ot_counter 30 | defdelegate measurement(name_or_instrument, number), to: :ot_counter 31 | end 32 | -------------------------------------------------------------------------------- /lib/open_telemetry/updown_counter.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.UpdownCounter do 2 | @moduledoc """ 3 | 4 | require OpenTelemetry.UpdownCounter 5 | 6 | OpenTelemetry.UpdownCounter.new("some.counter") 7 | 8 | OpenTelemetry.UpdownCounter.add("some.counter", -3) 9 | """ 10 | 11 | defmacro new(name, opts \\ %{}) do 12 | quote do 13 | :ot_updown_counter.new(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(opts)) 14 | end 15 | end 16 | 17 | defmacro add(name, number, label_set) do 18 | quote do 19 | :ot_meter.record(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(number), unquote(label_set)) 20 | end 21 | end 22 | 23 | defmacro add(bound_instrument, number) do 24 | quote do 25 | :ot_meter.record(:opentelemetry.get_meter(__MODULE__), unquote(bound_instrument), unquote(number)) 26 | end 27 | end 28 | 29 | defdelegate definition(name, opts), to: :ot_updown_counter 30 | defdelegate measurement(name_or_instrument, number), to: :ot_updown_counter 31 | end 32 | -------------------------------------------------------------------------------- /lib/open_telemetry/value_recorder.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.ValueRecorder do 2 | @moduledoc """ 3 | 4 | require OpenTelemetry.ValueRecorder 5 | 6 | OpenTelemetry.ValueRecorder.record("some.recorder", 3) 7 | """ 8 | 9 | defmacro new(name, opts \\ %{}) do 10 | quote do 11 | :ot_value_recorder.new(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(opts)) 12 | end 13 | end 14 | 15 | defmacro record(name, number, label_set) do 16 | quote do 17 | :ot_meter.record( 18 | :opentelemetry.get_meter(__MODULE__), 19 | unquote(name), 20 | unquote(number), 21 | unquote(label_set) 22 | ) 23 | end 24 | end 25 | 26 | defmacro record(bound_instrument, number) do 27 | quote do 28 | :ot_meter.record( 29 | :opentelemetry.get_meter(__MODULE__), 30 | unquote(bound_instrument), 31 | unquote(number) 32 | ) 33 | end 34 | end 35 | 36 | defdelegate definition(name, opts), to: :ot_value_recorder 37 | defdelegate measurement(name_or_instrument, number), to: :ot_value_recorder 38 | end 39 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Common Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - 'master' 7 | push: 8 | branches: 9 | - 'master' 10 | 11 | jobs: 12 | erlang_tests: 13 | name: OTP ${{ matrix.otp_version }} 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | matrix: 18 | otp_version: ['23.0.2', '22.3.4.2', '21.3.8.16'] 19 | os: [ubuntu-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: gleam-lang/setup-erlang@v1.0.0 24 | with: 25 | otp-version: ${{ matrix.otp_version }} 26 | 27 | - name: Compile 28 | run: rebar3 compile 29 | - name: EUnit tests 30 | run: rebar3 eunit 31 | - name: Dialyzer 32 | run: rebar3 dialyzer 33 | - name: XRef 34 | run: rebar3 xref 35 | - name: Covertool 36 | run: rebar3 covertool generate 37 | - uses: codecov/codecov-action@v1 38 | with: 39 | file: _build/test/covertool/opentelemetry.covertool.xml 40 | 41 | elixir_tests: 42 | runs-on: ubuntu-latest 43 | name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} 44 | strategy: 45 | matrix: 46 | otp_version: ['23.0.2', '22.3.4.2', '21.3.8.16'] 47 | elixir: ['1.9.4'] 48 | steps: 49 | - uses: actions/checkout@v2 50 | - uses: actions/setup-elixir@v1 51 | with: 52 | otp-version: ${{matrix.otp_version}} 53 | elixir-version: ${{matrix.elixir}} 54 | - run: mix deps.get 55 | - run: mix test 56 | -------------------------------------------------------------------------------- /src/ot_observer.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2020, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc An `Observer' is a callback based instrument with a `LastValue' aggregator. 16 | %% On each collection cycle each Observer callback is run and passed an 17 | %% `ObserverInstrument' variable to use when `observe'ing the new value, passing 18 | %% the `ObserverInstrument', the new value and a list of attributes, key/value pairs. 19 | %% @end 20 | %%%------------------------------------------------------------------------- 21 | -module(ot_observer). 22 | 23 | %% value containing all information needed by the SDK to record an update 24 | -type instrument() :: term(). 25 | 26 | %% function called with an `observer_instrument' argument to update observer 27 | -type callback() :: fun((instrument()) -> ok). 28 | 29 | -export_type([callback/0]). 30 | 31 | -callback set_callback(opentelemetry:meter(), ot_meter:name(), callback()) -> ok. 32 | -callback observe(instrument(), number(), ot_meter:labels()) -> ok. 33 | -------------------------------------------------------------------------------- /test/otel_metrics_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(otel_metrics_SUITE). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("stdlib/include/assert.hrl"). 6 | -include_lib("common_test/include/ct.hrl"). 7 | 8 | -include("opentelemetry.hrl"). 9 | -include("meter.hrl"). 10 | 11 | all() -> 12 | [noop_metrics, macros, non_overridable]. 13 | 14 | init_per_suite(Config) -> 15 | application:load(opentelemetry_api), 16 | Config. 17 | 18 | end_per_suite(_Config) -> 19 | ok. 20 | 21 | noop_metrics(_Config) -> 22 | Meter = opentelemetry:get_meter(), 23 | ?assertMatch({ot_meter_noop, _}, Meter), 24 | 25 | ?assert(ot_counter:new(Meter, <<"noop-measure-1">>, #{description => <<"some description">>})), 26 | 27 | ok. 28 | 29 | macros(_Config) -> 30 | ?ot_new_instruments([#{name => <<"macros-measure-1">>, 31 | description => <<"some description">>, 32 | kind => counter, 33 | label_keys => [], 34 | monotonic => true, 35 | absolute => true, 36 | unit => one}]), 37 | ok. 38 | 39 | %% checks that opts from the user can't override static attributes of an instrument 40 | non_overridable(_Config) -> 41 | {_, _, Instrument} = ot_counter:definition(<<"noop-measure-1">>, #{description => <<"some description">>, 42 | monotonic => false, 43 | synchronous => false}), 44 | 45 | ?assert(maps:get(monotonic, Instrument)), 46 | ?assert(maps:get(synchronous, Instrument)), 47 | 48 | ok. 49 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "cmark": {:hex, :cmark, "0.7.0", "cf20106714b35801df3a2250a8ca478366f93ad6067df9d6815c80b2606ae01e", [:make, :mix], [], "hexpm", "deffa0b66cba126433efb54c068354bcf7c1fd8ba0f579741ab1d3cb26e0f942"}, 3 | "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, 4 | "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, 5 | "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a10c6eb62cca416019663129699769f0c2ccf39428b3bb3c0cb38c718a0c186d"}, 6 | "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"}, 7 | "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, 8 | "wts": {:hex, :wts, "0.4.0", "62d9dc400ad29f0d233f0665b9c75c8f8eb0a8af75eec921056ba4a73b0605a2", [:rebar3], [], "hexpm", "711bb675de2ce2b3ebab80a613ac93b994f74df44af4cff7970dc9eebe724869"}, 9 | } 10 | -------------------------------------------------------------------------------- /src/ot_meter_noop.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_meter_noop). 19 | 20 | -behaviour(ot_meter). 21 | 22 | -export([new_instrument/4, 23 | new_instruments/2, 24 | labels/2, 25 | record/3, 26 | record/4, 27 | record_batch/3, 28 | bind/3, 29 | release/2, 30 | set_observer_callback/3, 31 | register_observer/3, 32 | observe/3]). 33 | 34 | new_instrument(_, _, _, _) -> 35 | true. 36 | 37 | new_instruments(_, _) -> 38 | true. 39 | 40 | labels(_, _) -> 41 | #{}. 42 | 43 | record(_, _, _) -> 44 | ok. 45 | 46 | record(_, _, _, _) -> 47 | ok. 48 | 49 | record_batch(_, _, _) -> 50 | ok. 51 | 52 | bind(_, _, _) -> 53 | []. 54 | 55 | release(_, _) -> 56 | ok. 57 | 58 | set_observer_callback(_, _, _) -> 59 | ok. 60 | 61 | register_observer(_, _, _) -> 62 | ok. 63 | 64 | observe(_, _, _) -> 65 | ok. 66 | -------------------------------------------------------------------------------- /include/tracer.hrl: -------------------------------------------------------------------------------- 1 | %% macros for tracing 2 | %% register a tracer for an application with opentelemetry:register_application_tracer(AppName) 3 | 4 | -define(current_tracer, opentelemetry:get_tracer(?MODULE)). 5 | -define(current_span_ctx, ot_tracer:current_span_ctx(?current_tracer)). 6 | 7 | -define(start_span(SpanName), 8 | ot_tracer:start_span(?current_tracer, SpanName, #{})). 9 | 10 | -define(start_span(SpanName, StartOpts), 11 | ot_tracer:start_span(?current_tracer, SpanName, StartOpts)). 12 | 13 | -define(start_inactive_span(SpanName), 14 | ot_tracer:start_inactive_span(?current_tracer, SpanName, #{})). 15 | 16 | -define(start_inactive_span(SpanName, StartOpts), 17 | ot_tracer:start_inactive_span(?current_tracer, SpanName, StartOpts)). 18 | 19 | -define(set_span(SpanCtx), 20 | ot_tracer:set_span(?current_tracer, SpanCtx)). 21 | 22 | -define(with_span(SpanName, StartOpts, Fun), 23 | ot_tracer:with_span(?current_tracer, SpanName, StartOpts, Fun)). 24 | 25 | -define(end_span(), 26 | ot_tracer:end_span(?current_tracer)). 27 | 28 | -define(current_span_ctx(), 29 | ot_tracer:current_span_ctx(?current_tracer)). 30 | 31 | -define(is_recording(), 32 | ot_span:is_recording(?current_tracer, ?current_span_ctx)). 33 | 34 | -define(set_attribute(Key, Value), 35 | ot_span:set_attribute(?current_tracer, ?current_span_ctx, Key, Value)). 36 | 37 | -define(set_attributes(Attributes), 38 | ot_span:set_attributes(?current_tracer, ?current_span_ctx, Attributes)). 39 | 40 | -define(add_event(Event), 41 | ot_span:add_event(?current_tracer, ?current_span_ctx, Event)). 42 | 43 | -define(add_events(Events), 44 | ot_span:add_events(?current_tracer, ?current_span_ctx, Events)). 45 | 46 | -define(add_links(Links), 47 | ot_span:add_links(?current_tracer, ?current_span_ctx, Links)). 48 | 49 | -define(set_status(Status), 50 | ot_span:set_status(?current_tracer, ?current_span_ctx, Status)). 51 | 52 | -define(update_name(Name), 53 | ot_span:update_name(?current_tracer, ?current_span_ctx, Name)). 54 | -------------------------------------------------------------------------------- /include/meter.hrl: -------------------------------------------------------------------------------- 1 | %% macros for meters 2 | %% register a meter for an application with opentelemetry:register_application_meter(AppName) 3 | 4 | -define(ot_current_meter, opentelemetry:get_meter(?MODULE)). 5 | 6 | -define(ot_new_counter(Meter, Name, Opts), 7 | ot_counter:new(?ot_current_meter, Name, Opts)). 8 | 9 | -define(ot_new_updown_counter(Meter, Name, Opts), 10 | ot_updown_counter:new(?ot_current_meter, Name, Opts)). 11 | 12 | -define(ot_new_value_recorder(Meter, Name, Opts), 13 | ot_value_recorder:new(?ot_current_meter, Name, Opts)). 14 | 15 | -define(ot_new_sum_observer(Meter, Name, Opts), 16 | ot_sum_observer:new(?ot_current_meter, Name, Opts)). 17 | 18 | -define(ot_new_updown_observer(Meter, Name, Opts), 19 | ot_updown_observer:new(?ot_current_meter, Name, Opts)). 20 | 21 | -define(ot_new_value_observer(Meter, Name, Opts), 22 | ot_value_observer:new(?ot_current_meter, Name, Opts)). 23 | 24 | -define(ot_new_instruments(List), 25 | ot_meter:new_instruments(?ot_current_meter, List)). 26 | 27 | -define(ot_counter_add(BoundCounter, Number), 28 | ot_counter:add(BoundCounter, Number)). 29 | 30 | -define(ot_counter_add(Name, Number, LabelSet), 31 | ot_counter:add(?ot_current_meter, Name, Number, LabelSet)). 32 | 33 | -define(ot_measure_record(BoundMeasure, Number), 34 | ot_measure:record(BoundMeasure, Number)). 35 | 36 | -define(ot_measure_record(Name, Number, LabelSet), 37 | ot_measure:record(?ot_current_meter, Name, Number, LabelSet)). 38 | 39 | -define(ot_bind(Name, LabelSet), 40 | ot_meter:bind(?ot_current_meter, Name, LabelSet)). 41 | 42 | -define(ot_release(BoundInstrument), 43 | ot_meter:release(?ot_current_meter, BoundInstrument)). 44 | 45 | -define(ot_record(Name, Number, LabelSet), 46 | ot_meter:record(?ot_current_meter, Name, Number, LabelSet)). 47 | 48 | -define(ot_record(BoundInstrument, Number), 49 | ot_meter:record(BoundInstrument, Number)). 50 | 51 | -define(ot_record_batch(LabelSet, Measurements), 52 | ot_meter:record_batch(?ot_current_meter, LabelSet, Measurements)). 53 | -------------------------------------------------------------------------------- /lib/open_telemetry/meter.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.Meter do 2 | @moduledoc """ 3 | 4 | require OpenTelemetry.ValueRecorder 5 | require OpenTelemetry.Counter 6 | require OpenTelemetry.Meter 7 | 8 | OpenTelemetry.register_application_meter(Your.Application) 9 | 10 | OpenTelemetry.Meter.new_instruments([OpenTelemetry.ValueRecorder.instrument("some.latency"), 11 | OpenTelemetry.Counter.instrument("some.counter")]) 12 | 13 | # use the new instrument by name 14 | OpenTelemetry.Counter.add("some.counter", 1) 15 | 16 | # or use a bound instrument 17 | bound = OpenTelemetry.Meter.bind("some.latency", []) 18 | # measure time spent on some function and then record it 19 | OpenTelemetry.ValueRecorder.record(bound, time) 20 | """ 21 | 22 | defmacro new_instruments(list) do 23 | quote do 24 | :ot_meter.new_instruments(:opentelemetry.get_meter(__MODULE__), unquote(list)) 25 | end 26 | end 27 | 28 | defmacro bind(name, label_set) do 29 | quote do 30 | :ot_meter.bind(:opentelemetry.get_meter(__MODULE__), unquote(name), unquote(label_set)) 31 | end 32 | end 33 | 34 | defmacro release(bound_instrument) do 35 | quote do 36 | :ot_meter.release(:opentelemetry.get_meter(__MODULE__), unquote(bound_instrument)) 37 | end 38 | end 39 | 40 | defmacro record(name, number, label_set) do 41 | quote do 42 | :ot_meter.record( 43 | :opentelemetry.get_meter(__MODULE__), 44 | unquote(name), 45 | unquote(number), 46 | unquote(label_set) 47 | ) 48 | end 49 | end 50 | 51 | defmacro record(bound_instrument, number) do 52 | quote do 53 | :ot_meter.record( 54 | :opentelemetry.get_meter(__MODULE__), 55 | unquote(bound_instrument), 56 | unquote(number) 57 | ) 58 | end 59 | end 60 | 61 | defmacro record_batch(label_set, measurements) do 62 | quote do 63 | :ot_meter.record_batch( 64 | :opentelemetry.get_meter(__MODULE__), 65 | unquote(label_set), 66 | unquote(measurements) 67 | ) 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /test/open_telemetry_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetryTest do 2 | use ExUnit.Case, async: true 3 | 4 | require OpenTelemetry.Tracer, as: Tracer 5 | require OpenTelemetry.Span, as: Span 6 | 7 | require Record 8 | @fields Record.extract(:span_ctx, from_lib: "opentelemetry_api/include/opentelemetry.hrl") 9 | Record.defrecordp(:span_ctx, @fields) 10 | 11 | @fields Record.extract(:link, from_lib: "opentelemetry_api/include/opentelemetry.hrl") 12 | Record.defrecordp(:link, @fields) 13 | 14 | test "current_span tracks nesting" do 15 | _ctx1 = Tracer.start_span("span-1") 16 | ctx2 = Tracer.start_span("span-2") 17 | 18 | assert ctx2 == Tracer.current_span_ctx() 19 | end 20 | 21 | test "link creation" do 22 | ctx = span_ctx(trace_id: 1, span_id: 2, tracestate: []) 23 | 24 | link(trace_id: t, span_id: s, attributes: a, tracestate: ts) = OpenTelemetry.link(ctx) 25 | 26 | assert 1 == t 27 | assert 2 == s 28 | assert [] == ts 29 | assert [] == a 30 | 31 | link(trace_id: t, span_id: s, attributes: a, tracestate: ts) = 32 | OpenTelemetry.link(ctx, [{"attr-1", "value-1"}]) 33 | 34 | assert 1 == t 35 | assert 2 == s 36 | assert [] == ts 37 | assert [{"attr-1", "value-1"}] == a 38 | 39 | end 40 | 41 | test "closing a span makes the parent current" do 42 | ctx1 = Tracer.start_span("span-1") 43 | ctx2 = Tracer.start_span("span-2") 44 | 45 | assert ctx2 == Tracer.current_span_ctx() 46 | OpenTelemetry.Tracer.end_span() 47 | assert ctx1 == Tracer.current_span_ctx() 48 | end 49 | 50 | test "macro start_span" do 51 | Tracer.with_span "span-1" do 52 | Tracer.with_span "span-2" do 53 | Span.set_attribute("attr-1", "value-1") 54 | 55 | event1 = OpenTelemetry.event("event-1", []) 56 | event2 = OpenTelemetry.event("event-2", []) 57 | 58 | Span.add_events([event1, event2]) 59 | end 60 | end 61 | end 62 | 63 | test "can deconstruct a span context" do 64 | Tracer.with_span "span-1" do 65 | span = Tracer.current_span_ctx() 66 | 67 | assert nil != Span.trace_id(span) 68 | assert nil != Span.span_id(span) 69 | assert [] = Span.tracestate(span) 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /src/ot_sum_observer.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2020, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_sum_observer). 19 | 20 | -behaviour(ot_instrument). 21 | -behaviour(ot_observer). 22 | 23 | -export([new/2, 24 | new/3, 25 | definition/1, 26 | definition/2, 27 | set_callback/3, 28 | observe/3]). 29 | 30 | -include("meter.hrl"). 31 | 32 | -define(PROPERTIES, #{monotonic => true, 33 | synchronous => false}). 34 | 35 | -spec new(opentelemetry:meter(), ot_meter:name()) -> boolean(). 36 | new(Meter, Name) -> 37 | new(Meter, Name, #{}). 38 | 39 | -spec new(opentelemetry:meter(), ot_meter:name(), ot_meter:instrument_opts()) -> boolean(). 40 | new(Meter, Name, Opts) -> 41 | ot_meter:new_instrument(Meter, Name, ?MODULE, Opts). 42 | 43 | -spec definition(ot_meter:name()) -> ot_meter:instrument_definition(). 44 | definition(Name) -> 45 | definition(Name, #{}). 46 | 47 | -spec definition(ot_meter:name(), ot_meter:instrument_opts()) -> ot_meter:instrument_definition(). 48 | definition(Name, Opts) -> 49 | ot_meter:instrument_definition(?MODULE, Name, ?PROPERTIES, Opts). 50 | 51 | -spec set_callback(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) -> ok. 52 | set_callback(Meter, Observer, Callback) -> 53 | ot_meter:set_observer_callback(Meter, Observer, Callback). 54 | 55 | -spec observe(ot_observer:instrument(), number(), ot_meter:labels()) -> ok. 56 | observe(ObserverInstrument, Number, LabelSet) -> 57 | ot_meter:observe(ObserverInstrument, Number, LabelSet). 58 | -------------------------------------------------------------------------------- /src/ot_value_observer.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2020, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_value_observer). 19 | 20 | -behaviour(ot_instrument). 21 | -behaviour(ot_observer). 22 | 23 | -export([new/2, 24 | new/3, 25 | definition/1, 26 | definition/2, 27 | set_callback/3, 28 | observe/3]). 29 | 30 | -include("meter.hrl"). 31 | 32 | -define(PROPERTIES, #{monotonic => false, 33 | synchronous => false}). 34 | 35 | -spec new(opentelemetry:meter(), ot_meter:name()) -> boolean(). 36 | new(Meter, Name) -> 37 | new(Meter, Name, #{}). 38 | 39 | -spec new(opentelemetry:meter(), ot_meter:name(), ot_meter:instrument_opts()) -> boolean(). 40 | new(Meter, Name, Opts) -> 41 | ot_meter:new_instrument(Meter, Name, ?MODULE, Opts). 42 | 43 | -spec definition(ot_meter:name()) -> ot_meter:instrument_definition(). 44 | definition(Name) -> 45 | definition(Name, #{}). 46 | 47 | -spec definition(ot_meter:name(), ot_meter:instrument_opts()) -> ot_meter:instrument_definition(). 48 | definition(Name, Opts) -> 49 | ot_meter:instrument_definition(?MODULE, Name, ?PROPERTIES, Opts). 50 | 51 | -spec set_callback(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) -> ok. 52 | set_callback(Meter, Observer, Callback) -> 53 | ot_meter:set_observer_callback(Meter, Observer, Callback). 54 | 55 | -spec observe(ot_observer:instrument(), number(), ot_meter:labels()) -> ok. 56 | observe(ObserverInstrument, Number, LabelSet) -> 57 | ot_meter:observe(ObserverInstrument, Number, LabelSet). 58 | -------------------------------------------------------------------------------- /src/ot_updown_sum_observer.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2020, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_updown_sum_observer). 19 | 20 | -behaviour(ot_instrument). 21 | -behaviour(ot_observer). 22 | 23 | -export([new/2, 24 | new/3, 25 | definition/1, 26 | definition/2, 27 | set_callback/3, 28 | observe/3]). 29 | 30 | -include("meter.hrl"). 31 | 32 | -define(PROPERTIES, #{monotonic => false, 33 | synchronous => false}). 34 | 35 | -spec new(opentelemetry:meter(), ot_meter:name()) -> boolean(). 36 | new(Meter, Name) -> 37 | new(Meter, Name, #{}). 38 | 39 | -spec new(opentelemetry:meter(), ot_meter:name(), ot_meter:instrument_opts()) -> boolean(). 40 | new(Meter, Name, Opts) -> 41 | ot_meter:new_instrument(Meter, Name, ?MODULE, Opts). 42 | 43 | -spec definition(ot_meter:name()) -> ot_meter:instrument_definition(). 44 | definition(Name) -> 45 | definition(Name, #{}). 46 | 47 | -spec definition(ot_meter:name(), ot_meter:instrument_opts()) -> ot_meter:instrument_definition(). 48 | definition(Name, Opts) -> 49 | ot_meter:instrument_definition(?MODULE, Name, ?PROPERTIES, Opts). 50 | 51 | -spec set_callback(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) -> ok. 52 | set_callback(Meter, Observer, Callback) -> 53 | ot_meter:set_observer_callback(Meter, Observer, Callback). 54 | 55 | -spec observe(ot_observer:instrument(), number(), ot_meter:label_set()) -> ok. 56 | observe(ObserverInstrument, Number, LabelSet) -> 57 | ot_meter:observe(ObserverInstrument, Number, LabelSet). 58 | -------------------------------------------------------------------------------- /src/ot_counter.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_counter). 19 | 20 | -behaviour(ot_instrument). 21 | 22 | -export([new/2, 23 | new/3, 24 | definition/1, 25 | definition/2, 26 | add/2, 27 | add/4, 28 | measurement/2]). 29 | 30 | -include("meter.hrl"). 31 | 32 | -define(PROPERTIES, #{monotonic => true, 33 | synchronous => true}). 34 | 35 | -spec new(opentelemetry:meter(), ot_meter:name()) -> boolean(). 36 | new(Meter, Name) -> 37 | new(Meter, Name, #{}). 38 | 39 | -spec new(opentelemetry:meter(), ot_meter:name(), ot_meter:instrument_opts()) -> boolean(). 40 | new(Meter, Name, Opts) -> 41 | ot_meter:new_instrument(Meter, Name, ?MODULE, Opts). 42 | 43 | -spec definition(ot_meter:name()) -> ot_meter:instrument_definition(). 44 | definition(Name) -> 45 | definition(Name, #{}). 46 | 47 | -spec definition(ot_meter:name(), ot_meter:instrument_opts()) -> ot_meter:instrument_definition(). 48 | definition(Name, Opts) -> 49 | ot_meter:instrument_definition(?MODULE, Name, ?PROPERTIES, Opts). 50 | 51 | -spec add(ot_meter:bound_instrument(), number()) -> ok. 52 | add(BoundInstrument, Number) -> 53 | ot_meter:record(BoundInstrument, Number). 54 | 55 | -spec add(opentelemetry:meter(), ot_meter:name(), number(), ot_meter:labels()) -> ok. 56 | add(Meter, Name, Number, Labels) -> 57 | ot_meter:record(Meter, Name, Number, Labels). 58 | 59 | -spec measurement(ot_meter:bound_instrument() | ot_meter:name(), number()) 60 | -> {ot_meter:bound_instrument() | ot_meter:name(), number()}. 61 | measurement(NameOrInstrument, Number) -> 62 | {NameOrInstrument, Number}. 63 | -------------------------------------------------------------------------------- /src/ot_updown_counter.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_updown_counter). 19 | 20 | -behaviour(ot_instrument). 21 | 22 | -export([new/2, 23 | new/3, 24 | definition/1, 25 | definition/2, 26 | add/2, 27 | add/4, 28 | measurement/2]). 29 | 30 | -include("meter.hrl"). 31 | 32 | -define(PROPERTIES, #{monotonic => false, 33 | synchronous => true}). 34 | 35 | -spec new(opentelemetry:meter(), ot_meter:name()) -> boolean(). 36 | new(Meter, Name) -> 37 | new(Meter, Name, #{}). 38 | 39 | -spec new(opentelemetry:meter(), ot_meter:name(), ot_meter:instrument_opts()) -> boolean(). 40 | new(Meter, Name, Opts) -> 41 | ot_meter:new_instrument(Meter, Name, ?MODULE, Opts). 42 | 43 | -spec definition(ot_meter:name()) -> ot_meter:instrument_definition(). 44 | definition(Name) -> 45 | definition(Name, #{}). 46 | 47 | -spec definition(ot_meter:name(), ot_meter:instrument_opts()) -> ot_meter:instrument_definition(). 48 | definition(Name, Opts) -> 49 | ot_meter:instrument_definition(?MODULE, Name, ?PROPERTIES, Opts). 50 | 51 | -spec add(ot_meter:bound_instrument(), number()) -> ok. 52 | add(BoundInstrument, Number) -> 53 | ot_meter:record(BoundInstrument, Number). 54 | 55 | -spec add(opentelemetry:meter(), ot_meter:name(), number(), ot_meter:label_set()) -> ok. 56 | add(Meter, Name, Number, LabelSet) -> 57 | ot_meter:record(Meter, Name, Number, LabelSet). 58 | 59 | -spec measurement(ot_meter:bound_instrument() | ot_meter:name(), number()) 60 | -> {ot_meter:bound_instrument() | ot_meter:name(), number()}. 61 | measurement(NameOrInstrument, Number) -> 62 | {NameOrInstrument, Number}. 63 | -------------------------------------------------------------------------------- /src/ot_value_recorder.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_value_recorder). 19 | 20 | -behaviour(ot_instrument). 21 | 22 | -export([new/2, 23 | new/3, 24 | definition/1, 25 | definition/2, 26 | record/2, 27 | record/4, 28 | measurement/2]). 29 | 30 | -include("meter.hrl"). 31 | 32 | -define(PROPERTIES, #{monotonic => false, 33 | synchronous => true}). 34 | 35 | -spec new(opentelemetry:meter(), ot_meter:name()) -> boolean(). 36 | new(Meter, Name) -> 37 | new(Meter, Name, #{}). 38 | 39 | -spec new(opentelemetry:meter(), ot_meter:name(), ot_meter:instrument_opts()) -> boolean(). 40 | new(Meter, Name, Opts) -> 41 | ot_meter:new_instrument(Meter, Name, ?MODULE, Opts). 42 | 43 | -spec definition(ot_meter:name()) -> ot_meter:instrument_definition(). 44 | definition(Name) -> 45 | definition(Name, #{}). 46 | 47 | -spec definition(ot_meter:name(), ot_meter:instrument_opts()) -> ot_meter:instrument_definition(). 48 | definition(Name, Opts) -> 49 | ot_meter:instrument_definition(?MODULE, Name, ?PROPERTIES, Opts). 50 | 51 | -spec record(ot_meter:bound_instrument(), number()) -> ok. 52 | record(BoundInstrument, Number) -> 53 | ot_meter:record(BoundInstrument, Number). 54 | 55 | -spec record(opentelemetry:meter(), ot_meter:name(), number(), ot_meter:label_set()) -> ok. 56 | record(Meter, Name, Number, LabelSet) -> 57 | ot_meter:record(Meter, Name, Number, LabelSet). 58 | 59 | -spec measurement(ot_meter:bound_instrument() | ot_meter:name(), number()) 60 | -> {ot_meter:bound_instrument() | ot_meter:name(), number()}. 61 | measurement(NameOrInstrument, Number) -> 62 | {NameOrInstrument, Number}. 63 | -------------------------------------------------------------------------------- /src/ot_instrument.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2020, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc All measurements are associated with an instrument. To record 16 | %% measurements for an instrument it must first be created with `new' and 17 | %% then can be referenced by name. 18 | %% @end 19 | %%%------------------------------------------------------------------------- 20 | -module(ot_instrument). 21 | 22 | %% @doc Calls the SDK to create a new instrument which can then be referenced 23 | %% by name. 24 | %% @end 25 | -callback new(opentelemetry:meter(), ot_meter:name()) -> boolean(). 26 | -callback new(opentelemetry:meter(), ot_meter:name(), ot_meter:instrument_opts()) -> boolean(). 27 | 28 | %% @doc Returns an instrument definition which can be used to create a new instrument 29 | %% by passing to `ot_meter:new_instruments/1' 30 | %% @end 31 | -callback definition(ot_meter:name()) -> ot_meter:instrument_definition(). 32 | -callback definition(ot_meter:name(), ot_meter:instrument_opts()) -> ot_meter:instrument_definition(). 33 | 34 | %% @doc Used by additive instruments to record measurements. 35 | -callback add(ot_meter:bound_instrument(), number()) -> ok. 36 | -callback add(opentelemetry:meter(), ot_meter:name(), number(), ot_meter:labels()) -> ok. 37 | 38 | %% @doc Used by non-additive instruments to record measurements. 39 | -callback record(ot_meter:bound_instrument(), number()) -> ok. 40 | -callback record(opentelemetry:meter(), ot_meter:name(), number(), ot_meter:labels()) -> ok. 41 | 42 | %% @doc Returns a measurement tuple that can be based to a batch recording through `ot_meter:batch_record/3' 43 | -callback measurement(ot_meter:bound_instrument() | ot_meter:name(), number()) -> 44 | {ot_meter:bound_instrument() | ot_meter:name(), number()}. 45 | 46 | -optional_callbacks([add/2, 47 | add/4, 48 | record/2, 49 | record/4, 50 | measurement/2]). 51 | -------------------------------------------------------------------------------- /src/ot_propagation.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_propagation). 19 | 20 | -export([http_inject/1, 21 | http_extract/1]). 22 | 23 | -type extractor(T) :: {fun((T, {fun(), term()}) -> ok), term()} | 24 | {fun((T, ot_ctx:key(), {fun(), term()}) -> ok), term()}. 25 | -type injector(T) :: {fun((T, {fun(), term()}) -> T), term()} | 26 | {fun((T, ot_ctx:key(), {fun(), term()}) -> T), term()}. 27 | 28 | -type http_headers() :: [{binary(), binary()}]. 29 | 30 | -type http_injector() :: injector(http_headers()). 31 | -type http_extractor() :: extractor(http_headers()). 32 | 33 | -export_type([extractor/1, 34 | injector/1, 35 | http_injector/0, 36 | http_extractor/0, 37 | http_headers/0]). 38 | 39 | http_inject(Headers) -> 40 | Injectors = opentelemetry:get_http_injector(), 41 | run_injectors(Headers, Injectors). 42 | 43 | http_extract(Headers) -> 44 | Extractors = opentelemetry:get_http_extractor(), 45 | run_extractors(Headers, Extractors). 46 | 47 | run_extractors(Headers, Extractors) -> 48 | lists:foldl(fun({Extract, {Key, FromText}}, ok) -> 49 | Extract(Headers, Key, FromText), 50 | ok; 51 | ({Extract, FromText}, ok) -> 52 | Extract(Headers, FromText), 53 | ok; 54 | (_, ok) -> 55 | ok 56 | end, ok, Extractors). 57 | 58 | run_injectors(Headers, Injectors) -> 59 | lists:foldl(fun({Inject, {Key, ToText}}, HeadersAcc) -> 60 | Inject(HeadersAcc, Key, ToText); 61 | ({Inject, ToText}, HeadersAcc) -> 62 | Inject(HeadersAcc, ToText); 63 | (_, HeadersAcc) -> 64 | HeadersAcc 65 | end, Headers, Injectors). 66 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | {app, desc} = load_app() 6 | config = load_config() 7 | 8 | [ 9 | app: app, 10 | version: version(Keyword.fetch!(desc, :vsn)), 11 | description: to_string(Keyword.fetch!(desc, :description)), 12 | elixir: "~> 1.8", 13 | start_permanent: Mix.env() == :prod, 14 | # We should never have dependencies 15 | deps: deps(Keyword.fetch!(config, :deps)), 16 | # Docs 17 | name: "OpenTelemetry API", 18 | # source_url: "https://github.com/USER/PROJECT", 19 | # homepage_url: "http://YOUR_PROJECT_HOMEPAGE", 20 | docs: [ 21 | markdown_processor: ExDoc.Markdown.Cmark, 22 | main: "OpenTelemetry", 23 | # logo: "path/to/logo.png", 24 | extras: erlang_docs() 25 | ], 26 | aliases: [ 27 | # when build docs first build edocs with rebar3 28 | docs: ["cmd rebar3 edoc", "docs"] 29 | ], 30 | package: package() 31 | ] 32 | end 33 | 34 | defp version(version) when is_list(version) do 35 | List.to_string(version) 36 | end 37 | 38 | defp version({:file, path}) do 39 | path 40 | |> File.read!() 41 | |> String.trim() 42 | end 43 | 44 | # Run "mix help compile.app" to learn about applications. 45 | def application, do: [] 46 | 47 | defp deps(rebar) do 48 | rebar 49 | |> Enum.map(fn 50 | {dep, version} -> {dep, to_string(version)} 51 | dep when is_atom(dep) -> {dep, ">= 0.0.0"} 52 | end) 53 | |> Enum.concat([ 54 | {:cmark, "~> 0.7", only: :dev, runtime: false}, 55 | {:ex_doc, "~> 0.21", only: :dev, runtime: false} 56 | ]) 57 | end 58 | 59 | defp package() do 60 | [ 61 | description: "OpenTelemetry API", 62 | build_tools: ["rebar3", "mix"], 63 | files: ~w(lib mix.exs README.md LICENSE CODEOWNERS rebar.config rebar.lock VERSION include src), 64 | licenses: ["Apache-2.0"], 65 | links: %{"GitHub" => "https://github.com/open-telemetry/opentelemetry-erlang-api", 66 | "OpenTelemetry.io" => "https://opentelemetry.io"} 67 | ] 68 | end 69 | 70 | def erlang_docs() do 71 | files = 72 | for file <- Path.wildcard("edoc/*.md"), 73 | file != "edoc/README.md", 74 | do: {String.to_atom(file), [title: Path.basename(file, ".md")]} 75 | 76 | [{:"README.md", [title: "Overview"]} | files] 77 | end 78 | 79 | defp load_config do 80 | {:ok, config} = :file.consult('rebar.config') 81 | 82 | config 83 | end 84 | 85 | defp load_app do 86 | {:ok, [{:application, name, desc}]} = :file.consult('src/opentelemetry_api.app.src') 87 | 88 | {name, desc} 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /src/ot_meter_provider.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2020, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_meter_provider). 19 | 20 | -behaviour(gen_server). 21 | 22 | -export([start_link/2, 23 | register_application_meter/1, 24 | register_meter/2]). 25 | 26 | -export([init/1, 27 | handle_call/3, 28 | handle_cast/2]). 29 | 30 | -type cb_state() :: term(). 31 | 32 | -callback init(term()) -> {ok, cb_state()}. 33 | -callback register_meter(atom(), string(), cb_state()) -> boolean(). 34 | 35 | -record(state, {callback :: module(), 36 | cb_state :: term()}). 37 | 38 | start_link(ProviderModule, Opts) -> 39 | gen_server:start_link({local, ?MODULE}, ?MODULE, [ProviderModule, Opts], []). 40 | 41 | -spec register_meter(atom(), string()) -> boolean(). 42 | register_meter(Name, Vsn) -> 43 | try 44 | gen_server:call(?MODULE, {register_meter, Name, Vsn}) 45 | catch exit:{noproc, _} -> 46 | %% ignore register_meter because no SDK has been included and started 47 | false 48 | end. 49 | 50 | -spec register_application_meter(atom()) -> boolean(). 51 | register_application_meter(Name) -> 52 | try 53 | {ok, Vsn} = application:get_key(Name, vsn), 54 | {ok, Modules} = application:get_key(Name, modules), 55 | gen_server:call(?MODULE, {register_meter, Name, Vsn, Modules}) 56 | catch exit:{noproc, _} -> 57 | %% ignore register_meter because no SDK has been included and started 58 | false 59 | end. 60 | 61 | init([ProviderModule, Opts]) -> 62 | case ProviderModule:init(Opts) of 63 | {ok, CbState} -> 64 | {ok, #state{callback=ProviderModule, 65 | cb_state=CbState}}; 66 | Other -> 67 | Other 68 | end. 69 | 70 | handle_call({register_meter, Name, Vsn, Modules}, _From, State=#state{callback=Cb, 71 | cb_state=CbState}) -> 72 | _ = Cb:register_meter(Name, Vsn, Modules, CbState), 73 | {reply, true, State}; 74 | handle_call({register_meter, Name, Vsn}, _From, State=#state{callback=Cb, 75 | cb_state=CbState}) -> 76 | _ = Cb:register_meter(Name, Vsn, CbState), 77 | {reply, true, State}; 78 | handle_call(_Msg, _From, State) -> 79 | {noreply, State}. 80 | 81 | handle_cast(_Msg, State) -> 82 | {noreply, State}. 83 | 84 | %% 85 | -------------------------------------------------------------------------------- /src/ot_baggage.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_baggage). 19 | 20 | -export([ctx_key/0, 21 | set/2, 22 | get_all/0, 23 | get_http_propagators/0]). 24 | 25 | -type key() :: string(). 26 | -type value() :: string(). 27 | 28 | -type t() :: #{key() => value()}. 29 | 30 | -export_type([t/0, 31 | key/0, 32 | value/0]). 33 | 34 | -define(BAGGAGE_KEY, '$__ot_baggage_ctx_key'). 35 | -define(BAGGAGE_HEADER, <<"otcorrelations">>). 36 | 37 | ctx_key() -> 38 | ?BAGGAGE_KEY. 39 | 40 | -spec set(key(), value()) -> ok. 41 | set(Key, Value) -> 42 | Baggage = ot_ctx:get_value(?BAGGAGE_KEY, #{}), 43 | ot_ctx:set_value(?BAGGAGE_KEY, Baggage#{Key => Value}). 44 | 45 | -spec get_all() -> t(). 46 | get_all() -> 47 | ot_ctx:get_value(?BAGGAGE_KEY, #{}). 48 | 49 | -spec get_http_propagators() -> {ot_propagation:http_extractor(), ot_propagation:http_injector()}. 50 | get_http_propagators() -> 51 | ToText = fun(_Headers, Baggage) -> 52 | case maps:fold(fun(Key, Value, Acc) -> 53 | [$,, [Key, "=", Value] | Acc] 54 | end, [], Baggage) of 55 | [$, | List] -> 56 | [{?BAGGAGE_HEADER, unicode:characters_to_list(List)}]; 57 | _ -> 58 | [] 59 | end 60 | end, 61 | FromText = fun(Headers, CurrentBaggage) -> 62 | case lookup(?BAGGAGE_HEADER, Headers) of 63 | undefined -> 64 | CurrentBaggage; 65 | String -> 66 | Pairs = string:lexemes(String, [$,]), 67 | lists:foldl(fun(Pair, Acc) -> 68 | [Key, Value] = string:split(Pair, "="), 69 | Acc#{unicode:characters_to_list(Key) => 70 | unicode:characters_to_list(Value)} 71 | end, CurrentBaggage, Pairs) 72 | end 73 | end, 74 | Inject = ot_ctx:http_injector(?BAGGAGE_KEY, ToText), 75 | Extract = ot_ctx:http_extractor(?BAGGAGE_KEY, FromText), 76 | {Extract, Inject}. 77 | 78 | %% find a header in a list, ignoring case 79 | lookup(_, []) -> 80 | undefined; 81 | lookup(Header, [{H, Value} | Rest]) -> 82 | case string:equal(Header, H, true, none) of 83 | true -> 84 | Value; 85 | false -> 86 | lookup(Header, Rest) 87 | end. 88 | -------------------------------------------------------------------------------- /src/ot_tracer_noop.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_tracer_noop). 19 | 20 | -behaviour(ot_tracer). 21 | 22 | -export([start_span/3, 23 | start_span/4, 24 | start_inactive_span/3, 25 | start_inactive_span/4, 26 | set_span/2, 27 | with_span/3, 28 | with_span/4, 29 | end_span/1, 30 | end_span/2, 31 | span_module/1, 32 | current_ctx/1, 33 | current_span_ctx/1]). 34 | 35 | -include("opentelemetry.hrl"). 36 | 37 | -define(NOOP_SPAN_CTX, #span_ctx{trace_id=0, 38 | span_id=0, 39 | trace_flags=0, 40 | tracestate=[], 41 | is_valid=false, 42 | is_recording=false}). 43 | -define(NOOP_TRACER_CTX, []). 44 | 45 | -spec start_span(opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts()) -> opentelemetry:span_ctx(). 46 | start_span(_, _Name, _) -> 47 | ?NOOP_SPAN_CTX. 48 | 49 | -spec start_span(ot_ctx:ctx(), opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts()) 50 | -> {opentelemetry:span_ctx(), ot_ctx:ctx()}. 51 | start_span(Ctx, _, _Name, _) -> 52 | {?NOOP_SPAN_CTX, Ctx}. 53 | 54 | -spec start_inactive_span(opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts()) 55 | -> opentelemetry:span_ctx(). 56 | start_inactive_span(_, _Name, _Opts) -> 57 | ?NOOP_SPAN_CTX. 58 | 59 | -spec start_inactive_span(ot_ctx:ctx(), opentelemetry:tracer(), opentelemetry:span_name(), 60 | ot_span:start_opts()) -> {opentelemetry:span_ctx(), ot_ctx:ctx()}. 61 | start_inactive_span(Ctx, _, _Name, _Opts) -> 62 | {?NOOP_SPAN_CTX, Ctx}. 63 | 64 | -spec set_span(opentelemetry:tracer(), opentelemetry:span_ctx()) -> ok. 65 | set_span(_, _SpanCtx) -> 66 | ok. 67 | 68 | -spec with_span(opentelemetry:tracer(), opentelemetry:span_name(), ot_tracer:traced_fun(T)) -> T. 69 | with_span(_, _SpanName, Fun) -> 70 | Fun(?NOOP_SPAN_CTX). 71 | 72 | -spec with_span(opentelemetry:tracer(), opentelemetry:span_name(), 73 | ot_span:start_opts(), ot_tracer:traced_fun(T)) -> T. 74 | with_span(_, _SpanName, _Opts, Fun) -> 75 | Fun(?NOOP_SPAN_CTX). 76 | 77 | -spec current_ctx(opentelemetry:tracer()) -> ot_tracer:tracer_ctx(). 78 | current_ctx(_Tracer) -> 79 | ?NOOP_TRACER_CTX. 80 | 81 | -spec current_span_ctx(opentelemetry:tracer()) -> opentelemetry:span_ctx(). 82 | current_span_ctx(_) -> 83 | ?NOOP_SPAN_CTX. 84 | 85 | span_module(_) -> 86 | ot_span_noop. 87 | 88 | -spec end_span(ot_ctx:ctx() | opentelemetry:tracer(), opentelemetry:tracer() | opentelemetry:span_ctx()) 89 | -> boolean() | {error, term()}. 90 | end_span(_, _) -> 91 | true. 92 | 93 | -spec end_span(opentelemetry:tracer()) -> boolean() | {error, term()}. 94 | end_span(_) -> 95 | true. 96 | -------------------------------------------------------------------------------- /src/ot_tracer_provider.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_tracer_provider). 19 | 20 | -behaviour(gen_server). 21 | 22 | -export([start_link/2, 23 | resource/0, 24 | register_application_tracer/1, 25 | register_tracer/2]). 26 | 27 | -export([init/1, 28 | handle_call/3, 29 | handle_cast/2, 30 | code_change/3]). 31 | 32 | -type cb_state() :: term(). 33 | 34 | -callback init(term()) -> {ok, cb_state()}. 35 | -callback register_tracer(atom(), string(), cb_state()) -> boolean(). 36 | -callback resource(cb_state()) -> term() | undefined. 37 | 38 | -record(state, {callback :: module(), 39 | cb_state :: term()}). 40 | 41 | start_link(ProviderModule, Opts) -> 42 | gen_server:start_link({local, ?MODULE}, ?MODULE, [ProviderModule, Opts], []). 43 | 44 | -spec resource() -> term() | undefined. 45 | resource() -> 46 | try 47 | gen_server:call(?MODULE, resource) 48 | catch exit:{noproc, _} -> 49 | %% ignore because no SDK has been included and started 50 | undefined 51 | end. 52 | 53 | 54 | -spec register_tracer(atom(), string()) -> boolean(). 55 | register_tracer(Name, Vsn) -> 56 | try 57 | gen_server:call(?MODULE, {register_tracer, Name, Vsn}) 58 | catch exit:{noproc, _} -> 59 | %% ignore register_tracer because no SDK has been included and started 60 | false 61 | end. 62 | 63 | -spec register_application_tracer(atom()) -> boolean(). 64 | register_application_tracer(Name) -> 65 | try 66 | {ok, Vsn} = application:get_key(Name, vsn), 67 | {ok, Modules} = application:get_key(Name, modules), 68 | gen_server:call(?MODULE, {register_tracer, Name, Vsn, Modules}) 69 | catch exit:{noproc, _} -> 70 | %% ignore register_tracer because no SDK has been included and started 71 | false 72 | end. 73 | 74 | init([ProviderModule, Opts]) -> 75 | case ProviderModule:init(Opts) of 76 | {ok, CbState} -> 77 | {ok, #state{callback=ProviderModule, 78 | cb_state=CbState}}; 79 | Other -> 80 | Other 81 | end. 82 | 83 | handle_call({register_tracer, Name, Vsn, Modules}, _From, State=#state{callback=Cb, 84 | cb_state=CbState}) -> 85 | _ = Cb:register_tracer(Name, Vsn, Modules, CbState), 86 | {reply, true, State}; 87 | handle_call({register_tracer, Name, Vsn}, _From, State=#state{callback=Cb, 88 | cb_state=CbState}) -> 89 | _ = Cb:register_tracer(Name, Vsn, CbState), 90 | {reply, true, State}; 91 | handle_call(resource, _From, State=#state{callback=Cb, 92 | cb_state=CbState}) -> 93 | Resource = Cb:resource(CbState), 94 | {reply, Resource, State}; 95 | handle_call(_Msg, _From, State) -> 96 | {noreply, State}. 97 | 98 | handle_cast(_Msg, State) -> 99 | {noreply, State}. 100 | 101 | %% TODO: Use `Extra' as options to update the state like the sampler? 102 | code_change(_OldVsn, State=#state{callback=Cb, 103 | cb_state=CbState}, _Extra) -> 104 | NewCbState = Cb:code_change(CbState), 105 | {ok, State#state{cb_state=NewCbState}}. 106 | 107 | %% 108 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to opentelemetry-erlang-api 2 | 3 | The Erlang/Elixir special interest group (SIG) meets as part of the 4 | [Erlang Ecosystem Foundation](https://erlef.org/wg/observability). See the 5 | OpenTelemetry [community](https://github.com/open-telemetry/community#erlangelixir-sdk) 6 | repo for information on this and other language SIGs. 7 | 8 | 9 | ## Pull Requests 10 | 11 | ### How to Send Pull Requests 12 | 13 | Everyone is welcome to contribute code to `opentelemetry-erlang-api` via 14 | GitHub pull requests (PRs). 15 | 16 | To create a new PR, fork the project in GitHub and clone the upstream 17 | repo: 18 | 19 | ```sh 20 | $ git clone https://github.com/open-telemetry/opentelemetry-erlang-api/ 21 | ``` 22 | 23 | This would put the project in the `opentelemetry-erlang-api` directory in 24 | current working directory. 25 | 26 | Enter the newly created directory and add your fork as a new remote: 27 | 28 | ```sh 29 | $ git remote add git@github.com:/opentelemetry-erlang-api 30 | ``` 31 | 32 | Check out a new branch, make modifications, run tests, and 33 | push the branch to your fork: 34 | 35 | ```sh 36 | $ git checkout -b 37 | # edit files 38 | $ rebar3 ct 39 | $ mix test 40 | $ git add -p 41 | $ git commit 42 | $ git push 43 | ``` 44 | 45 | Open a pull request against the main `opentelemetry-erlang-api` repo. 46 | 47 | ### How to Receive Comments 48 | 49 | * If the PR is not ready for review, please put `[WIP]` in the title, 50 | tag it as `work-in-progress`, or mark it as 51 | [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/). 52 | * Make sure CLA is signed and CI is clear. 53 | 54 | ### How to Get PRs Merged 55 | 56 | A PR is considered to be **ready to merge** when: 57 | 58 | * It has received two approvals from Collaborators/Maintainers (at 59 | different companies). 60 | * Major feedbacks are resolved. 61 | * It has been open for review for at least one working day. This gives 62 | people reasonable time to review. 63 | * Trivial change (typo, cosmetic, doc, etc.) doesn't have to wait for 64 | one day. 65 | * Urgent fix can take exception as long as it has been actively 66 | communicated. 67 | 68 | Any Collaborator/Maintainer can merge the PR once it is **ready to 69 | merge**. 70 | 71 | ## Design Choices 72 | 73 | As with other OpenTelemetry clients, opentelemetry-erlang-api follows the 74 | [opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification). 75 | 76 | It's especially valuable to read through the [library 77 | guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/library-guidelines.md). 78 | 79 | ### Focus on Capabilities, Not Structure Compliance 80 | 81 | OpenTelemetry is an evolving specification, one where the desires and 82 | use cases are clear, but the method to satisfy those uses cases are 83 | not. 84 | 85 | As such, Contributions should provide functionality and behavior that 86 | conforms to the specification, but the interface and structure is 87 | flexible. 88 | 89 | It is preferable to have contributions follow the idioms of the 90 | language rather than conform to specific API names or argument 91 | patterns in the spec. 92 | 93 | For a deeper discussion, see: 94 | https://github.com/open-telemetry/opentelemetry-specification/issues/165 95 | 96 | ## Style Guide 97 | 98 | * Make sure to run `make precommit` - this will find and fix the code 99 | formatting. 100 | 101 | ## Approvers and Maintainers 102 | 103 | Approvers: 104 | 105 | - [Fred Hebert](https://github.com/ferd), Postmates 106 | - [Greg Mefford](https://github.com/gregmefford), Bleacher Report 107 | - [Zach Daniel](https://github.com/zachdaniel), Variance 108 | 109 | Maintainers: 110 | 111 | - [Tristan Sloughter](https://github.com/tsloughter), Postmates 112 | - [Ilya Khaprov](https://github.com/deadtrickster), Kobil Systems GmbH 113 | - [Łukasz Jan Niemier](https://github.com/hauleth), Kobil Systems GmbH 114 | 115 | ### Become an Approver or a Maintainer 116 | 117 | See the [community membership document in OpenTelemetry community 118 | repo](https://github.com/open-telemetry/community/blob/master/community-membership.md). 119 | -------------------------------------------------------------------------------- /src/ot_ctx.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_ctx). 19 | 20 | -export([new/0, 21 | set_value/2, 22 | set_value/3, 23 | get_value/1, 24 | get_value/2, 25 | get_value/3, 26 | remove/1, 27 | clear/0, 28 | 29 | attach/1, 30 | detach/1, 31 | get_current/0, 32 | 33 | http_extractor/1, 34 | http_extractor/2, 35 | http_injector/1, 36 | http_injector/2, 37 | http_extractor_fun/2, 38 | http_extractor_fun/3, 39 | http_injector_fun/2, 40 | http_injector_fun/3]). 41 | 42 | -type t() :: map(). 43 | -type key() :: term(). 44 | -type value() :: term(). 45 | -type token() :: reference(). 46 | 47 | -export_type([t/0, 48 | key/0, 49 | value/0]). 50 | 51 | -define(CURRENT_CTX, '$__current_otel_ctx'). 52 | 53 | -spec new() -> t(). 54 | new() -> 55 | #{}. 56 | 57 | -spec set_value(term(), term()) -> ok. 58 | set_value(Key, Value) -> 59 | erlang:put(?CURRENT_CTX, set_value(erlang:get(?CURRENT_CTX), Key, Value)). 60 | 61 | -spec set_value(t(), term(), term()) -> map(). 62 | set_value(Ctx, Key, Value) when is_map(Ctx) -> 63 | Ctx#{Key => Value}; 64 | set_value(_, Key, Value) -> 65 | #{Key => Value}. 66 | 67 | -spec get_value(term()) -> term(). 68 | get_value(Key) -> 69 | get_value(erlang:get(?CURRENT_CTX), Key, undefined). 70 | 71 | -spec get_value(term(), term()) -> term(). 72 | get_value(Key, Default) -> 73 | get_value(erlang:get(?CURRENT_CTX), Key, Default). 74 | 75 | -spec get_value(t(), term(), term()) -> term(). 76 | get_value(undefined, _Key, Default) -> 77 | Default; 78 | get_value(Ctx, Key, Default) when is_map(Ctx) -> 79 | maps:get(Key, Ctx, Default); 80 | get_value(_, _, Default) -> 81 | Default. 82 | 83 | -spec clear() -> ok. 84 | clear() -> 85 | erlang:erase(?CURRENT_CTX). 86 | 87 | -spec remove(term()) -> ok. 88 | remove(Key) -> 89 | case erlang:get(?CURRENT_CTX) of 90 | Map when is_map(Map) -> 91 | erlang:put(?CURRENT_CTX, maps:remove(Key, Map)), 92 | ok; 93 | _ -> 94 | ok 95 | end. 96 | 97 | -spec get_current() -> map(). 98 | get_current() -> 99 | case erlang:get(?CURRENT_CTX) of 100 | Map when is_map(Map) -> 101 | Map; 102 | _ -> 103 | #{} 104 | end. 105 | 106 | -spec attach(map()) -> token(). 107 | attach(Ctx) -> 108 | erlang:put(?CURRENT_CTX, Ctx). 109 | 110 | -spec detach(token()) -> ok. 111 | detach(Token) -> 112 | erlang:put(?CURRENT_CTX, Token). 113 | 114 | 115 | %% Extractor and Injector setup functions 116 | 117 | http_extractor(FromText) -> 118 | {fun ?MODULE:http_extractor_fun/2, FromText}. 119 | 120 | http_extractor_fun(Headers, FromText) -> 121 | New = FromText(Headers, ?MODULE:get_current()), 122 | ?MODULE:attach(New). 123 | 124 | http_extractor(Key, FromText) -> 125 | {fun ?MODULE:http_extractor_fun/3, {Key, FromText}}. 126 | 127 | http_extractor_fun(Headers, Key, FromText) -> 128 | New = FromText(Headers, ?MODULE:get_value(Key, #{})), 129 | ?MODULE:set_value(Key, New). 130 | 131 | http_injector(ToText) -> 132 | {fun ?MODULE:http_injector_fun/2, ToText}. 133 | 134 | http_injector_fun(Headers, ToText) -> 135 | Headers ++ ToText(Headers, ?MODULE:get_current()). 136 | 137 | http_injector(Key, ToText) -> 138 | {fun ?MODULE:http_injector_fun/3, {Key, ToText}}. 139 | 140 | http_injector_fun(Headers, Key, ToText) -> 141 | Headers ++ ToText(Headers, ?MODULE:get_value(Key, #{})). 142 | -------------------------------------------------------------------------------- /include/opentelemetry.hrl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %%%------------------------------------------------------------------------ 15 | 16 | %% These records are based on protos found in the opentelemetry-proto repo: 17 | %% src/opentelemetry/proto/trace/v1/trace.proto 18 | %% They are not exact translations because further processing is done after 19 | %% the span has finished and can be vendor specific. For example, there is 20 | %% no count of the number of dropped attributes in the span record. And 21 | %% an attribute's value can be a function to only evaluate the value if it 22 | %% is actually used (at the time of exporting). 23 | 24 | %% for use in guards: sampling bit is the first bit in 8-bit trace options 25 | -define(IS_SPAN_ENABLED(TraceOptions), (TraceOptions band 1) =/= 0). 26 | 27 | -define(SPAN_KIND_INTERNAL, 'INTERNAL'). 28 | -define(SPAN_KIND_SERVER, 'SERVER'). 29 | -define(SPAN_KIND_CLIENT, 'CLIENT'). 30 | -define(SPAN_KIND_PRODUCER, 'PRODUCER'). 31 | -define(SPAN_KIND_CONSUMER, 'CONSUMER'). 32 | 33 | -record(span_ctx, { 34 | %% 128 bit int trace id 35 | trace_id :: opentelemetry:trace_id() | undefined, 36 | %% 64 bit int span id 37 | span_id :: opentelemetry:span_id() | undefined, 38 | %% 8-bit integer, lowest bit is if it is sampled 39 | trace_flags = 1 :: integer() | undefined, 40 | %% Tracestate represents tracing-system specific context in a list of key-value pairs. 41 | %% Tracestate allows different vendors propagate additional information and 42 | %% inter-operate with their legacy Id formats. 43 | tracestate :: opentelemetry:tracestate() | undefined, 44 | %% IsValid is a boolean flag which returns true if the SpanContext has a non-zero 45 | %% TraceID and a non-zero SpanID. 46 | is_valid :: boolean() | undefined, 47 | %% true if the span context came from a remote process 48 | is_remote :: boolean() | undefined, 49 | %% this field is not propagated and is only here as an implementation optimization 50 | %% If true updates like adding events are done on the span. The same as if the 51 | %% trace flags lowest bit is 1 but simply not propagated. 52 | is_recording :: boolean() | undefined 53 | }). 54 | 55 | -record(link, { 56 | trace_id :: opentelemetry:trace_id(), 57 | span_id :: opentelemetry:span_id(), 58 | attributes :: opentelemetry:attributes(), 59 | tracestate :: opentelemetry:tracestate() 60 | }). 61 | 62 | -record(event, { 63 | system_time_nano :: non_neg_integer(), 64 | name :: unicode:unicode_binary() | atom(), 65 | attributes = [] :: opentelemetry:attributes() 66 | }). 67 | 68 | -record(status, { 69 | code :: atom() | integer(), 70 | %% developer-facing error message 71 | message :: unicode:unicode_binary() 72 | }). 73 | 74 | -define(OTEL_STATUS_OK, 'Ok'). 75 | -define(OTEL_STATUS_CANCELLED, 'Cancelled'). 76 | -define(OTEL_STATUS_UNKNOWN, 'UnknownError'). 77 | -define(OTEL_STATUS_INVALID_ARGUMENT, 'InvalidArgument'). 78 | -define(OTEL_STATUS_DEADLINE_EXCEEDED, 'DeadlineExceeded'). 79 | -define(OTEL_STATUS_NOT_FOUND, 'NotFound'). 80 | -define(OTEL_STATUS_ALREADY_EXISTS , 'AlreadyExists'). 81 | -define(OTEL_STATUS_PERMISSION_DENIED, 'PermissionDenied'). 82 | -define(OTEL_STATUS_RESOURCE_EXHAUSTED, 'ResourceExhausted'). 83 | -define(OTEL_STATUS_FAILED_PRECONDITION, 'FailedPrecondition'). 84 | -define(OTEL_STATUS_ABORTED, 'Aborted'). 85 | -define(OTEL_STATUS_OUT_OF_RANGE, 'OutOfRange'). 86 | -define(OTEL_STATUS_UNIMPLEMENTED, 'Unimplemented'). 87 | -define(OTEL_STATUS_INTERNAL, 'InternalError'). 88 | -define(OTEL_STATUS_UNAVAILABLE, 'Unavailable'). 89 | -define(OTEL_STATUS_DATA_LOSS, 'DataLoss'). 90 | -define(OTEL_STATUS_UNAUTHENTICATED, 'Unauthenticated'). 91 | -------------------------------------------------------------------------------- /src/ot_http_status.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% HTTP Status helper module. Provides functions to working with HTTP status 17 | %% codes to status records. 18 | %% @end 19 | %%%------------------------------------------------------------------------- 20 | -module(ot_http_status). 21 | 22 | -export([to_status/1, 23 | to_status/2]). 24 | 25 | -include("opentelemetry.hrl"). 26 | 27 | -type http_status_code() :: 1..599. 28 | 29 | -callback to_status(http_status_code()) -> opentelemetry:status(). 30 | -callback to_status(http_status_code(), unicode:unicode_binary()) -> opentelemetry:status(). 31 | 32 | -spec to_status(HttpStatusCode) -> opentelemetry:status() when 33 | HttpStatusCode :: http_status_code(). 34 | to_status(HttpStatusCode) -> 35 | to_status(HttpStatusCode, <<"">>). 36 | 37 | -spec to_status(HttpStatusCode, Message) -> opentelemetry:status() when 38 | HttpStatusCode :: http_status_code(), 39 | Message :: unicode:unicode_binary(). 40 | to_status(HttpStatusCode, Message) -> 41 | Status = 42 | case HttpStatusCode of 43 | Code when Code >= 100 andalso Code < 300 -> 44 | opentelemetry:status(?OTEL_STATUS_OK, Message); 45 | Code when Code >= 300 andalso Code < 400 -> 46 | opentelemetry:status(?OTEL_STATUS_OK, Message); 47 | 401 -> 48 | opentelemetry:status(?OTEL_STATUS_UNAUTHENTICATED, Message); 49 | 403 -> 50 | opentelemetry:status(?OTEL_STATUS_PERMISSION_DENIED, Message); 51 | 404 -> 52 | opentelemetry:status(?OTEL_STATUS_NOT_FOUND, Message); 53 | 412 -> 54 | opentelemetry:status(?OTEL_STATUS_FAILED_PRECONDITION, Message); 55 | 416 -> 56 | opentelemetry:status(?OTEL_STATUS_OUT_OF_RANGE, Message); 57 | 429 -> 58 | opentelemetry:status(?OTEL_STATUS_RESOURCE_EXHAUSTED, Message); 59 | Code when Code >= 400 andalso Code < 500 -> 60 | opentelemetry:status(?OTEL_STATUS_INVALID_ARGUMENT, Message); 61 | 501 -> 62 | opentelemetry:status(?OTEL_STATUS_UNIMPLEMENTED, Message); 63 | 503 -> 64 | opentelemetry:status(?OTEL_STATUS_UNAVAILABLE, Message); 65 | 504 -> 66 | opentelemetry:status(?OTEL_STATUS_DEADLINE_EXCEEDED, Message); 67 | Code when Code >= 500 -> 68 | opentelemetry:status(?OTEL_STATUS_INTERNAL, Message); 69 | _ -> 70 | opentelemetry:status(?OTEL_STATUS_UNKNOWN, Message) 71 | end, 72 | status_message(Status). 73 | 74 | status_message(#status{code = Code, message = <<"">>} = Status) -> 75 | Message = 76 | case Code of 77 | ?OTEL_STATUS_OK -> 78 | <<"Ok">>; 79 | ?OTEL_STATUS_UNKNOWN -> 80 | <<"Unknown Status">>; 81 | ?OTEL_STATUS_INVALID_ARGUMENT -> 82 | <<"Bad Argument">>; 83 | ?OTEL_STATUS_DEADLINE_EXCEEDED -> 84 | <<"Gateway Timeout">>; 85 | ?OTEL_STATUS_NOT_FOUND -> 86 | <<"Not Found">>; 87 | ?OTEL_STATUS_PERMISSION_DENIED -> 88 | <<"Forbidden">>; 89 | ?OTEL_STATUS_RESOURCE_EXHAUSTED -> 90 | <<"Too Many Requests">>; 91 | ?OTEL_STATUS_FAILED_PRECONDITION -> 92 | <<"Failed Precondition">>; 93 | ?OTEL_STATUS_OUT_OF_RANGE -> 94 | <<"Range Not Satisfiable">>; 95 | ?OTEL_STATUS_UNIMPLEMENTED -> 96 | <<"Not Implemented">>; 97 | ?OTEL_STATUS_INTERNAL -> 98 | <<"Internal Error">>; 99 | ?OTEL_STATUS_UNAVAILABLE -> 100 | <<"Service Unavailable">>; 101 | ?OTEL_STATUS_UNAUTHENTICATED -> 102 | <<"Unauthorized">> 103 | end, 104 | Status#status{message = Message}; 105 | status_message(Status) -> 106 | Status. 107 | -------------------------------------------------------------------------------- /test/opentelemetry_api_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(opentelemetry_api_SUITE). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("stdlib/include/assert.hrl"). 6 | -include_lib("common_test/include/ct.hrl"). 7 | 8 | -include("opentelemetry.hrl"). 9 | -include("tracer.hrl"). 10 | 11 | all() -> 12 | [noop_tracer, update_span_data, noop_with_span, macros, can_create_link_from_span]. 13 | 14 | init_per_suite(Config) -> 15 | application:load(opentelemetry_api), 16 | Config. 17 | 18 | end_per_suite(_Config) -> 19 | ok. 20 | 21 | can_create_link_from_span(_Config) -> 22 | %% start a span to create a link to 23 | SpanCtx = ?start_span(<<"span-1">>), 24 | 25 | %% extract individual values from span context 26 | TraceId = ot_span:trace_id(SpanCtx), 27 | SpanId = ot_span:span_id(SpanCtx), 28 | Tracestate = ot_span:tracestate(SpanCtx), 29 | 30 | %% end span, so there's no current span set 31 | ?end_span(), 32 | 33 | Attributes = [{<<"attr-1">>, <<"value-1">>}], 34 | 35 | ?assertMatch(undefined, opentelemetry:link(undefined)), 36 | ?assertMatch(undefined, opentelemetry:link(undefined, Attributes)), 37 | 38 | ?assertMatch(#link{trace_id=TraceId, 39 | span_id=SpanId, 40 | attributes=Attributes, 41 | tracestate=Tracestate}, 42 | opentelemetry:link(TraceId, SpanId, Attributes, Tracestate)), 43 | 44 | ?assertMatch(#link{trace_id=TraceId, 45 | span_id=SpanId, 46 | attributes=[], 47 | tracestate=Tracestate}, 48 | opentelemetry:link(SpanCtx)), 49 | 50 | ?assertMatch(#link{trace_id=TraceId, 51 | span_id=SpanId, 52 | attributes=Attributes, 53 | tracestate=Tracestate}, 54 | opentelemetry:link(SpanCtx, Attributes)), 55 | 56 | ?assertMatch([#link{trace_id=TraceId, 57 | span_id=SpanId, 58 | attributes=Attributes, 59 | tracestate=Tracestate}, 60 | #link{trace_id=TraceId, 61 | span_id=SpanId, 62 | attributes=[], 63 | tracestate=Tracestate}], 64 | opentelemetry:links([undefined, {SpanCtx, Attributes}, SpanCtx])). 65 | 66 | 67 | noop_tracer(_Config) -> 68 | %% start a span and 2 children 69 | SpanCtx1 = ?start_span(<<"span-1">>), 70 | SpanCtx2 = ?start_span(<<"span-2">>), 71 | SpanCtx3 = ?start_span(<<"span-3">>), 72 | 73 | %% end the 3rd span 74 | ?assertMatch(SpanCtx3, ?current_span_ctx()), 75 | ?end_span(), 76 | 77 | %% 2nd span should be the current span ctx now 78 | ?assertMatch(SpanCtx2, ?current_span_ctx()), 79 | 80 | %% start another child of the 2nd span 81 | SpanCtx4 = ?start_span(<<"span-4">>), 82 | ?assertMatch(SpanCtx4, ?current_span_ctx()), 83 | 84 | %% end 4th span and 2nd should be current 85 | ?end_span(), 86 | ?assertMatch(SpanCtx2, ?current_span_ctx()), 87 | 88 | %% end 2th span and 1st should be current 89 | ?end_span(), 90 | ?assertMatch(SpanCtx1, ?current_span_ctx()), 91 | 92 | %% end first and no span should be current ctx 93 | ?end_span(), 94 | 95 | %% always returns a noop span 96 | ?assertMatch(SpanCtx1, ?current_span_ctx()). 97 | 98 | %% just shouldn't crash 99 | update_span_data(_Config) -> 100 | Links = [#link{trace_id=0, 101 | span_id=0, 102 | attributes=[], 103 | tracestate=[]}], 104 | 105 | SpanCtx1 = ?start_span(<<"span-1">>, #{links => Links}), 106 | ?set_attribute(<<"key-1">>, <<"value-1">>), 107 | 108 | Events = opentelemetry:events([{opentelemetry:timestamp(), 109 | <<"timed-event-name">>, []}]), 110 | Status = ot_http_status:to_status(200), 111 | ?assertMatch(#status{code = ?OTEL_STATUS_OK, message = <<"Ok">>}, Status), 112 | 113 | %% with spanctx and tracer passed as an argument 114 | Tracer = opentelemetry:get_tracer(), 115 | ot_span:set_status(Tracer, SpanCtx1, Status), 116 | 117 | ot_span:add_events(Tracer, SpanCtx1, Events), 118 | 119 | ?assertMatch(SpanCtx1, ?current_span_ctx()), 120 | ?end_span(), 121 | 122 | ok. 123 | 124 | noop_with_span(_Config) -> 125 | Tracer = opentelemetry:get_tracer(), 126 | ?assertMatch({ot_tracer_noop, _}, Tracer), 127 | 128 | Result = some_result, 129 | ?assertEqual(Result, ot_tracer:with_span(Tracer, <<"span1">>, fun(_) -> Result end)), 130 | ok. 131 | 132 | macros(_Config) -> 133 | _SpanCtx1 = ?start_span(<<"span-1">>), 134 | SpanCtx2 = ?start_span(<<"span-2">>), 135 | 136 | ?assertMatch(SpanCtx2, ?current_span_ctx()), 137 | ?end_span(), 138 | 139 | %% 2nd span should be the current span ctx now 140 | ?assertMatch(SpanCtx2, ?current_span_ctx()). 141 | -------------------------------------------------------------------------------- /lib/open_telemetry/tracer.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.Tracer do 2 | @moduledoc """ 3 | This module contains macros for Tracer operations around the lifecycle of the Spans within a Trace. 4 | 5 | The Tracer is able to start a new Span as a child of the active Span of the current process, set 6 | a different Span to be the current Span by passing the Span's context, end a Span or run a code 7 | block within the context of a newly started span that is ended when the code block completes. 8 | 9 | The macros use the Tracer registered to the Application the module using the macro is included in, 10 | assuming `OpenTelemetry.register_application_tracer/1` has been called for the Application. If 11 | not then the default Tracer is used. 12 | 13 | require OpenTelemetry.Tracer 14 | 15 | OpenTelemetry.Tracer.with_span "span-1" do 16 | ... do something ... 17 | end 18 | """ 19 | 20 | @type start_opts() :: %{ 21 | optional(:parent) => OpenTelemetry.span() | OpenTelemetry.span_ctx(), 22 | optional(:attributes) => OpenTelemetry.attributes(), 23 | optional(:sampler) => :ot_sampler.sampler(), 24 | optional(:links) => OpenTelemetry.links(), 25 | optional(:is_recording) => boolean(), 26 | optional(:start_time) => :opentelemetry.timestamp(), 27 | optional(:kind) => OpenTelemetry.span_kind() 28 | } 29 | 30 | @doc """ 31 | Starts a new span and makes it the current active span of the current process. 32 | 33 | The current active Span is used as the parent of the created Span unless a `parent` is given in the 34 | `t:start_opts/0` argument or there is no active Span. If there is neither a current Span or a 35 | `parent` option given then the Tracer checks for an extracted SpanContext to use as the parent. If 36 | there is also no extracted context then the created Span is a root Span. 37 | """ 38 | defmacro start_span(name, opts \\ quote(do: %{})) do 39 | quote bind_quoted: [name: name, start_opts: opts] do 40 | :ot_tracer.start_span(:opentelemetry.get_tracer(__MODULE__), name, start_opts) 41 | end 42 | end 43 | 44 | @doc """ 45 | Starts a new span but does not make it the current active span of the current process. 46 | 47 | This is particularly useful when creating a child Span that is for a new process. Before spawning 48 | the new process start an inactive Span, which uses the current context as the parent, then 49 | pass this new SpanContext as an argument to the spawned function and in that function use 50 | `set_span/1`. 51 | 52 | The current active Span is used as the parent of the created Span unless a `parent` is given in the 53 | `t:start_opts/0` argument or there is no active Span. If there is neither a current Span or a 54 | `parent` option given then the Tracer checks for an extracted SpanContext to use as the parent. If 55 | there is also no extracted context then the created Span is a root Span. 56 | """ 57 | defmacro start_inactive_span(name, opts \\ quote(do: %{})) do 58 | quote bind_quoted: [name: name, start_opts: opts] do 59 | :ot_tracer.start_inactive_span(:opentelemetry.get_tracer(__MODULE__), name, start_opts) 60 | end 61 | end 62 | 63 | @doc """ 64 | Takes a `t:OpenTelemetry.span_ctx/0` and the Tracer sets it to the currently active Span. 65 | """ 66 | defmacro set_span(span_ctx) do 67 | quote bind_quoted: [span_ctx: span_ctx] do 68 | :ot_tracer.set_span(:opentelemetry.get_tracer(__MODULE__), span_ctx) 69 | end 70 | end 71 | 72 | @doc """ 73 | End the Span. Sets the end timestamp for the currently active Span. This has no effect on any 74 | child Spans that may exist of this Span. 75 | 76 | The default Tracer in the OpenTelemetry Erlang/Elixir SDK will then set the parent, if there 77 | is a local parent of the current Span, to the current active Span. 78 | """ 79 | defmacro end_span() do 80 | quote do 81 | :ot_tracer.end_span(:opentelemetry.get_tracer(__MODULE__)) 82 | end 83 | end 84 | 85 | @doc """ 86 | Creates a new span which is ended automatically when the `block` completes. 87 | 88 | See `start_span/2` and `end_span/0`. 89 | """ 90 | defmacro with_span(name, start_opts \\ quote(do: %{}), do: block) do 91 | quote do 92 | :ot_tracer.with_span( 93 | :opentelemetry.get_tracer(__MODULE__), 94 | unquote(name), 95 | unquote(start_opts), 96 | fn _ -> unquote(block) end 97 | ) 98 | end 99 | end 100 | 101 | @doc """ 102 | Returns the currently active `t:OpenTelemetry.tracer_ctx/0`. 103 | """ 104 | defmacro current_ctx() do 105 | quote do 106 | :ot_tracer.current_ctx(:opentelemetry.get_tracer(__MODULE__)) 107 | end 108 | end 109 | 110 | @doc """ 111 | Returns the currently active `t:OpenTelemetry.span_ctx/0`. 112 | """ 113 | defmacro current_span_ctx() do 114 | quote do 115 | :ot_tracer.current_span_ctx(:opentelemetry.get_tracer(__MODULE__)) 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/open_telemetry/span.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry.Span do 2 | @moduledoc """ 3 | This module contains macros for Span operations that update the active current Span in the current process. 4 | An example of creating an Event and adding it to the current Span: 5 | 6 | require OpenTelemetry.Span 7 | ... 8 | event = "ecto.query" 9 | ecto_attributes = OpenTelemetry.event([{"query", query}, {"total_time", total_time}]) 10 | OpenTelemetry.Span.add_event(event, ecto_attributes) 11 | ... 12 | 13 | A Span represents a single operation within a trace. Spans can be nested to form a trace tree. 14 | Each trace contains a root span, which typically describes the end-to-end latency and, optionally, 15 | one or more sub-spans for its sub-operations. 16 | 17 | Spans encapsulate: 18 | 19 | - The span name 20 | - An immutable SpanContext (`t:OpenTelemetry.span_ctx/0`) that uniquely identifies the Span 21 | - A parent Span in the form of a Span (`t:OpenTelemetry.span/0`), SpanContext (`t:OpenTelemetry.span_ctx/0`), or `undefined` 22 | - A start timestamp 23 | - An end timestamp 24 | - An ordered mapping of Attributes (`t:OpenTelemetry.attributes/0`) 25 | - A list of Links to other Spans (`t:OpenTelemetry.link/0`) 26 | - A list of timestamped Events (`t:OpenTelemetry.event/0`) 27 | - A Status (`t:OpenTelemetry.status/0`) 28 | """ 29 | 30 | @doc """ 31 | Get the SpanId of a Span. 32 | """ 33 | @spec span_id(OpenTelemetry.span_ctx()) :: OpenTelemetry.span_id() 34 | defdelegate span_id(span), to: :ot_span 35 | 36 | @doc """ 37 | Get the TraceId of a Span. 38 | """ 39 | @spec trace_id(OpenTelemetry.span_ctx()) :: OpenTelemetry.trace_id() 40 | defdelegate trace_id(span), to: :ot_span 41 | 42 | @doc """ 43 | Get the Tracestate of a Span. 44 | """ 45 | @spec tracestate(OpenTelemetry.span_ctx()) :: OpenTelemetry.tracestate() 46 | defdelegate tracestate(span), to: :ot_span 47 | 48 | @doc """ 49 | Set an attribute with key and value on the currently active Span. 50 | """ 51 | @spec set_attribute(OpenTelemetry.attribute_key(), OpenTelemetry.attribute_value()) :: boolean() 52 | defmacro set_attribute(key, value) do 53 | quote do 54 | tracer = :opentelemetry.get_tracer(__MODULE__) 55 | 56 | :ot_span.set_attribute( 57 | tracer, 58 | :ot_tracer.current_span_ctx(tracer), 59 | unquote(key), 60 | unquote(value) 61 | ) 62 | end 63 | end 64 | 65 | @doc """ 66 | Add a list of attributes to the currently active Span. 67 | """ 68 | @spec set_attributes([OpenTelemetry.attribute()]) :: boolean() 69 | defmacro set_attributes(attributes) do 70 | quote do 71 | tracer = :opentelemetry.get_tracer(__MODULE__) 72 | :ot_span.set_attributes(tracer, :ot_tracer.current_span_ctx(tracer), unquote(attributes)) 73 | end 74 | end 75 | 76 | @doc """ 77 | Add an event to the currently active Span. 78 | """ 79 | @spec add_event(String.t(), [OpenTelemetry.attribute()]) :: boolean() 80 | defmacro add_event(event, attributes) do 81 | quote do 82 | tracer = :opentelemetry.get_tracer(__MODULE__) 83 | 84 | :ot_span.add_event( 85 | tracer, 86 | :ot_tracer.current_span_ctx(tracer), 87 | unquote(event), 88 | unquote(attributes) 89 | ) 90 | end 91 | end 92 | 93 | @doc """ 94 | Add a list of events to the currently active Span. 95 | """ 96 | @spec add_events([OpenTelemetry.event()]) :: boolean() 97 | defmacro add_events(events) do 98 | quote do 99 | tracer = :opentelemetry.get_tracer(__MODULE__) 100 | :ot_span.add_events(tracer, :ot_tracer.current_span_ctx(tracer), unquote(events)) 101 | end 102 | end 103 | 104 | @doc """ 105 | Sets the Status of the currently active Span. 106 | 107 | If used, this will override the default Span Status, which is `Ok`. 108 | """ 109 | @spec set_status(OpenTelemetry.status()) :: boolean() 110 | defmacro set_status(status) do 111 | quote do 112 | tracer = :opentelemetry.get_tracer(__MODULE__) 113 | :ot_span.set_status(tracer, :ot_tracer.current_span_ctx(tracer), unquote(status)) 114 | end 115 | end 116 | 117 | @doc """ 118 | Updates the Span name. 119 | 120 | It is highly discouraged to update the name of a Span after its creation. Span name is 121 | often used to group, filter and identify the logical groups of spans. And often, filtering 122 | logic will be implemented before the Span creation for performance reasons. Thus the name 123 | update may interfere with this logic. 124 | 125 | The function name is called UpdateName to differentiate this function from the regular 126 | property setter. It emphasizes that this operation signifies a major change for a Span 127 | and may lead to re-calculation of sampling or filtering decisions made previously 128 | depending on the implementation. 129 | """ 130 | @spec update_name(String.t()) :: boolean() 131 | defmacro update_name(name) do 132 | quote do 133 | tracer = :opentelemetry.get_tracer(__MODULE__) 134 | :ot_span.update_name(tracer, :ot_tracer.current_span_ctx(tracer), unquote(name)) 135 | end 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /src/ot_tracer.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_tracer). 19 | 20 | -export([start_span/3, 21 | start_span/4, 22 | start_inactive_span/3, 23 | start_inactive_span/4, 24 | set_span/2, 25 | with_span/3, 26 | with_span/4, 27 | current_ctx/1, 28 | current_span_ctx/1, 29 | end_span/1, 30 | end_span/2]). 31 | 32 | %% tracer access functions 33 | -export([span_module/1]). 34 | 35 | -include("opentelemetry.hrl"). 36 | 37 | -type traced_fun(T) :: fun((opentelemetry:span_ctx()) -> T). 38 | -type tracer_ctx() :: term(). 39 | 40 | -export_type([traced_fun/1]). 41 | 42 | -callback start_span(opentelemetry:tracer(), 43 | opentelemetry:span_name(), 44 | ot_span:start_opts()) -> opentelemetry:span_ctx(). 45 | -callback start_span(ot_ctx:ctx(), 46 | opentelemetry:tracer(), 47 | opentelemetry:span_name(), 48 | ot_span:start_opts()) -> {opentelemetry:span_ctx(), ot_ctx:ctx()}. 49 | -callback start_inactive_span(opentelemetry:tracer(), 50 | opentelemetry:span_name(), 51 | ot_span:start_opts()) -> opentelemetry:span_ctx(). 52 | -callback start_inactive_span(ot_ctx:ctx(), 53 | opentelemetry:tracer(), 54 | opentelemetry:span_name(), 55 | ot_span:start_opts()) -> {opentelemetry:span_ctx(), ot_ctx:ctx()}. 56 | -callback set_span(opentelemetry:tracer(), opentelemetry:span_ctx()) -> ok. 57 | -callback with_span(opentelemetry:tracer(), opentelemetry:span_name(), traced_fun(T)) -> T. 58 | -callback with_span(opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts(), traced_fun(T)) -> T. 59 | -callback end_span(ot_ctx:ctx() | opentelemetry:tracer(), opentelemetry:tracer() | opentelemetry:span_ctx()) -> boolean() | {error, term()}. 60 | -callback end_span(opentelemetry:tracer()) -> boolean() | {error, term()}. 61 | -callback current_ctx(opentelemetry:tracer()) -> tracer_ctx(). 62 | -callback current_span_ctx(opentelemetry:tracer()) -> opentelemetry:span_ctx(). 63 | -callback span_module(opentelemetry:tracer()) -> module(). 64 | 65 | -spec start_span(opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts()) 66 | -> opentelemetry:span_ctx(). 67 | start_span(Tracer={Module, _}, Name, Opts) -> 68 | Module:start_span(Tracer, Name, Opts). 69 | 70 | -spec start_span(ot_ctx:ctx(), opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts()) 71 | -> {opentelemetry:span_ctx(), ot_ctx:ctx()}. 72 | start_span(Ctx, Tracer={Module, _}, Name, Opts) -> 73 | Module:start_span(Ctx, Tracer, Name, Opts). 74 | 75 | -spec start_inactive_span(opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts()) 76 | -> opentelemetry:span_ctx(). 77 | start_inactive_span(Tracer={Module, _}, Name, Opts) -> 78 | Module:start_inactive_span(Tracer, Name, Opts). 79 | 80 | -spec start_inactive_span(ot_ctx:ctx(), opentelemetry:tracer(), opentelemetry:span_name(), 81 | ot_span:start_opts()) -> {opentelemetry:span_ctx(), ot_ctx:ctx()}. 82 | start_inactive_span(Ctx, Tracer={Module, _}, Name, Opts) -> 83 | Module:start_inactive_span(Ctx, Tracer, Name, Opts). 84 | 85 | -spec set_span(opentelemetry:tracer(), opentelemetry:span_ctx()) -> ok. 86 | set_span(Tracer={Module, _}, SpanCtx) when is_atom(Module) -> 87 | Module:set_span(Tracer, SpanCtx). 88 | 89 | -spec with_span(opentelemetry:tracer(), opentelemetry:span_name(), traced_fun(T)) -> T. 90 | with_span(Tracer={Module, _}, SpanName, Fun) when is_atom(Module) -> 91 | Module:with_span(Tracer, SpanName, Fun). 92 | 93 | -spec with_span(opentelemetry:tracer(), opentelemetry:span_name(), ot_span:start_opts(), traced_fun(T)) -> T. 94 | with_span(Tracer={Module, _}, SpanName, Opts, Fun) when is_atom(Module) -> 95 | Module:with_span(Tracer, SpanName, Opts, Fun). 96 | 97 | -spec end_span(opentelemetry:tracer()) -> boolean() | {error, term()}. 98 | end_span(Tracer={Module, _}) -> 99 | Module:end_span(Tracer). 100 | 101 | -spec end_span(ot_ctx:ctx() | opentelemetry:tracer(), opentelemetry:tracer() | opentelemetry:span_ctx()) 102 | -> boolean() | {error, term()}. 103 | end_span(Ctx, Tracer={Module, _}) -> 104 | Module:end_span(Ctx, Tracer); 105 | end_span(Tracer={Module, _}, SpanCtx) -> 106 | Module:end_span(Tracer, SpanCtx). 107 | 108 | -spec current_ctx(opentelemetry:tracer()) -> ot_tracer:tracer_ctx(). 109 | current_ctx(Tracer={Module, _}) -> 110 | Module:current_ctx(Tracer). 111 | 112 | -spec current_span_ctx(opentelemetry:tracer()) -> opentelemetry:span_ctx(). 113 | current_span_ctx(Tracer={Module, _}) -> 114 | Module:current_span_ctx(Tracer). 115 | 116 | %% tracer access functions 117 | 118 | span_module(Tracer={Module, _}) -> 119 | Module:span_module(Tracer). 120 | -------------------------------------------------------------------------------- /src/ot_meter.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% @end 17 | %%%------------------------------------------------------------------------- 18 | -module(ot_meter). 19 | 20 | -include("meter.hrl"). 21 | 22 | -callback new_instrument(opentelemetry:meter(), name(), instrument_kind(), instrument_opts()) -> boolean(). 23 | -callback new_instruments(opentelemetry:meter(), [instrument_opts()]) -> boolean(). 24 | 25 | -callback record(opentelemetry:meter(), term (), number()) -> ok. 26 | -callback record(opentelemetry:meter(), name(), labels(), number()) -> ok. 27 | 28 | -callback record_batch(opentelemetry:meter(), [{instrument(), number()}], labels()) -> ok. 29 | 30 | -callback bind(opentelemetry:meter(), instrument(), labels()) -> term(). 31 | -callback release(opentelemetry:meter(), term()) -> ok. 32 | 33 | -callback register_observer(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) -> ok | unknown_instrument. 34 | -callback set_observer_callback(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) -> ok | unknown_instrument. 35 | 36 | -callback observe(ot_observer:observer_result(), number(), labels()) -> ok. 37 | 38 | -export([new_instrument/4, 39 | new_instruments/2, 40 | instrument_definition/4, 41 | bind/3, 42 | release/1, 43 | record/2, 44 | record/4, 45 | record_batch/3, 46 | register_observer/3, 47 | set_observer_callback/3, 48 | observe/3]). 49 | 50 | -type label_key() :: unicode:unicode_binary(). 51 | -type label_value() :: unicode:unicode_binary(). 52 | -type label() :: {label_key(), label_value()}. 53 | -type labels() :: [label()]. 54 | 55 | -type name() :: unicode:unicode_binary(). 56 | -type description() :: unicode:unicode_binary(). 57 | -type instrument_kind() :: module(). 58 | -type unit() :: atom(). 59 | -type number_kind() :: integer | float. 60 | 61 | -type instrument_config() :: #{description => description(), 62 | number_kind => number_kind(), 63 | unit => unit(), 64 | monotonic := boolean(), 65 | synchronous := boolean()}. 66 | 67 | -type instrument_properties() :: #{monotonic := boolean(), 68 | synchronous := boolean()}. 69 | 70 | -type instrument_opts() :: #{description => description(), 71 | number_kind => number_kind(), 72 | unit => unit()}. 73 | 74 | -type instrument_definition() :: {name(), instrument_kind(), instrument_opts()}. 75 | -type instrument() :: term(). 76 | -type bound_instrument() :: {opentelemetry:meter(), term()}. 77 | 78 | -type measurement() :: {bound_instrument() | name(), number()}. 79 | 80 | -export_type([name/0, 81 | description/0, 82 | instrument_kind/0, 83 | instrument_config/0, 84 | instrument_opts/0, 85 | number_kind/0, 86 | unit/0, 87 | measurement/0, 88 | labels/0]). 89 | 90 | -spec new_instrument(opentelemetry:meter(), name(), instrument_kind(), instrument_opts()) -> boolean(). 91 | new_instrument(Meter={Module, _}, Name, InstrumentKind, InstrumentOpts) -> 92 | Module:new_instrument(Meter, Name, InstrumentKind, InstrumentOpts). 93 | 94 | -spec new_instruments(opentelemetry:meter(), [instrument_definition()]) -> boolean(). 95 | new_instruments(Meter={Module, _}, List) -> 96 | Module:new_instruments(Meter, List). 97 | 98 | -spec instrument_definition(module(), name(), instrument_properties(), instrument_opts()) -> instrument_definition(). 99 | instrument_definition(InstrumentModule, Name, Properties, Opts) -> 100 | %% instrument config values are not allowed to be overridden so in case the user 101 | %% attempts to pass as an optiion this merge will use the config value 102 | {Name, InstrumentModule, maps:merge(Opts, Properties)}. 103 | 104 | -spec bind(opentelemetry:meter(), name(), labels()) -> bound_instrument(). 105 | bind(Meter={Module, _}, Name, Labels) -> 106 | {Meter, Module:bind(Meter, Name, Labels)}. 107 | 108 | -spec release(bound_instrument()) -> ok. 109 | release({Meter={Module, _}, BoundInstrument}) -> 110 | Module:release(Meter, BoundInstrument). 111 | 112 | -spec record(opentelemetry:meter(), name(), number(), labels()) -> ok. 113 | record(Meter={Module, _}, Name, Number, Labels) -> 114 | Module:record(Meter, Name, Labels, Number). 115 | 116 | -spec record(bound_instrument(), number()) -> ok. 117 | record({Meter={Module, _}, BoundInstrument}, Number) -> 118 | Module:record(Meter, BoundInstrument, Number). 119 | 120 | -spec record_batch(opentelemetry:meter(), labels(), [measurement()]) -> ok. 121 | record_batch(Meter={Module, _}, Labels, Measurements) -> 122 | Module:record_batch(Meter, Labels, Measurements). 123 | 124 | -spec register_observer(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) 125 | -> ok | unknown_instrument. 126 | register_observer(Meter={Module, _}, Name, Callback) -> 127 | Module:register_observer(Meter, Name, Callback). 128 | 129 | -spec set_observer_callback(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) 130 | -> ok | unknown_instrument. 131 | set_observer_callback(Meter={Module, _}, Name, Callback) -> 132 | Module:set_observer_callback(Meter, Name, Callback). 133 | 134 | -spec observe(ot_observer:observer_result(), number(), labels()) -> ok. 135 | observe({Module, Instrument}, Number, Labels) -> 136 | Module:observe(Instrument, Number, Labels). 137 | -------------------------------------------------------------------------------- /src/ot_span.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc 16 | %% Span behaviour. 17 | %% @end 18 | %%%------------------------------------------------------------------------- 19 | -module(ot_span). 20 | 21 | -export([get_ctx/2, 22 | trace_id/1, 23 | span_id/1, 24 | tracestate/1, 25 | is_recording/2, 26 | set_attribute/4, 27 | set_attributes/3, 28 | add_event/4, 29 | add_events/3, 30 | set_status/3, 31 | update_name/3]). 32 | 33 | -include("opentelemetry.hrl"). 34 | 35 | -define(is_recording(SpanCtx), SpanCtx =/= undefined andalso SpanCtx#span_ctx.is_recording =:= true). 36 | 37 | -type start_opts() :: #{attributes => opentelemetry:attributes(), 38 | sampler => term(), 39 | links => opentelemetry:links(), 40 | is_recording => boolean(), 41 | start_time => opentelemetry:timestamp(), 42 | kind => opentelemetry:span_kind()}. 43 | 44 | -export_type([start_opts/0]). 45 | 46 | -callback get_ctx(opentelemetry:span()) -> opentelemetry:span_ctx(). 47 | -callback set_attribute(opentelemetry:span_ctx(), 48 | opentelemetry:attribute_key(), 49 | opentelemetry:attribute_value()) -> boolean(). 50 | -callback set_attributes(opentelemetry:span_ctx(), opentelemetry:attributes()) -> boolean(). 51 | -callback add_event(opentelemetry:span_ctx(), unicode:unicode_binary(), opentelemetry:attributes()) -> boolean(). 52 | -callback add_events(opentelemetry:span_ctx(), opentelemetry:events()) -> boolean(). 53 | -callback set_status(opentelemetry:span_ctx(), opentelemetry:status()) -> boolean(). 54 | -callback update_name(opentelemetry:span_ctx(), opentelemetry:span_name()) -> boolean(). 55 | 56 | %% handy macros so we don't have function name typos 57 | -define(DO(Tracer, SpanCtx, Args), do_span_function(?FUNCTION_NAME, Tracer, SpanCtx, Args)). 58 | 59 | -spec get_ctx(Tracer, Span) -> SpanCtx when 60 | Tracer :: opentelemetry:tracer(), 61 | Span :: opentelemetry:span(), 62 | SpanCtx :: opentelemetry:span_ctx(). 63 | get_ctx(Tracer, Span) -> 64 | SpanModule = ot_tracer:span_module(Tracer), 65 | SpanModule:get_ctx(Span). 66 | 67 | -spec is_recording(Tracer, SpanCtx) -> boolean() when 68 | Tracer :: opentelemetry:tracer(), 69 | SpanCtx :: opentelemetry:span_ctx(). 70 | is_recording(ot_span_noop, _) -> 71 | false; 72 | is_recording(_Tracer, SpanCtx) -> 73 | ?is_recording(SpanCtx). 74 | 75 | -spec set_attribute(Tracer, SpanCtx, Key, Value) -> boolean() when 76 | Tracer :: opentelemetry:tracer(), 77 | Key :: opentelemetry:attribute_key(), 78 | Value :: opentelemetry:attribute_value(), 79 | SpanCtx :: opentelemetry:span_ctx(). 80 | set_attribute(Tracer, SpanCtx, Key, Value) when ?is_recording(SpanCtx) -> 81 | ?DO(Tracer, SpanCtx, [Key, Value]); 82 | set_attribute(_, _, _, _) -> 83 | false. 84 | 85 | -spec set_attributes(Tracer, SpanCtx, Attributes) -> boolean() when 86 | Tracer :: opentelemetry:tracer(), 87 | Attributes :: opentelemetry:attributes(), 88 | SpanCtx :: opentelemetry:span_ctx(). 89 | set_attributes(Tracer, SpanCtx, Attributes) when ?is_recording(SpanCtx) , is_list(Attributes) -> 90 | ?DO(Tracer, SpanCtx, [Attributes]); 91 | set_attributes(_, _, _) -> 92 | false. 93 | 94 | -spec add_event(Tracer, SpanCtx, Name, Attributes) -> boolean() when 95 | Tracer :: opentelemetry:tracer(), 96 | Name :: unicode:unicode_binary(), 97 | Attributes :: opentelemetry:attributes(), 98 | SpanCtx :: opentelemetry:span_ctx(). 99 | add_event(Tracer, SpanCtx, Name, Attributes) when ?is_recording(SpanCtx) -> 100 | ?DO(Tracer, SpanCtx, [Name, Attributes]); 101 | add_event(_, _, _, _) -> 102 | false. 103 | 104 | -spec add_events(Tracer, SpanCtx, Events) -> boolean() when 105 | Tracer :: opentelemetry:tracer(), 106 | Events :: opentelemetry:events(), 107 | SpanCtx :: opentelemetry:span_ctx(). 108 | add_events(Tracer, SpanCtx, Events) when ?is_recording(SpanCtx) , is_list(Events) -> 109 | ?DO(Tracer, SpanCtx, [Events]); 110 | add_events(_, _, _) -> 111 | false. 112 | 113 | -spec set_status(Tracer, SpanCtx, Status) -> boolean() when 114 | Tracer :: opentelemetry:tracer(), 115 | Status :: opentelemetry:status(), 116 | SpanCtx :: opentelemetry:span_ctx(). 117 | set_status(Tracer, SpanCtx, Status) when ?is_recording(SpanCtx) -> 118 | ?DO(Tracer, SpanCtx, [Status]); 119 | set_status(_, _, _) -> 120 | false. 121 | 122 | -spec update_name(Tracer, SpanCtx, Name) -> boolean() when 123 | Tracer :: opentelemetry:tracer(), 124 | Name :: opentelemetry:span_name(), 125 | SpanCtx :: opentelemetry:span_ctx(). 126 | update_name(Tracer, SpanCtx, SpanName) when ?is_recording(SpanCtx) -> 127 | ?DO(Tracer, SpanCtx, [SpanName]); 128 | update_name(_, _, _) -> 129 | false. 130 | 131 | %% accessors 132 | -spec trace_id(opentelemetry:span_ctx()) -> opentelemetry:trace_id(). 133 | trace_id(#span_ctx{ trace_id = TraceId }) -> TraceId. 134 | 135 | -spec span_id(opentelemetry:span_ctx()) -> opentelemetry:span_id(). 136 | span_id(#span_ctx{ span_id = SpanId }) -> SpanId. 137 | 138 | -spec tracestate(opentelemetry:span_ctx()) -> opentelemetry:tracestate(). 139 | tracestate(#span_ctx{ tracestate = Tracestate }) -> Tracestate. 140 | 141 | %% internal functions 142 | 143 | do_span_function(Function, Tracer, SpanCtx, Args) -> 144 | SpanModule = ot_tracer:span_module(Tracer), 145 | apply_span_function(SpanModule, Function, [SpanCtx | Args]). 146 | 147 | apply_span_function(ot_span_noop, _Function, _Args) -> 148 | ok; 149 | apply_span_function(SpanModule, Function, Args) -> 150 | erlang:apply(SpanModule, Function, Args). 151 | -------------------------------------------------------------------------------- /lib/open_telemetry.ex: -------------------------------------------------------------------------------- 1 | defmodule OpenTelemetry do 2 | @moduledoc """ 3 | An [OpenTelemetry](https://opentelemetry.io) Trace consists of 1 or more Spans that either have a 4 | parent/child relationship or are linked together through a Link. Each Span has a TraceId (`t:trace_id/0`), 5 | SpanId (`t:span_id/0`), and a start and end time in nanoseconds. 6 | 7 | This module provides declaration of the types used throughout the library, as well as functions for 8 | building the additional pieces of a span that are optional. Each item can be attached to individual 9 | Span using the functions in `OpenTelemetry.Span` module. 10 | 11 | ## Example 12 | 13 | require OpenTelemetry.Tracer 14 | require OpenTelemetry.Span 15 | 16 | OpenTelemetry.register_application_tracer(:this_otp_app) 17 | 18 | Tracer.start_span("some-span") 19 | ... 20 | event = "ecto.query" 21 | ecto_attributes = OpenTelemetry.event([{"query", query}, {"total_time", total_time}]) 22 | OpenTelemetry.Span.add_event(event, ecto_event) 23 | ... 24 | Tracer.end_span() 25 | """ 26 | 27 | @typedoc """ 28 | A SpanContext represents the portion of a Span needed to do operations on a 29 | Span. Within a process it acts as a key for looking up and modifying the 30 | actual Span. It is also what is serialized and propagated across process 31 | boundaries. 32 | """ 33 | @type span_ctx() :: :opentelemetry.span_ctx() 34 | 35 | @typedoc """ 36 | TracerContext refers to the data kept in process by the tracer to track 37 | the current SpanContext and the parent. 38 | """ 39 | @type tracer_ctx() :: :opentelemetry.tracer_ctx() 40 | 41 | @typedoc """ 42 | Span represents a single operation within a trace. Spans can be 43 | nested to form a trace tree. Spans may also be linked to other spans 44 | from the same or different trace and form graphs. Often, a trace 45 | contains a root span that describes the end-to-end latency, and one 46 | or more subspans for its sub-operations. A trace can also contain 47 | multiple root spans, or none at all. Spans do not need to be 48 | contiguous - there may be gaps or overlaps between spans in a trace. 49 | """ 50 | @type span() :: :opentelemetry.span() 51 | 52 | @typedoc """ 53 | TraceId is a unique identifier for a trace. All spans from the same trace share 54 | the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes 55 | is considered invalid. 56 | """ 57 | @type trace_id() :: non_neg_integer() 58 | 59 | @typedoc """ 60 | SpanId is a unique identifier for a span within a trace, assigned when the span 61 | is created. The ID is an 8-byte array. An ID with all zeroes is considered 62 | invalid. 63 | """ 64 | @type span_id() :: non_neg_integer() 65 | 66 | @type attribute_key() :: String.t() 67 | @type attribute_value() :: String.t() | integer() | float() | boolean() 68 | 69 | @typedoc """ 70 | Attributes are a collection of key/value pairs. The value can be a string, 71 | an integer, a double or the boolean values `true` or `false`. Note, global attributes 72 | like server name can be set using the resource API. 73 | 74 | Examples of attributes: 75 | 76 | [{"/http/user_agent" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"} 77 | {"/http/server_latency", 300} 78 | {"abc.com/myattribute", True} 79 | {"abc.com/score", 10.239}] 80 | """ 81 | @type attributes() :: [{attribute_key(), attribute_value()}] 82 | 83 | @typedoc """ 84 | Tracestate represents tracing-system specific context in a list of key-value pairs. 85 | Tracestate allows different vendors propagate additional information and 86 | inter-operate with their legacy Id formats. 87 | 88 | It is a tracestate in the [w3c-trace-context format](https://www.w3.org/TR/trace-context/#tracestate-header). 89 | See also [https://github.com/w3c/distributed-tracing](https://github.com/w3c/distributed-tracing) 90 | for more details about this field. 91 | """ 92 | @type tracestate() :: [{String.t(), String.t()}] 93 | 94 | @typedoc """ 95 | A Link is a pointer from the current span to another span in the same trace or in a 96 | different trace. For example, this can be used in batching operations, 97 | where a single batch handler processes multiple requests from different 98 | traces or when the handler receives a request from a different project. 99 | """ 100 | @type link() :: :opentelemetry.link() 101 | 102 | @typedoc """ 103 | An Event is a time-stamped annotation of the span, consisting of user-supplied 104 | text description and key-value pairs. 105 | """ 106 | @type event() :: :opentelemetry.event() 107 | 108 | @typedoc """ 109 | An optional final status for this span. Semantically when Status 110 | wasn't set it means span ended without errors and assume `Ok`. 111 | """ 112 | @type status() :: :opentelemetry.status() 113 | 114 | @doc """ 115 | Registering a [Named Tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#obtaining-a-tracer) with the name of an OTP Application enables each module in 116 | the Application to be mapped to the Named Tracer, named for the Application and using the 117 | version of the currently loaded Application by that name. 118 | 119 | Macros in `OpenTelemetry.Tracer` use the name of the module they are being used in in order 120 | to lookup the Named Tracer registered for that module and using it for trace operations. 121 | """ 122 | @spec register_application_tracer(atom()) :: boolean() 123 | defdelegate register_application_tracer(otp_app), to: :opentelemetry 124 | 125 | @spec register_application_meter(atom()) :: boolean() 126 | defdelegate register_application_meter(name), to: :opentelemetry 127 | 128 | # Helpers to build OpenTelemetry structured types 129 | 130 | @doc """ 131 | A monotonically increasing time provided by the Erlang runtime system in the native time unit. 132 | This value is the most accurate and precise timestamp available from the Erlang runtime and 133 | should be used for finding durations or any timestamp that can be converted to a system 134 | time before being sent to another system. 135 | 136 | Use `convert_timestamp/2` or `timestamp_to_nano/1` to convert a native monotonic time to a 137 | system time of either nanoseconds or another unit. 138 | 139 | Using these functions allows timestamps to be accurate, used for duration and be exportable 140 | as POSIX time when needed. 141 | """ 142 | @spec timestamp() :: integer() 143 | defdelegate timestamp(), to: :opentelemetry 144 | 145 | @doc """ 146 | Convert a native monotonic timestamp to nanosecond POSIX time. Meaning the time since Epoch. 147 | Epoch is defined to be 00:00:00 UTC, 1970-01-01. 148 | """ 149 | @spec timestamp_to_nano(integer()) :: integer() 150 | defdelegate timestamp_to_nano(timestamp), to: :opentelemetry 151 | 152 | @doc """ 153 | Convert a native monotonic timestamp to POSIX time of any `:erlang.time_unit/0`. 154 | Meaning the time since Epoch. Epoch is defined to be 00:00:00 UTC, 1970-01-01. 155 | """ 156 | @spec convert_timestamp(integer(), :erlang.time_unit()) :: integer() 157 | defdelegate convert_timestamp(timestamp, unit), to: :opentelemetry 158 | 159 | # span item functions 160 | 161 | @doc """ 162 | Creates a `t:link/0`. 163 | """ 164 | @spec link(trace_id(), span_id(), attributes(), tracestate()) :: link() 165 | defdelegate link(trace_id, span_id, attributes, tracestate), to: :opentelemetry 166 | 167 | @doc """ 168 | Creates a `t:link/0` from a `t:span_ctx/0`. 169 | """ 170 | @spec link(span_ctx() | :undefined) :: link() 171 | defdelegate link(span_ctx), to: :opentelemetry 172 | 173 | @doc """ 174 | Creates a `t:link/0` from a `t:span_ctx/0` and list of `t:attributes/0`. 175 | """ 176 | @spec link(span_ctx() | :undefined, attributes()) :: link() 177 | defdelegate link(span_ctx, attributes), to: :opentelemetry 178 | 179 | @doc """ 180 | Creates a list of `t:link/0` from a list of 4-tuples. 181 | """ 182 | @spec links([ 183 | {integer(), integer(), attributes(), tracestate()} 184 | | span_ctx() 185 | | {span_ctx(), attributes()} 186 | ]) :: [link()] 187 | defdelegate links(link_list), to: :opentelemetry 188 | 189 | @doc """ 190 | Creates a `t:event/0`. 191 | """ 192 | @spec event(String.t(), attributes()) :: event() 193 | defdelegate event(name, attributes), to: :opentelemetry 194 | 195 | @doc """ 196 | Creates a `t:event/0`. 197 | """ 198 | @spec event(integer(), String.t(), attributes()) :: event() 199 | defdelegate event(timestamp, name, attributes), to: :opentelemetry 200 | 201 | @doc """ 202 | Creates a list of `t:event/0` items. 203 | """ 204 | @spec events(list()) :: [event()] 205 | defdelegate events(event_list), to: :opentelemetry 206 | 207 | @doc """ 208 | Creates a Status. 209 | """ 210 | @spec status(atom(), String.t()) :: status() 211 | defdelegate status(code, message), to: :opentelemetry 212 | end 213 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/opentelemetry.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------------ 2 | %% Copyright 2019, OpenTelemetry Authors 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | %% 15 | %% @doc The types defined here, and referencing records in opentelemetry.hrl 16 | %% are used to store trace information while being collected on the 17 | %% Erlang node. 18 | %% 19 | %% Thus, while the types are based on protos found in the opentelemetry-proto 20 | %% repo: src/opentelemetry/proto/trace/v1/trace.proto, 21 | %% they are not exact translations because further processing is done after 22 | %% the span has finished and can be vendor specific. For example, there is 23 | %% no count of the number of dropped attributes in the span record. And 24 | %% an attribute's value can be a function to only evaluate the value if it 25 | %% is actually used (at the time of exporting). And the stacktrace is a 26 | %% regular Erlang stack trace. 27 | %% @end 28 | %%%------------------------------------------------------------------------- 29 | -module(opentelemetry). 30 | 31 | -export([set_default_tracer/1, 32 | set_tracer/2, 33 | set_meter/2, 34 | set_default_meter/1, 35 | register_tracer/2, 36 | register_application_tracer/1, 37 | register_meter/2, 38 | register_application_meter/1, 39 | get_tracer/0, 40 | get_tracer/1, 41 | get_meter/0, 42 | get_meter/1, 43 | set_http_extractor/1, 44 | get_http_extractor/0, 45 | set_http_injector/1, 46 | get_http_injector/0, 47 | timestamp/0, 48 | timestamp_to_nano/1, 49 | convert_timestamp/2, 50 | links/1, 51 | link/1, 52 | link/2, 53 | link/4, 54 | event/2, 55 | event/3, 56 | events/1, 57 | status/2, 58 | generate_trace_id/0, 59 | generate_span_id/0]). 60 | 61 | -include("opentelemetry.hrl"). 62 | -include_lib("kernel/include/logger.hrl"). 63 | 64 | -export_type([tracer/0, 65 | meter/0, 66 | trace_id/0, 67 | span_id/0, 68 | timestamp/0, 69 | span_name/0, 70 | span_ctx/0, 71 | span/0, 72 | span_kind/0, 73 | link/0, 74 | links/0, 75 | attribute_key/0, 76 | attribute_value/0, 77 | attributes/0, 78 | event/0, 79 | events/0, 80 | stack_trace/0, 81 | tracestate/0, 82 | status/0, 83 | resource/0, 84 | http_headers/0]). 85 | 86 | -type tracer() :: {module(), term()}. 87 | -type meter() :: {module(), term()}. 88 | 89 | -type trace_id() :: non_neg_integer(). 90 | -type span_id() :: non_neg_integer(). 91 | 92 | -type timestamp() :: integer(). 93 | 94 | -type span_ctx() :: #span_ctx{}. 95 | -type span() :: term(). 96 | -type span_name() :: unicode:unicode_binary() | atom(). 97 | 98 | -type attribute_key() :: unicode:unicode_binary() | atom(). 99 | -type attribute_value() :: any(). 100 | -type attribute() :: {attribute_key(), attribute_value()}. 101 | -type attributes() :: [attribute()]. 102 | 103 | -type span_kind() :: ?SPAN_KIND_INTERNAL | 104 | ?SPAN_KIND_SERVER | 105 | ?SPAN_KIND_CLIENT | 106 | ?SPAN_KIND_PRODUCER | 107 | ?SPAN_KIND_CONSUMER. 108 | -type event() :: #event{}. 109 | -type events() :: [#event{}]. 110 | -type link() :: #link{}. 111 | -type links() :: [#link{}]. 112 | -type status() :: #status{}. 113 | 114 | %% The key must begin with a lowercase letter, and can only contain 115 | %% lowercase letters 'a'-'z', digits '0'-'9', underscores '_', dashes 116 | %% '-', asterisks '*', and forward slashes '/'. 117 | %% The value is opaque string up to 256 characters printable ASCII 118 | %% RFC0020 characters (i.e., the range 0x20 to 0x7E) except ',' and '='. 119 | %% Note that this also excludes tabs, newlines, carriage returns, etc. 120 | -type tracestate() :: [{unicode:latin1_chardata(), unicode:latin1_chardata()}]. 121 | 122 | -type stack_trace() :: [erlang:stack_item()]. 123 | 124 | -type resource() :: #{unicode:unicode_binary() => unicode:unicode_binary()}. 125 | 126 | -type http_headers() :: [{unicode:unicode_binary(), unicode:unicode_binary()}]. 127 | 128 | -spec set_default_tracer(tracer()) -> boolean(). 129 | set_default_tracer(Tracer) -> 130 | verify_and_set_term(Tracer, default_tracer, ot_tracer). 131 | 132 | -spec set_tracer(atom(), tracer()) -> boolean(). 133 | set_tracer(Name, Tracer) -> 134 | verify_and_set_term(Tracer, Name, ot_tracer). 135 | 136 | -spec set_default_meter(meter()) -> boolean(). 137 | set_default_meter(Meter) -> 138 | verify_and_set_term(Meter, default_meter, ot_meter). 139 | 140 | -spec set_meter(atom(), meter()) -> boolean(). 141 | set_meter(Name, Meter) -> 142 | verify_and_set_term(Meter, Name, ot_meter). 143 | 144 | -spec register_tracer(atom(), string()) -> boolean(). 145 | register_tracer(Name, Vsn) -> 146 | ot_tracer_provider:register_tracer(Name, Vsn). 147 | 148 | -spec register_application_tracer(atom()) -> boolean(). 149 | register_application_tracer(Name) -> 150 | ot_tracer_provider:register_application_tracer(Name). 151 | 152 | -spec register_meter(atom(), string()) -> boolean(). 153 | register_meter(Name, Vsn) -> 154 | ot_meter_provider:register_meter(Name, Vsn). 155 | 156 | -spec register_application_meter(atom()) -> boolean(). 157 | register_application_meter(Name) -> 158 | ot_meter_provider:register_application_meter(Name). 159 | 160 | -spec get_tracer() -> tracer(). 161 | get_tracer() -> 162 | persistent_term:get({?MODULE, default_tracer}, {ot_tracer_noop, []}). 163 | 164 | -spec get_tracer(atom()) -> tracer(). 165 | get_tracer(Name) -> 166 | persistent_term:get({?MODULE, Name}, get_tracer()). 167 | 168 | -spec get_meter() -> meter(). 169 | get_meter() -> 170 | persistent_term:get({?MODULE, default_meter}, {ot_meter_noop, []}). 171 | 172 | -spec get_meter(atom()) -> meter(). 173 | get_meter(Name) -> 174 | persistent_term:get({?MODULE, Name}, get_meter()). 175 | 176 | set_http_extractor(List) when is_list(List) -> 177 | persistent_term:put({?MODULE, http_extractor}, List); 178 | set_http_extractor(_) -> 179 | ok. 180 | 181 | set_http_injector(List) when is_list(List) -> 182 | persistent_term:put({?MODULE, http_injector}, List); 183 | set_http_injector(_) -> 184 | ok. 185 | 186 | get_http_extractor() -> 187 | persistent_term:get({?MODULE, http_extractor}, []). 188 | 189 | get_http_injector() -> 190 | persistent_term:get({?MODULE, http_injector}, []). 191 | 192 | %% @doc A monotonically increasing time provided by the Erlang runtime system in the native time unit. 193 | %% This value is the most accurate and precise timestamp available from the Erlang runtime and 194 | %% should be used for finding durations or any timestamp that can be converted to a system 195 | %% time before being sent to another system. 196 | 197 | %% Use {@link convert_timestamp/2} or {@link timestamp_to_nano/1} to convert a native monotonic time to a 198 | %% system time of either nanoseconds or another {@link erlang:time_unit()}. 199 | 200 | %% Using these functions allows timestamps to be accurate, used for duration and be exportable 201 | %% as POSIX time when needed. 202 | %% @end 203 | -spec timestamp() -> integer(). 204 | timestamp() -> 205 | erlang:monotonic_time(). 206 | 207 | %% @doc Convert a native monotonic timestamp to nanosecond POSIX time. Meaning the time since Epoch. 208 | %% Epoch is defined to be 00:00:00 UTC, 1970-01-01. 209 | %% @end 210 | -spec timestamp_to_nano(timestamp()) -> integer(). 211 | timestamp_to_nano(Timestamp) -> 212 | convert_timestamp(Timestamp, nanosecond). 213 | 214 | %% @doc Convert a native monotonic timestamp to POSIX time of any {@link erlang:time_unit()}. 215 | %% Meaning the time since Epoch. Epoch is defined to be 00:00:00 UTC, 1970-01-01. 216 | %% @end 217 | -spec convert_timestamp(timestamp(), erlang:time_unit()) -> integer(). 218 | convert_timestamp(Timestamp, Unit) -> 219 | Offset = erlang:time_offset(), 220 | erlang:convert_time_unit(Timestamp + Offset, native, Unit). 221 | 222 | -spec links([{TraceId, SpanId, Attributes, TraceState} | span_ctx() | {span_ctx(), Attributes}]) -> links() when 223 | TraceId :: trace_id(), 224 | SpanId :: span_id(), 225 | Attributes :: attributes(), 226 | TraceState :: tracestate(). 227 | links(List) when is_list(List) -> 228 | lists:filtermap(fun({TraceId, SpanId, Attributes, TraceState}) when is_integer(TraceId) , 229 | is_integer(SpanId) , 230 | is_list(Attributes) , 231 | is_list(TraceState) -> 232 | link_or_false(TraceId, SpanId, Attributes, TraceState); 233 | ({#span_ctx{trace_id=TraceId, 234 | span_id=SpanId, 235 | tracestate=TraceState}, Attributes}) when is_integer(TraceId) , 236 | is_integer(SpanId) , 237 | is_list(Attributes) , 238 | is_list(TraceState) -> 239 | link_or_false(TraceId, SpanId, Attributes, TraceState); 240 | (#span_ctx{trace_id=TraceId, 241 | span_id=SpanId, 242 | tracestate=TraceState}) when is_integer(TraceId) , 243 | is_integer(SpanId) , 244 | is_list(TraceState) -> 245 | link_or_false(TraceId, SpanId, [], TraceState); 246 | (_) -> 247 | false 248 | end, List); 249 | links(_) -> 250 | []. 251 | 252 | -spec link(span_ctx() | undefined) -> link(). 253 | link(SpanCtx) -> 254 | link(SpanCtx, []). 255 | 256 | -spec link(span_ctx() | undefined, attributes()) -> link(). 257 | link(#span_ctx{trace_id=TraceId, 258 | span_id=SpanId, 259 | tracestate=TraceState}, Attributes) -> 260 | ?MODULE:link(TraceId, SpanId, Attributes, TraceState); 261 | link(_, _) -> 262 | undefined. 263 | 264 | -spec link(TraceId, SpanId, Attributes, TraceState) -> link() | undefined when 265 | TraceId :: trace_id(), 266 | SpanId :: span_id(), 267 | Attributes :: attributes(), 268 | TraceState :: tracestate(). 269 | link(TraceId, SpanId, Attributes, TraceState) when is_integer(TraceId), 270 | is_integer(SpanId), 271 | is_list(Attributes), 272 | is_list(TraceState) -> 273 | #link{trace_id=TraceId, 274 | span_id=SpanId, 275 | attributes=Attributes, 276 | tracestate=TraceState}; 277 | link(_, _, _, _) -> 278 | undefined. 279 | 280 | -spec event(Name, Attributes) -> event() | undefined when 281 | Name :: unicode:unicode_binary(), 282 | Attributes :: attributes(). 283 | event(Name, Attributes) when is_binary(Name), 284 | is_list(Attributes) -> 285 | event(erlang:system_time(nanosecond), Name, Attributes); 286 | event(_, _) -> 287 | undefined. 288 | 289 | -spec event(Timestamp, Name, Attributes) -> event() | undefined when 290 | Timestamp :: non_neg_integer(), 291 | Name :: unicode:unicode_binary(), 292 | Attributes :: attributes(). 293 | event(Timestamp, Name, Attributes) when is_integer(Timestamp), 294 | is_binary(Name), 295 | is_list(Attributes) -> 296 | #event{system_time_nano=Timestamp, 297 | name=Name, 298 | attributes=Attributes}; 299 | event(_, _, _) -> 300 | undefined. 301 | 302 | events(List) -> 303 | Timestamp = timestamp(), 304 | lists:filtermap(fun({Time, Name, Attributes}) when is_binary(Name), 305 | is_list(Attributes) -> 306 | case event(Time, Name, Attributes) of 307 | undefined -> 308 | false; 309 | Event -> 310 | {true, Event} 311 | end; 312 | ({Name, Attributes}) when is_binary(Name), 313 | is_list(Attributes) -> 314 | case event(Timestamp, Name, Attributes) of 315 | undefined -> 316 | false; 317 | Event -> 318 | {true, Event} 319 | end; 320 | (_) -> 321 | false 322 | end, List). 323 | 324 | -spec status(Code, Message) -> status() | undefined when 325 | Code :: atom(), 326 | Message :: unicode:unicode_binary(). 327 | status(Code, Message) when is_atom(Code), 328 | is_binary(Message) -> 329 | #status{code=Code, 330 | message=Message}; 331 | status(_, _) -> 332 | undefined. 333 | 334 | %%-------------------------------------------------------------------- 335 | %% @doc 336 | %% Generates a 128 bit random integer to use as a trace id. 337 | %% @end 338 | %%-------------------------------------------------------------------- 339 | -spec generate_trace_id() -> trace_id(). 340 | generate_trace_id() -> 341 | uniform(2 bsl 127 - 1). %% 2 shifted left by 127 == 2 ^ 128 342 | 343 | %%-------------------------------------------------------------------- 344 | %% @doc 345 | %% Generates a 64 bit random integer to use as a span id. 346 | %% @end 347 | %%-------------------------------------------------------------------- 348 | -spec generate_span_id() -> span_id(). 349 | generate_span_id() -> 350 | uniform(2 bsl 63 - 1). %% 2 shifted left by 63 == 2 ^ 64 351 | 352 | uniform(X) -> 353 | rand:uniform(X). 354 | 355 | %% internal functions 356 | 357 | -spec verify_and_set_term(module() | {module(), term()}, term(), atom()) -> boolean(). 358 | verify_and_set_term(Module, TermKey, Behaviour) -> 359 | case verify_behaviour(Module, Behaviour) of 360 | true -> 361 | persistent_term:put({?MODULE, TermKey}, Module), 362 | true; 363 | false -> 364 | ?LOG_WARNING("Module ~p does not implement behaviour ~p. " 365 | "A noop ~p will be used until a module implementing " 366 | "the behaviour is configured.", 367 | [Module, Behaviour, Behaviour]), 368 | false 369 | end. 370 | 371 | -spec verify_behaviour(module() | {module(), term()}, atom()) -> boolean(). 372 | verify_behaviour({Module, _}, Behaviour) -> 373 | verify_behaviour(Module, Behaviour); 374 | verify_behaviour(Module, Behaviour) -> 375 | try Module:module_info(attributes) of 376 | Attributes -> 377 | case lists:keyfind(behaviour, 1, Attributes) of 378 | {behaviour, Behaviours} -> 379 | lists:member(Behaviour, Behaviours); 380 | _ -> 381 | false 382 | end 383 | catch 384 | error:undef -> 385 | false 386 | end. 387 | 388 | %% for use in a filtermap 389 | %% return {true, Link} if a link is returned or return false 390 | link_or_false(TraceId, SpanId, Attributes, TraceState) -> 391 | case link(TraceId, SpanId, Attributes, TraceState) of 392 | Link=#link{} -> 393 | {true, Link}; 394 | _ -> 395 | false 396 | end. 397 | --------------------------------------------------------------------------------