Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inital api impl #4

Merged
merged 1 commit into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ OpenTelemetry

OpenTelemetry stats collection and distributed tracing framework for Erlang.

Requires OTP 21.3 of above.

## 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()
}).
111 changes: 111 additions & 0 deletions src/opentelemetry.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
%%%------------------------------------------------------------------------
%% 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

uniform(X) ->
rand:uniform(X).
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
23 changes: 8 additions & 15 deletions src/opentelemetry_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,22 @@

-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
%% period => pos_integer()} % optional
%% child_spec() = #{id => child_id(), % mandatory
%% start => mfargs(), % mandatory
%% restart => restart(), % optional
%% 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_sup,
start => {ot_span_sup, start_link, [Opts]},
type => supervisor}],
{ok, {SupFlags, ChildSpecs}}.

%% internal functions
46 changes: 46 additions & 0 deletions src/ot_ctx.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
%%%------------------------------------------------------------------------
%% 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).

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

-callback get(term()) -> term().
-callback get(term(), term()) -> term().
-callback with_value(term(), term()) -> ok.
-callback with_value(term(), term(), fun()) -> ok.
tsloughter marked this conversation as resolved.
Show resolved Hide resolved

-define(ctx, (persistent_term:get({opentelemetry, ctx}, ot_ctx_pdict))).

-spec get(term()) -> term().
get(Key) ->
?ctx:get(Key).

-spec get(term(), term()) -> term().
get(Key, Default) ->
?ctx:get(Key, Default).

-spec with_value(term(), term()) -> ok.
with_value(Key, Value) ->
?ctx:with_value(Key, Value).

-spec with_value(term(), term(), fun()) -> ok.
with_value(Key, Value, Fun) ->
?ctx:with_value(Key, Value, Fun).
Loading