Skip to content

Commit

Permalink
otel resource implementation, including resources from OS vars
Browse files Browse the repository at this point in the history
  • Loading branch information
tsloughter committed Jan 31, 2020
1 parent 11d12a2 commit 9341b47
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 5 deletions.
72 changes: 72 additions & 0 deletions include/ot_resource.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
%% if any `service' attribute is defined then
%% `service.name' and `service.instance.id' are required

%% The name of the telemetry library.
-define(SERVICE_NAME, "service.name").
%% The string ID of the service instance. MUST be unique for each instance of the same
%% `service.namespace,service.name` pair.
-define(SERVICE_INSTANCE, "service.instance.id").
%% A namespace for `service.name`. A string value having a meaning that helps to distinguish a
%% group of services,
-define(SERVICE_NAMESPACE, "service.namespace").
%% The version string of the service API or implementation.
-define(SERVICE_VERSION, "service.version").

%% Library attributes

%% The name of the telemetry library.
-define(LIBRARY_NAME, "library.name").
%% The language of telemetry library and of the code instrumented with it.
-define(LIBRARY_LANGUAGE, "library.language").
%% The version string of the library.
-define(LIBRARY_VERSION, "library.version").

%% Container attributes

%% Container name.
-define(CONTAINER_NAME, "container.name").
%% Name of the image the container was built on.
-define(CONTAINER_IMAGE_NAME, "container.image.name").
%% Container image tag.
-define(CONTAINER_IMAGE_TAG, "container.image.tag").

%% K8s attributes

%% The name of the cluster that the pod is running in.
-define(K8S_CLUSTER, "k8s.cluster.name").
%% The name of the namespace that the pod is running in.
-define(K8S_NAMESPACE, "k8s.namespace.name").
%% The name of the pod.
-define(K8S_POD, "k8s.pod.name").
%% The name of the deployment.
-define(K8S_DEPLOYMENT, "k8s.deployment.name").


%% Host attributes

%% Hostname of the host. It contains what the `hostname` command returns on the host machine.
-define(HOST_HOSTNAME, "host.hostname").
%% Unique host id. For Cloud this must be the instance_id assigned by the cloud provider.
-define(HOST_ID, "host.id").
%% Name of the host. It may contain what `hostname` returns on Unix systems, the fully qualified,
%% or a name specified by the user.
-define(HOST_NAME, "host.name").
%% Type of host. For Cloud this must be the machine type.
-define(HOST_TYPE, "host.type").
%% Name of the VM image or OS install the host was instantiated from.
-define(HOST_IMAGE_NAME, "host.image.name").
%% VM image id. For Cloud, this value is from the provider.
-define(HOST_IMAGE_ID, "host.image.id").
%% The version string of the VM image.
-define(HOST_IMAGE_VERSION, "host.image.version").

%% Cloud attributes

%% Name of the cloud provider.
-define(CLOUD_PROVIDER, "cloud.provider").
%% The cloud account id used to identify different entities.
-define(CLOUD_ACCOUNT, "cloud.account.id").
%% A specific geographical location where different entities can run.
-define(CLOUD_REGION, "cloud.region").
%% Zones are a sub set of the region connected through low-latency links.
-define(CLOUD_ZONE, "cloud.zone").
2 changes: 2 additions & 0 deletions include/ot_span.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
%% @end
%%%-------------------------------------------------------------------------

%% Holds information about the instrumentation library specified when
%% getting a Tracer from the TracerProvider.
-record(library_resource, {name :: atom() | undefined,
version :: string() | undefined}).

Expand Down
64 changes: 64 additions & 0 deletions src/ot_resource.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
%%%------------------------------------------------------------------------
%% 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_resource).

-export([create/1,
merge/2]).

-type key() :: io_lib:latin1_string().
-type value() :: io_lib:latin1_string() | integer() | float() | boolean().
-type resource() :: {resource, #{key() => value()}}.
-opaque t() :: resource().

-export_type([t/0]).

-define(MAX_LENGTH, 255).

%% verifies each key and value and drops any that don't pass verification
-spec create(#{key() => value()}) -> t().
create(Map) ->
{resource, maps:filter(fun(K, V) ->
%% TODO: log an info or debug message when dropping?
check_key(K) andalso check_value(V)
end, Map)}.

%% In case of collision the current, first argument, resource takes precedence.
-spec merge(resource(), resource()) -> t().
merge({resource, CurrentResource}, {resource, OtherResource}) ->
{resource, maps:merge(OtherResource, CurrentResource)}.

%%

%% all resource strings, key or value, must be latin1 with length less than 255
check_string(S) ->
length(S) =< ?MAX_LENGTH andalso io_lib:deep_latin1_char_list(S).

%% a resource key must be a non-empty latin1 string
check_key(K) ->
is_list(K) andalso length(K) > 0 andalso check_string(K).

%% a resource value can be a latin1 string, integer, float or boolean
check_value(V) when is_integer(V) ;
is_float(V) ;
is_boolean(V) ->
true;
check_value(V) when is_list(V) ->
check_string(V);
check_value(_) ->
false.
44 changes: 44 additions & 0 deletions src/ot_resource_env_var.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
%%%------------------------------------------------------------------------
%% 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_resource_env_var).

-export([get_resource/0]).

-define(OS_ENV, "OTEL_RESOURCE_LABELS").
-define(LABEL_LIST_SPLITTER, ",").
-define(LABEL_KEY_VALUE_SPLITTER, "=").

get_resource() ->
ot_resource:create(parse_resource_labels(os:getenv(?OS_ENV))).

%%

parse_resource_labels(false) ->
#{};
parse_resource_labels(RawLabels) ->
Labels = string:split(RawLabels, ?LABEL_LIST_SPLITTER, all),
lists:foldl(fun(Label, Acc) ->
case string:split(Label, ?LABEL_KEY_VALUE_SPLITTER, all) of
[K, V] ->
V1 = re:replace(string:trim(V), "^\"|\"$", "", [global, {return, list}]),
Acc#{string:trim(K) => V1};
_ ->
Acc
end
end, #{}, Labels).
5 changes: 3 additions & 2 deletions src/ot_sampler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@
opentelemetry:kind(),
opentelemetry:attributes(),
term()) -> sampling_result()), term()}.
-opaque t() :: sampler().
-export_type([sampling_result/0,
sampling_decision/0,
sampler/0]).
t/0]).

-define(MAX_VALUE, 9223372036854775807). %% 2^63 - 1
-define(DEFAULT_PROBABILITY, 0.5).

-spec setup(atom() | module(), map()) -> sampler().
-spec setup(atom() | module(), map()) -> t().
setup(always_on, _Opts) ->
{fun ?MODULE:always_on/8, []};
setup(always_off, _Opts) ->
Expand Down
4 changes: 2 additions & 2 deletions src/ot_tracer.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
ctx_module :: module(),
on_start_processors :: fun((opentelemetry:span()) -> opentelemetry:span()),
on_end_processors :: fun((opentelemetry:span()) -> boolean() | {error, term()}),
sampler :: ot_sampler:sampler(),
sampler :: ot_sampler:t(),
library_resource :: ot_tracer_server:library_resource() | undefined,
resource :: term() | undefined}).
resource :: ot_resource:t() | undefined}).
-type tracer() :: #tracer{}.

-record(tracer_ctx, {active :: opentelemetry:span_ctx() | undefined,
Expand Down
5 changes: 4 additions & 1 deletion src/ot_tracer_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,21 @@
-record(state, {tracer :: tracer(),
processors :: [module()],
deny_list :: [atom() | {atom(), string()}],
sampler :: ot_sampler:sampler()}).
sampler :: ot_sampler:t()}).

init(Opts) ->
{Sampler, SamplerOpts} = proplists:get_value(sampler, Opts, {always_on, #{}}),
SamplerFun = ot_sampler:setup(Sampler, SamplerOpts),
Processors = proplists:get_value(processors, Opts, []),
DenyList = proplists:get_value(deny_list, Opts, []),

Resource = ot_resource_env_var:get_resource(),

Tracer = #tracer{module=ot_tracer_default,
sampler=SamplerFun,
on_start_processors=on_start(Processors),
on_end_processors=on_end(Processors),
resource=Resource,
span_module=ot_span_ets,
ctx_module=ot_ctx_pdict},
opentelemetry:set_default_tracer({ot_tracer_default, Tracer}),
Expand Down
40 changes: 40 additions & 0 deletions test/ot_resource_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-module(ot_resource_SUITE).

-compile(export_all).

-include_lib("stdlib/include/assert.hrl").
-include_lib("common_test/include/ct.hrl").

-include_lib("opentelemetry_api/include/opentelemetry.hrl").
-include("ot_span.hrl").
-include("ot_test_utils.hrl").
-include("ot_tracer.hrl").

all() ->
[os_env_resource].

init_per_suite(Config) ->
os:putenv("OTEL_RESOURCE_LABELS", "service.name=cttest,service.version=1.1.1"),

application:load(opentelemetry),
application:set_env(opentelemetry, processors, [{ot_batch_processor, [{scheduled_delay_ms, 1}]}]),
{ok, _} = application:ensure_all_started(opentelemetry),
Config.

end_per_suite(_Config) ->
_ = application:stop(opentelemetry).

init_per_testcase(_, Config) ->
%% adds an exporter for a new table
%% spans will be exported to a separate table for each of the test cases
Tid = ets:new(exported_spans, [public, bag]),
ot_batch_processor:set_exporter(ot_exporter_tab, Tid),
[{tid, Tid} | Config].

os_env_resource(_Config) ->
{_, Tracer} = opentelemetry:get_tracer(),

?assertMatch({resource, #{"service.name" := "cttest",
"service.version" := "1.1.1"}}, Tracer#tracer.resource),

ok.

0 comments on commit 9341b47

Please sign in to comment.