Current section
Files
Jump to
Current section
Files
src/otel_observables.erl
%%%------------------------------------------------------------------------
%% Copyright 2023, 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(otel_observables).
-export([run_callbacks/4]).
-include_lib("kernel/include/logger.hrl").
-include_lib("opentelemetry_api_experimental/include/otel_metrics.hrl").
-include("otel_metrics.hrl").
-include("otel_view.hrl").
-type callbacks() :: [{otel_instrument:callback(), otel_instrument:callback_args(), otel_instrument:t()}].
%% call each callback and associate the result with the Instruments it observes
-spec run_callbacks(callbacks(), reference(), ets:table(), ets:table()) -> ok.
run_callbacks(Callbacks, ReaderId, ViewAggregationTab, MetricsTab) ->
lists:foreach(fun({Callback, CallbackArgs, Instruments})
when is_list(Instruments) ->
Results = Callback(CallbackArgs),
handle_instruments_observations(Results,
Instruments,
ViewAggregationTab,
MetricsTab,
ReaderId);
({Callback, CallbackArgs, Instrument}) ->
Results = Callback(CallbackArgs),
%% eqwalizer:ignore we know this is [otel_instrument:observation()] but eqwalizer doesn't
handle_instrument_observations(Results,
Instrument,
ViewAggregationTab,
MetricsTab,
ReaderId)
end, Callbacks).
%% lookup ViewAggregations for Instrument and aggregate each observation
-spec handle_instrument_observations([otel_instrument:observation()], otel_instrument:t(),
ets:table(), ets:table(), reference()) -> ok.
handle_instrument_observations(Results, #instrument{meter=Meter,
name=Name},
ViewAggregationTab, MetricsTab, ReaderId) ->
try ets:lookup_element(ViewAggregationTab, {Meter, Name}, 2) of
ViewAggregations ->
[handle_observations(MetricsTab, ViewAggregation, Results)
|| #view_aggregation{reader=Id}=ViewAggregation <- ViewAggregations,
Id =:= ReaderId],
ok
catch
error:badarg ->
%% no Views for this Instrument, so nothing to do
ok
end.
%% handle results for a multi-instrument callback
-spec handle_instruments_observations([otel_instrument:named_observations()], [otel_instrument:t()],
ets:table(), ets:table(), reference()) -> ok.
handle_instruments_observations([], _Instruments, _ViewAggregationTab, _MetricsTab, _ReaderId) ->
ok;
handle_instruments_observations([{InstrumentName, Results} | Rest], Instruments,
ViewAggregationTab, MetricsTab, ReaderId) ->
case lists:keyfind(InstrumentName, #instrument.name, Instruments) of
false ->
?LOG_DEBUG("Unknown Instrument ~p used in metric callback", [InstrumentName]);
Instrument ->
handle_instrument_observations(Results, Instrument, ViewAggregationTab, MetricsTab, ReaderId)
end,
handle_instruments_observations(Rest, Instruments, ViewAggregationTab, MetricsTab, ReaderId);
handle_instruments_observations([Result | Rest], Instruments, ViewAggregationTab, MetricsTab, ReaderId) ->
?LOG_DEBUG("Each multi-instrument callback result must be a tuple of "
"type {atom(), [{number(), map()}]} but got ~p", [Result]),
handle_instruments_observations(Rest, Instruments, ViewAggregationTab, MetricsTab, ReaderId);
handle_instruments_observations(Results, _Instruments, _ViewAggregationTab, _MetricsTab, _ReaderId) ->
?LOG_DEBUG("Multi-instrument callback result must be a list of type "
"[{atom(), [{number(), map()}]}] but got ~p", [Results]),
ok.
%% update aggregation for each observation
handle_observations(_MetricsTab, _ViewAggregation, []) ->
ok;
handle_observations(MetricsTab, ViewAggregation, [{Number, Attributes} | Rest])
when is_number(Number),
is_map(Attributes) ->
_ = otel_aggregation:maybe_init_aggregate(MetricsTab, ViewAggregation, Number, Attributes),
handle_observations(MetricsTab, ViewAggregation, Rest);
handle_observations(MetricsTab, ViewAggregation, [Result | Rest]) ->
?LOG_DEBUG("Each metric callback result must be of type {number(), map()} but got ~p", [Result]),
handle_observations(MetricsTab, ViewAggregation, Rest);
handle_observations(_MetricsTab, _ViewAggregation, Result) ->
?LOG_DEBUG("Metric callback return must be a list of type [{number(), map()}] or "
"[{atom(), [{number(), map()}]}] but got", [Result]),
ok.