Skip to content

Commit 7098818

Browse files
fix: Stop exporting metrics with empty data points (#1859)
* fix: Stop exporting metrics with empty data points The OTLP exporter raised the following error when attempting to export a histogram with a view without data points: "ERROR -- : OpenTelemetry error: unexpected error in OTLP::MetricsExporter#encode - nil message not allowed here." * rubocop
1 parent 0ec5eb1 commit 7098818

File tree

3 files changed

+68
-14
lines changed

3 files changed

+68
-14
lines changed

metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_store.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ def initialize
2323
def collect
2424
@mutex.synchronize do
2525
@epoch_end_time = now_in_nano
26-
# snapshot = @metric_streams.map { |ms| ms.collect(@epoch_start_time, @epoch_end_time) }
2726
snapshot = @metric_streams.flat_map { |ms| ms.collect(@epoch_start_time, @epoch_end_time) }
2827
@epoch_start_time = @epoch_end_time
28+
2929
snapshot
3030
end
3131
end

metrics_sdk/lib/opentelemetry/sdk/metrics/state/metric_stream.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ def initialize(
4141
def collect(start_time, end_time)
4242
@mutex.synchronize do
4343
metric_data = []
44+
45+
# data points are required to export over OTLP
46+
return metric_data if @data_points.empty?
47+
4448
if @registered_views.empty?
4549
metric_data << aggregate_metric_data(start_time, end_time)
4650
else

metrics_sdk/test/integration/periodic_metric_reader_test.rb

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,51 @@
1111
describe '#periodic_metric_reader' do
1212
before { reset_metrics_sdk }
1313

14+
# OTLP cannot export a metric without data points
15+
it 'does not export metrics without data points' do
16+
OpenTelemetry::SDK.configure
17+
18+
metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new
19+
periodic_metric_reader = OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(export_interval_millis: 5000, export_timeout_millis: 5000, exporter: metric_exporter)
20+
21+
OpenTelemetry.meter_provider.add_metric_reader(periodic_metric_reader)
22+
23+
meter = OpenTelemetry.meter_provider.meter('test')
24+
meter.create_histogram('example', unit: 's', description: 'test')
25+
26+
sleep(1)
27+
28+
periodic_metric_reader.shutdown
29+
snapshot = metric_exporter.metric_snapshots
30+
31+
assert_empty snapshot
32+
end
33+
34+
it 'does not export metrics without data points when they have a view' do
35+
OpenTelemetry::SDK.configure
36+
37+
metric_exporter = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new
38+
periodic_metric_reader = OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(export_interval_millis: 5000, export_timeout_millis: 5000, exporter: metric_exporter)
39+
40+
OpenTelemetry.meter_provider.add_metric_reader(periodic_metric_reader)
41+
42+
boundaries = [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10]
43+
44+
OpenTelemetry.meter_provider.add_view('http.server.request.duration',
45+
type: :histogram,
46+
aggregation: OpenTelemetry::SDK::Metrics::Aggregation::ExplicitBucketHistogram.new(boundaries: boundaries))
47+
48+
meter = OpenTelemetry.meter_provider.meter('test')
49+
meter.create_histogram('http.server.request.duration', unit: 's', description: 'Duration of HTTP server requests.')
50+
51+
sleep(8)
52+
53+
periodic_metric_reader.shutdown
54+
snapshot = metric_exporter.metric_snapshots
55+
56+
assert_empty snapshot
57+
end
58+
1459
it 'emits 2 metrics after 10 seconds' do
1560
OpenTelemetry::SDK.configure
1661

@@ -30,29 +75,34 @@
3075

3176
sleep(8)
3277

78+
counter.add(5)
79+
counter.add(6)
80+
3381
periodic_metric_reader.shutdown
3482
snapshot = metric_exporter.metric_snapshots
3583

3684
_(snapshot.size).must_equal(2)
3785

38-
first_snapshot = snapshot
39-
_(first_snapshot[0].name).must_equal('counter')
40-
_(first_snapshot[0].unit).must_equal('smidgen')
41-
_(first_snapshot[0].description).must_equal('a small amount of something')
86+
_(snapshot[0].name).must_equal('counter')
87+
_(snapshot[0].unit).must_equal('smidgen')
88+
_(snapshot[0].description).must_equal('a small amount of something')
89+
90+
_(snapshot[0].instrumentation_scope.name).must_equal('test')
4291

43-
_(first_snapshot[0].instrumentation_scope.name).must_equal('test')
92+
_(snapshot[0].data_points[0].value).must_equal(1)
93+
_(snapshot[0].data_points[0].attributes).must_equal({})
4494

45-
_(first_snapshot[0].data_points[0].value).must_equal(1)
46-
_(first_snapshot[0].data_points[0].attributes).must_equal({})
95+
_(snapshot[0].data_points[1].value).must_equal(4)
96+
_(snapshot[0].data_points[1].attributes).must_equal('a' => 'b')
4797

48-
_(first_snapshot[0].data_points[1].value).must_equal(4)
49-
_(first_snapshot[0].data_points[1].attributes).must_equal('a' => 'b')
98+
_(snapshot[0].data_points[2].value).must_equal(3)
99+
_(snapshot[0].data_points[2].attributes).must_equal('b' => 'c')
50100

51-
_(first_snapshot[0].data_points[2].value).must_equal(3)
52-
_(first_snapshot[0].data_points[2].attributes).must_equal('b' => 'c')
101+
_(snapshot[0].data_points[3].value).must_equal(4)
102+
_(snapshot[0].data_points[3].attributes).must_equal('d' => 'e')
53103

54-
_(first_snapshot[0].data_points[3].value).must_equal(4)
55-
_(first_snapshot[0].data_points[3].attributes).must_equal('d' => 'e')
104+
_(snapshot[1].data_points.size).must_equal(1)
105+
_(snapshot[1].data_points[0].value).must_equal(11)
56106

57107
_(periodic_metric_reader.instance_variable_get(:@thread).alive?).must_equal false
58108
end

0 commit comments

Comments
 (0)