Skip to content

Commit 1f02419

Browse files
author
Tristan Sloughter
authored
Merge pull request #553 from tsloughter/view-attr-filter
metric views: support filtering attributes
2 parents 6cf68f6 + b42e112 commit 1f02419

File tree

5 files changed

+111
-8
lines changed

5 files changed

+111
-8
lines changed

apps/opentelemetry_experimental/src/otel_aggregation.erl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,26 @@
4949
ViewAggregation :: #view_aggregation{},
5050
CollectionStartTime :: integer().
5151

52-
maybe_init_aggregate(MetricsTab, ViewAggregation=#view_aggregation{aggregation_module=AggregationModule},
52+
maybe_init_aggregate(MetricsTab, ViewAggregation=#view_aggregation{aggregation_module=AggregationModule,
53+
attribute_keys=AttributeKeys},
5354
Value, Attributes) ->
54-
case AggregationModule:aggregate(MetricsTab, ViewAggregation, Value, Attributes) of
55+
FilteredAttributes = filter_attributes(AttributeKeys, Attributes),
56+
case AggregationModule:aggregate(MetricsTab, ViewAggregation, Value, FilteredAttributes) of
5557
true ->
5658
ok;
5759
false ->
5860
%% entry doesn't exist, create it and rerun the aggregate function
59-
Metric = AggregationModule:init(ViewAggregation, Attributes),
61+
Metric = AggregationModule:init(ViewAggregation, FilteredAttributes),
6062
%% don't overwrite a possible concurrent measurement doing the same
6163
_ = ets:insert_new(MetricsTab, Metric),
62-
AggregationModule:aggregate(MetricsTab, ViewAggregation, Value, Attributes)
64+
AggregationModule:aggregate(MetricsTab, ViewAggregation, Value, FilteredAttributes)
6365
end.
6466

67+
filter_attributes(undefined, Attributes) ->
68+
Attributes;
69+
filter_attributes(Keys, Attributes) ->
70+
maps:with(Keys, Attributes).
71+
6572
-spec default_mapping() -> #{otel_instrument:kind() => module()}.
6673
default_mapping() ->
6774
#{?KIND_COUNTER => otel_aggregation_sum,

apps/opentelemetry_experimental/src/otel_meter_server.erl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,10 @@ handle_measurement(Meter, Name, Number, Attributes, ViewAggregationsTab, Metrics
390390

391391
update_aggregations(Value, Attributes, ViewAggregations, MetricsTab) ->
392392
lists:foreach(fun(ViewAggregation=#view_aggregation{}) ->
393-
otel_aggregation:maybe_init_aggregate(MetricsTab, ViewAggregation, Value, Attributes);
393+
otel_aggregation:maybe_init_aggregate(MetricsTab,
394+
ViewAggregation,
395+
Value,
396+
Attributes);
394397
(_) ->
395398
ok
396399
end, ViewAggregations).
@@ -400,6 +403,18 @@ per_reader_aggregations(Reader, Instrument, ViewAggregations) ->
400403
[view_aggregation_for_reader(Instrument, ViewAggregation, View, Reader)
401404
|| {View, ViewAggregation} <- ViewAggregations].
402405

406+
view_aggregation_for_reader(Instrument=#instrument{kind=Kind}, ViewAggregation, View=#view{attribute_keys=AttributeKeys},
407+
Reader=#reader{id=Id,
408+
default_temporality_mapping=ReaderTemporalityMapping}) ->
409+
AggregationModule = aggregation_module(Instrument, View, Reader),
410+
Temporality = maps:get(Kind, ReaderTemporalityMapping, ?AGGREGATION_TEMPORALITY_UNSPECIFIED),
411+
412+
ViewAggregation#view_aggregation{
413+
reader=Id,
414+
attribute_keys=AttributeKeys,
415+
aggregation_module=AggregationModule,
416+
aggregation_options=#{},
417+
temporality=Temporality};
403418
view_aggregation_for_reader(Instrument=#instrument{kind=Kind}, ViewAggregation, View,
404419
Reader=#reader{id=Id,
405420
default_temporality_mapping=ReaderTemporalityMapping}) ->
@@ -408,10 +423,12 @@ view_aggregation_for_reader(Instrument=#instrument{kind=Kind}, ViewAggregation,
408423

409424
ViewAggregation#view_aggregation{
410425
reader=Id,
426+
attribute_keys=undefined,
411427
aggregation_module=AggregationModule,
412428
aggregation_options=#{},
413429
temporality=Temporality}.
414430

431+
415432
%% no aggregation defined for the View, so get the aggregation from the Reader
416433
%% the Reader's mapping of Instrument Kind to Aggregation was merged with the
417434
%% global default, so any missing Kind entries are filled in from the global

apps/opentelemetry_experimental/src/otel_view.erl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ new(Criteria, Config) ->
6161
#view{name=CriteriaInstrumentName,
6262
instrument_matchspec=Matchspec,
6363
description=maps:get(description, Config, undefined),
64-
attribute_keys=maps:get(attribute_keys, Config, []),
64+
attribute_keys=maps:get(attribute_keys, Config, undefined),
6565
aggregation_module=maps:get(aggregation_module, Config, undefined),
6666
aggregation_options=maps:get(aggregation_options, Config, #{})}.
6767

@@ -84,6 +84,7 @@ match_instrument_to_views(Instrument=#instrument{name=InstrumentName,
8484
Scope = otel_meter:scope(Meter),
8585
case lists:filtermap(fun(View=#view{name=ViewName,
8686
description=ViewDescription,
87+
attribute_keys=AttributeKeys,
8788
aggregation_options=AggregationOptions,
8889
instrument_matchspec=Matchspec}) ->
8990
case ets:match_spec_run([Instrument], Matchspec) of
@@ -96,6 +97,7 @@ match_instrument_to_views(Instrument=#instrument{name=InstrumentName,
9697
instrument=Instrument,
9798
temporality=Temporality,
9899
is_monotonic=IsMonotonic,
100+
attribute_keys=AttributeKeys,
99101
aggregation_options=AggregationOptions,
100102
description=value_or(ViewDescription,
101103
Description)
@@ -108,6 +110,7 @@ match_instrument_to_views(Instrument=#instrument{name=InstrumentName,
108110
instrument=Instrument,
109111
temporality=Temporality,
110112
is_monotonic=IsMonotonic,
113+
attribute_keys=undefined,
111114
aggregation_options=#{},
112115
description=Description}}];
113116
Aggs ->

apps/opentelemetry_experimental/src/otel_view.hrl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{name :: otel_instrument:name(),
33
instrument_matchspec :: ets:compiled_match_spec(),
44
description :: unicode:unicode_binary() | undefined,
5-
attribute_keys :: [opentelemetry:attribute_key()],
5+
attribute_keys :: [opentelemetry:attribute_key()] | undefined,
66
aggregation_module :: module(),
77
aggregation_options=#{} :: map()}).
88

@@ -13,6 +13,8 @@
1313
instrument :: otel_instrument:t(),
1414
reader :: reference() | undefined,
1515

16+
attribute_keys :: [opentelemetry:attribute_key()] | undefined,
17+
1618
aggregation_module :: module(),
1719
aggregation_options :: map(),
1820

apps/opentelemetry_experimental/test/otel_metrics_SUITE.erl

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ all() ->
8181
[default_view, provider_test, view_creation_test, counter_add, multiple_readers,
8282
explicit_histograms, delta_explicit_histograms, delta_counter, cumulative_counter,
8383
kill_reader, kill_server, observable_counter, observable_updown_counter, observable_gauge,
84-
multi_instrument_callback, using_macros, float_counter, float_updown_counter, float_histogram].
84+
multi_instrument_callback, using_macros, float_counter, float_updown_counter, float_histogram,
85+
sync_filtered_attributes, async_filtered_attributes].
8586

8687
init_per_suite(Config) ->
8788
application:load(opentelemetry_experimental),
@@ -922,3 +923,76 @@ multi_instrument_callback(_Config) ->
922923
?assertLastValueReceive(GaugeName, GaugeDesc, Unit, [{5, #{<<"a">> => <<"b">>}}]),
923924

924925
ok.
926+
927+
sync_filtered_attributes(_Config) ->
928+
Meter = opentelemetry_experimental:get_meter(?MODULE),
929+
930+
CounterName = a_counter,
931+
CounterDesc = <<"counter description">>,
932+
CounterUnit = kb,
933+
934+
Counter = otel_counter:create(Meter, CounterName,
935+
#{description => CounterDesc,
936+
unit => CounterUnit}),
937+
?assertMatch(#instrument{meter = {otel_meter_default, _},
938+
module = otel_meter_default,
939+
name = CounterName,
940+
description = CounterDesc,
941+
kind = counter,
942+
unit = CounterUnit}, Counter),
943+
944+
?assert(otel_meter_server:add_view(view_a, #{instrument_name => CounterName},
945+
#{aggregation_module => otel_aggregation_sum,
946+
attribute_keys => [a, b]})),
947+
948+
?assertEqual(ok, otel_counter:add(Counter, 2, #{a => 1, b => 2, c => 3})),
949+
?assertEqual(ok, otel_counter:add(Counter, 5, #{a => 1, b => 2})),
950+
?assertEqual(ok, ?counter_add(CounterName, 5, #{a => 1, b => 2, c => 3})),
951+
952+
otel_meter_server:force_flush(),
953+
954+
?assertSumReceive(CounterName, CounterDesc, kb,
955+
[{7, #{a => 1, b => 2, c => 3}}, {5, #{a => 1, b => 2}}]),
956+
?assertSumReceive(view_a, CounterDesc, kb,
957+
[{12, #{a => 1, b => 2}}]),
958+
959+
960+
ok.
961+
962+
async_filtered_attributes(_Config) ->
963+
DefaultMeter = otel_meter_default,
964+
965+
Meter = opentelemetry_experimental:get_meter(),
966+
?assertMatch({DefaultMeter, _}, Meter),
967+
968+
CounterName = a_observable_counter,
969+
CounterDesc = <<"observable counter description">>,
970+
CounterUnit = kb,
971+
972+
?assert(otel_meter_server:add_view(#{instrument_name => CounterName},
973+
#{aggregation_module => otel_aggregation_sum,
974+
attribute_keys => [a]})),
975+
976+
Counter = otel_meter:create_observable_counter(Meter, CounterName,
977+
fun(_Args) ->
978+
MeasurementAttributes = #{a => b,
979+
c => d},
980+
{4, MeasurementAttributes}
981+
end,
982+
[],
983+
#{description => CounterDesc,
984+
unit => CounterUnit}),
985+
986+
?assertMatch(#instrument{meter = {DefaultMeter,_},
987+
module = DefaultMeter,
988+
name = CounterName,
989+
description = CounterDesc,
990+
kind = observable_counter,
991+
unit = CounterUnit,
992+
callback=_}, Counter),
993+
994+
otel_meter_server:force_flush(),
995+
996+
?assertSumReceive(CounterName, <<"observable counter description">>, kb, [{4, #{a => b}}]),
997+
998+
ok.

0 commit comments

Comments
 (0)