Current section

Files

Jump to
opentelemetry src ot_meter_default.erl
Raw

src/ot_meter_default.erl

%%%------------------------------------------------------------------------
%% 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_meter_default).
-behaviour(ot_meter).
-behaviour(gen_server).
-export([start_link/1,
new_instruments/2,
lookup_instrument/1,
record/4,
record_batch/3,
%% functions used for bound instruments
record/3,
bind/3,
release/2,
%% observer functions
observer_tab/0,
register_observer/3,
set_observer_callback/3,
observe/3]).
-export([init/1,
handle_call/3,
handle_cast/2]).
-include("ot_meter.hrl").
-define(OBSERVER_TAB, ot_metric_accumulator_observers).
-define(TAB, ?MODULE).
-record(state, {}).
start_link(Opts) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Opts, []).
-spec new_instruments(opentelemetry:meter(), [ot_meter:instrument_opts()]) -> boolean().
new_instruments(_Meter, List) ->
gen_server:call(?MODULE, {new, List}).
-spec record(opentelemetry:meter(), bound_instrument(), number()) -> ok.
record(_Meter, unknown_instrument, Number) when is_number(Number) ->
ok;
record(_Meter, BoundInstrument, Number) when is_number(Number) ->
_ = ot_metric_accumulator:record(BoundInstrument, Number),
ok;
record(_, _, _) ->
ok.
-spec record(opentelemetry:meter(), ot_meter:name(), ot_meter:label_set(), number()) -> ok.
record(_Meter, Name, LabelSet, Number) when is_number(Number) ->
_ = ot_metric_accumulator:record(Name, LabelSet, Number),
ok;
record(_, _, _, _) ->
ok.
-spec record_batch(opentelemetry:meter(), [{ot_meter:name(), number()}], ot_meter:label_set()) -> ok.
record_batch(_Meter, Measures, LabelSet) ->
[ot_metric_accumulator:record(Name, LabelSet, Number) || {Name, Number} <- Measures, is_number(Number)],
ok.
-spec release(opentelemetry:meter(), bound_instrument()) -> ok.
release(_Meter, _BoundInstrument) ->
ok.
-spec bind(opentelemetry:meter(), instrument() | ot_meter:name(), ot_meter:label_set())
-> bound_instrument().
bind(_Meter, Instrument=#instrument{}, LabelSet) ->
bind_instrument(Instrument, LabelSet);
bind(_Meter, Name, LabelSet) ->
case lookup_instrument(Name) of
unknown_instrument ->
unknown_instrument;
Instrument ->
bind_instrument(Instrument, LabelSet)
end.
-spec lookup_instrument(ot_meter:name()) -> instrument() | unknown_instrument.
lookup_instrument(Name) ->
case ets:lookup(?TAB, Name) of
[Instrument] ->
Instrument;
[] ->
unknown_instrument
end.
observer_tab() ->
?OBSERVER_TAB.
-spec register_observer(opentelemetry:meter(), ot_meter:name(), ot_observer:callback()) -> ok.
register_observer(_Meter, Name, Callback) ->
case lookup_instrument(Name) of
unknown_instrument ->
unknown_instrument;
Instrument ->
gen_server:call(?MODULE, {register_observer, Name, Instrument, Callback})
end.
-spec set_observer_callback(opentelemetry:meter(), ot_meter:name(), ot_observer:callback())
-> ok | unknown_instrument.
set_observer_callback(_Meter, Name, Callback) ->
case lookup_instrument(Name) of
unknown_instrument ->
unknown_instrument;
Instrument ->
gen_server:call(?MODULE, {register_observer, Name, Instrument, Callback})
end.
-spec observe(instrument(), number(), ot_meter:label_set()) -> ok.
observe(ObserverInstrument, Number, LabelSet) when is_number(Number) ->
ot_metric_accumulator:observe(ObserverInstrument, Number, LabelSet),
ok;
observe(_, _, _) ->
ok.
init(_Opts) ->
%% TODO: we do not want to lose instrument and observer tables ever
%% eventually need to have an heir to take them if this process crashes.
%% Another option is to just use persistent_term since these things
%% don't change after creation.
%% ets table is required for other parts to not crash so we create
%% it in init and not in a handle_continue or whatever else
case ets:info(?TAB, name) of
undefined ->
ets:new(?TAB, [named_table,
protected,
{read_concurrency, true},
{keypos, #instrument.name}
]);
_ ->
ok
end,
%% observers are stored in a separate table from other instruments
case ets:info(?OBSERVER_TAB, name) of
undefined ->
_ = ets:new(?OBSERVER_TAB, [named_table,
protected,
{keypos, #observer.name}]);
_ ->
ok
end,
{ok, #state{}}.
handle_call({new, List}, _From, State) ->
Result = ets:insert_new(?TAB,
[#instrument{name=Name,
description=maps:get(description, I, <<>>),
kind=MetricKind,
input_type=maps:get(input_type, I, integer),
unit=maps:get(unit, I, one),
label_keys=maps:get(label_keys, I, [])} || I=#{name := Name,
kind := MetricKind} <- List]),
{reply, Result, State};
handle_call({register_observer, Name, Instrument, Callback}, _From, State) ->
_ = ets:insert(?OBSERVER_TAB, #observer{name=Name,
instrument={ot_meter_default, Instrument},
callback=Callback}),
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
%% internal
%% TODO: use a counter ref for `sum' and `mmsc' aggregated
%% instruments with `input_type' `integer'?
bind_instrument(Instrument, LabelSet) ->
ot_metric_accumulator:lookup_active(Instrument, LabelSet).