Skip to content

Commit

Permalink
Automatically initialize time series without labels
Browse files Browse the repository at this point in the history
According to [Prometheus Best Practices](https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics),
client libraries are expected to automatically export a 0 value when declaring
a metric that has no labels.

We missed this so far in the Ruby client, this PR rectifies that.

NOTE: This can be considered a breaking change. On the one hand, it's a bug fix,
but on the other, it will make many time series materialize when scraping apps
that use the client, that previously wouldn't have.

Depending on how many label-less metrics the app is declaring, this may have a
significant impact, so we should probably cut a new major version and warn
about this in the Release Notes.

Signed-off-by: Daniel Magliola <dmagliola@crystalgears.com>
  • Loading branch information
dmagliola committed Dec 24, 2020
1 parent 4cdd0ab commit feccff3
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 28 deletions.
2 changes: 2 additions & 0 deletions lib/prometheus/client/metric.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ def initialize(name,
metric_type: type,
metric_settings: store_settings
)

init_label_set({}) if labels.empty?
end

# Returns the value for the given label set
Expand Down
18 changes: 13 additions & 5 deletions spec/prometheus/client/counter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,22 @@
end

describe '#init_label_set' do
let(:expected_labels) { [:test] }
context "with labels" do
let(:expected_labels) { [:test] }

it 'initializes the metric for a given label set' do
expect(counter.values).to eql({})
it 'initializes the metric for a given label set' do
expect(counter.values).to eql({})

counter.init_label_set(test: 'value')
counter.init_label_set(test: 'value')

expect(counter.values).to eql({test: 'value'} => 0.0)
expect(counter.values).to eql({test: 'value'} => 0.0)
end
end

context "without labels" do
it 'automatically initializes the metric' do
expect(counter.values).to eql({} => 0.0)
end
end
end
end
18 changes: 13 additions & 5 deletions spec/prometheus/client/gauge_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,22 @@
end

describe '#init_label_set' do
let(:expected_labels) { [:test] }
context "with labels" do
let(:expected_labels) { [:test] }

it 'initializes the metric for a given label set' do
expect(gauge.values).to eql({})
it 'initializes the metric for a given label set' do
expect(gauge.values).to eql({})

gauge.init_label_set(test: 'value')
gauge.init_label_set(test: 'value')

expect(gauge.values).to eql({test: 'value'} => 0.0)
expect(gauge.values).to eql({test: 'value'} => 0.0)
end
end

context "without labels" do
it 'automatically initializes the metric' do
expect(gauge.values).to eql({} => 0.0)
end
end
end
end
28 changes: 19 additions & 9 deletions spec/prometheus/client/histogram_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,28 @@
end

describe '#init_label_set' do
let(:expected_labels) { [:status] }
context "with labels" do
let(:expected_labels) { [:status] }

it 'initializes the metric for a given label set' do
expect(histogram.values).to eql({})
it 'initializes the metric for a given label set' do
expect(histogram.values).to eql({})

histogram.init_label_set(status: 'bar')
histogram.init_label_set(status: 'foo')
histogram.init_label_set(status: 'bar')
histogram.init_label_set(status: 'foo')

expect(histogram.values).to eql(
{ status: 'bar' } => { "2.5" => 0.0, "5" => 0.0, "10" => 0.0, "+Inf" => 0.0, "sum" => 0.0 },
{ status: 'foo' } => { "2.5" => 0.0, "5" => 0.0, "10" => 0.0, "+Inf" => 0.0, "sum" => 0.0 },
)
expect(histogram.values).to eql(
{ status: 'bar' } => { "2.5" => 0.0, "5" => 0.0, "10" => 0.0, "+Inf" => 0.0, "sum" => 0.0 },
{ status: 'foo' } => { "2.5" => 0.0, "5" => 0.0, "10" => 0.0, "+Inf" => 0.0, "sum" => 0.0 },
)
end
end

context "without labels" do
it 'automatically initializes the metric' do
expect(histogram.values).to eql(
{} => { "2.5" => 0.0, "5" => 0.0, "10" => 0.0, "+Inf" => 0.0, "sum" => 0.0 },
)
end
end
end
end
28 changes: 19 additions & 9 deletions spec/prometheus/client/summary_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,28 @@
end

describe '#init_label_set' do
let(:expected_labels) { [:status] }
context "with labels" do
let(:expected_labels) { [:status] }

it 'initializes the metric for a given label set' do
expect(summary.values).to eql({})
it 'initializes the metric for a given label set' do
expect(summary.values).to eql({})

summary.init_label_set(status: 'bar')
summary.init_label_set(status: 'foo')
summary.init_label_set(status: 'bar')
summary.init_label_set(status: 'foo')

expect(summary.values).to eql(
{ status: 'bar' } => { "count" => 0.0, "sum" => 0.0 },
{ status: 'foo' } => { "count" => 0.0, "sum" => 0.0 },
)
expect(summary.values).to eql(
{ status: 'bar' } => { "count" => 0.0, "sum" => 0.0 },
{ status: 'foo' } => { "count" => 0.0, "sum" => 0.0 },
)
end
end

context "without labels" do
it 'automatically initializes the metric' do
expect(summary.values).to eql(
{} => { "count" => 0.0, "sum" => 0.0 },
)
end
end
end
end

0 comments on commit feccff3

Please sign in to comment.