Skip to content

Commit

Permalink
wip: inital api impl
Browse files Browse the repository at this point in the history
  • Loading branch information
tsloughter committed Jul 25, 2019
1 parent cbed4fe commit a2ff481
Show file tree
Hide file tree
Showing 15 changed files with 934 additions and 11 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ OpenTelemetry

OpenTelemetry stats collection and distributed tracing framework for Erlang.

## Design

## Contributing

137 changes: 137 additions & 0 deletions include/opentelemetry.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
%%%------------------------------------------------------------------------
%% Copyright 2019, OpenTelemetry Authors
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%%------------------------------------------------------------------------

%% These records are based on protos found in the opentelemetry-proto repo:
%% src/opentelemetry/proto/trace/v1/trace.proto
%% They are not exact translations because further processing is done after
%% the span has finished and can be vendor specific. For example, there is
%% no count of the number of dropped attributes in the span record. And
%% an attribute's value can be a function to only evaluate the value if it
%% is actually used (at the time of exporting).

%% for use in guards: sampling bit is the first bit in 8-bit trace options
-define(IS_SPAN_ENABLED(X), (X band 1) =/= 0).

-define(MESSAGE_EVENT_TYPE_UNSPECIFIED, 'TYPE_UNSPECIFIED').
-define(MESSAGE_EVENT_TYPE_SENT, 'SENT').
-define(MESSAGE_EVENT_TYPE_RECEIVED, 'RECEIVED').

-define(SPAN_KIND_UNSPECIFIED, 'SPAN_KIND_UNSPECIFIED').
-define(SPAN_KIND_INTERNAL, 'INTERNAL').
-define(SPAN_KIND_SERVER, 'SERVER').
-define(SPAN_KIND_CLIENT, 'CLIENT').
-define(SPAN_KIND_PRODUCER, 'PRODUCER').
-define(SPAN_KIND_CONSUMER, 'CONSUMER').

-record(span_ctx, {
%% 128 bit int trace id
trace_id :: opentelemetry:trace_id() | undefined,
%% 64 bit int span id
span_id :: opentelemetry:span_id() | undefined,
%% 8-bit integer, lowest bit is if it is sampled
trace_options = 1 :: integer() | undefined,
%% Tracestate represents tracing-system specific context in a list of key-value pairs.
%% Tracestate allows different vendors propagate additional information and
%% inter-operate with their legacy Id formats.
tracestate :: opentelemetry:tracestate() | undefined,
%% IsValid is a boolean flag which returns true if the SpanContext has a non-zero
%% TraceID and a non-zero SpanID.
is_valid :: boolean() | undefined
}).

-record(span, {
%% name of the span
name :: unicode:unicode_binary(),

%% 128 bit int trace id
trace_id :: opentelemetry:trace_id() | undefined,

%% 64 bit int span id
span_id :: opentelemetry:span_id() | undefined,
%% 64 bit int parent span
parent_span_id :: opentelemetry:span_id() | undefined,

tracestate :: opentelemetry:tracestate() | undefined,

%% 8-bit integer, lowest bit is if it is sampled
trace_options = 1 :: integer() | undefined,

kind = ?SPAN_KIND_UNSPECIFIED :: opentelemetry:span_kind() | undefined,

start_time :: wts:timestamp(),
end_time :: wts:timestamp() | undefined,

%% A set of attributes on the span.
%% Kept as a list so ets:select_replace/2 can be used to add new elements
attributes = [] :: opentelemetry:attributes() | undefined,

%% optional stacktrace from where the span was started
stack_trace :: opentelemetry:stack_trace() | undefined,

%% A time-stamped annotation or message event in the Span.
time_events = [] :: opentelemetry:time_events(),

%% links to spans in other traces
links = [] :: opentelemetry:links(),

%% An optional final status for this span.
status :: opentelemetry:status() | undefined,

%% An optional resource that is associated with this span. If not set, this span
%% should be part of a batch that does include the resource information, unless resource
%% information is unknown.
resource :: opentelemetry:resource() | undefined,

%% A highly recommended but not required flag that identifies when a trace
%% crosses a process boundary. True when the parent_span belongs to the
%% same process as the current span.
same_process_as_parent_span = undefined :: boolean() | undefined,

%% An optional number of child spans that were generated while this span
%% was active. If set, allows implementation to detect missing child spans.
child_span_count = undefined :: integer() | undefined
}).

-record(link, {
trace_id :: opentelemetry:trace_id(),
span_id :: opentelemetry:span_id(),
attributes :: opentelemetry:attributes(),
tracestate :: opentelemetry:tracestate()
}).

-record(message_event, {
%% type of MessageEvent. Indicates whether the RPC message was sent or received.
type = 'TYPE_UNSPECIFIED' :: opentelemetry:message_event_type(),

%% identifier for the message, which must be unique in this span.
id :: integer(),

%% number of uncompressed bytes sent or received
uncompressed_size :: integer(),

%% number of compressed bytes sent or received
compressed_size :: integer()
}).

-record(annotation, {
description :: unicode:unicode_binary() | undefined,
attributes :: opentelemetry:attributes() | undefined
}).

-record(status, {
code :: integer(),
%% developer-facing error message
message :: unicode:unicode_binary()
}).
124 changes: 124 additions & 0 deletions src/opentelemetry.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
%%%------------------------------------------------------------------------
%% Copyright 2019, OpenTelemetry Authors
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% @doc The types defined here, and referencing records in opentelemetry.hrl
%% are used to store trace information while being collected on the
%% Erlang node.
%%
%% Thus, while the types are based on protos found in the opentelemetry-proto
%% repo: src/opentelemetry/proto/trace/v1/trace.proto,
%% they are not exact translations because further processing is done after
%% the span has finished and can be vendor specific. For example, there is
%% no count of the number of dropped attributes in the span record. And
%% an attribute's value can be a function to only evaluate the value if it
%% is actually used (at the time of exporting). And the stacktrace is a
%% regular Erlang stack trace.
%% @end
%%%-------------------------------------------------------------------------
-module(opentelemetry).

-export([generate_trace_id/0,
generate_span_id/0]).

-include("opentelemetry.hrl").

-export_type([trace_id/0,
span_id/0,
span_name/0,
span_ctx/0,
span/0,
span_kind/0,
link/0,
links/0,
attributes/0,
annotation/0,
time_events/0,
message_event/0,
message_event_type/0,
stack_trace/0,
tracestate/0,
status/0,
resource/0,
http_headers/0]).

-type trace_id() :: non_neg_integer().
-type span_id() :: non_neg_integer().

-type span_ctx() :: #span_ctx{}.
-type span() :: #span{}.
-type span_name() :: unicode:unicode_binary().

-type attribute_value() :: any().
-type attributes() :: [{unicode:unicode_binary(), attribute_value()}].

-type annotation() :: #annotation{}.
-type span_kind() :: ?SPAN_KIND_INTERNAL |
?SPAN_KIND_SERVER |
?SPAN_KIND_CLIENT.
-type message_event() :: #message_event{}.
-type message_event_type() :: ?MESSAGE_EVENT_TYPE_UNSPECIFIED |
?MESSAGE_EVENT_TYPE_SENT |
?MESSAGE_EVENT_TYPE_RECEIVED.
-type time_events() :: [{wts:timestamp(), annotation() | message_event()}].
-type link() :: #link{}.
-type links() :: [#link{}].
-type status() :: #status{}.

%% The key must begin with a lowercase letter, and can only contain
%% lowercase letters 'a'-'z', digits '0'-'9', underscores '_', dashes
%% '-', asterisks '*', and forward slashes '/'.
%% The value is opaque string up to 256 characters printable ASCII
%% RFC0020 characters (i.e., the range 0x20 to 0x7E) except ',' and '='.
%% Note that this also excludes tabs, newlines, carriage returns, etc.
-type tracestate() :: [{unicode:latin1_chardata(), unicode:latin1_chardata()}].

-type stack_trace() :: [erlang:stack_item()].

-type resource() :: #{unicode:unicode_binary() => unicode:unicode_binary()}.

-type http_headers() :: [{unicode:unicode_binary(), unicode:unicode_binary()}].

%%--------------------------------------------------------------------
%% @doc
%% Generates a 128 bit random integer to use as a trace id.
%% @end
%%--------------------------------------------------------------------
-spec generate_trace_id() -> trace_id().
generate_trace_id() ->
uniform(2 bsl 127 - 1). %% 2 shifted left by 127 == 2 ^ 128

%%--------------------------------------------------------------------
%% @doc
%% Generates a 64 bit random integer to use as a span id.
%% @end
%%--------------------------------------------------------------------
-spec generate_span_id() -> span_id().
generate_span_id() ->
uniform(2 bsl 63 - 1). %% 2 shifted left by 63 == 2 ^ 64

%% Before OTP-20 rand:uniform could not give precision higher than 2^56.
%% Here we do a compile time check for support of this feature and will
%% combine multiple calls to rand if on an OTP version older than 20.0
-ifdef(OTP_RELEASE).
uniform(X) ->
rand:uniform(X).
-else.
-define(TWO_POW_56, 2 bsl 55).

uniform(X) when X =< ?TWO_POW_56 ->
rand:uniform(X);
uniform(X) ->
R = rand:uniform(?TWO_POW_56),
(uniform(X bsr 56) bsl 56) + R.
-endif.
3 changes: 2 additions & 1 deletion src/opentelemetry_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
-export([start/2, stop/1]).

start(_StartType, _StartArgs) ->
opentelemetry_sup:start_link().
Opts = application:get_all_env(opentelemetry),
opentelemetry_sup:start_link(Opts).

stop(_State) ->
ok.
Expand Down
13 changes: 7 additions & 6 deletions src/opentelemetry_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@

-behaviour(supervisor).

-export([start_link/0]).
-export([start_link/1]).

-export([init/1]).

-define(SERVER, ?MODULE).

start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_link(Opts) ->
supervisor:start_link({local, ?SERVER}, ?MODULE, [Opts]).

%% sup_flags() = #{strategy => strategy(), % optional
%% intensity => non_neg_integer(), % optional
Expand All @@ -37,11 +37,12 @@ start_link() ->
%% shutdown => shutdown(), % optional
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
SupFlags = #{strategy => one_for_all,
init([Opts]) ->
SupFlags = #{strategy => one_for_one,
intensity => 0,
period => 1},
ChildSpecs = [],
ChildSpecs = [#{id => ot_span_ets,
start => {ot_span_ets, start_link, [Opts]}}],
{ok, {SupFlags, ChildSpecs}}.

%% internal functions
22 changes: 22 additions & 0 deletions src/ot_ctx.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
%%%------------------------------------------------------------------------
%% Copyright 2019, OpenTelemetry Authors
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% @doc
%% @end
%%%-------------------------------------------------------------------------
-module(ot_ctx).

-callback get(any()) -> any().
-callback with_value(any(), any()) -> ok.
-callback with_value(any(), any(), fun()) -> ok.
39 changes: 39 additions & 0 deletions src/ot_ctx_pdict.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
%%%------------------------------------------------------------------------
%% Copyright 2019, OpenTelemetry Authors
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% @doc
%% @end
%%%-------------------------------------------------------------------------
-module(ot_ctx_pdict).

-behaviour(ot_ctx).

-export([with_value/2,
with_value/3,
get/1]).

get(Key) ->
erlang:get(Key).

with_value(Key, Value) ->
erlang:put(Key, Value).

with_value(Key, Value, Fun) ->
Orig = erlang:get(Key),
try
erlang:put(Key, Value),
Fun()
after
erlang:put(Key, Orig)
end.
Loading

0 comments on commit a2ff481

Please sign in to comment.