Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add counter warmup option #315

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bin/prometheus_exporter
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ def run
opt.on('--logger-path PATH', String, '(optional) Path to file for logger output. Defaults to STDERR') do |o|
options[:logger_path] = o
end

opt.on('--counter-warmup', "Automate initialization of counter values to 0") do |o|
options[:counter_warmup] = true
end
end.parse!

logger = Logger.new(options[:logger_path])
Expand Down
32 changes: 31 additions & 1 deletion lib/prometheus_exporter/metric/counter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

module PrometheusExporter::Metric
class Counter < Base

@counter_warmup_enabled = nil if !defined?(@counter_warmup_enabled)

def self.counter_warmup=(enabled)
@counter_warmup_enabled = enabled
end

def self.counter_warmup
!!@counter_warmup_enabled
end

attr_reader :data

def initialize(name, help)
Expand All @@ -15,10 +26,12 @@ def type

def reset!
@data = {}
@counter_warmup = {}
end

def metric_text
@data.map do |labels, value|
@data.keys.map do |labels|
value = warmup_counter_value(labels)
"#{prefix(@name)}#{labels_text(labels)} #{value}"
end.join("\n")
end
Expand All @@ -28,26 +41,43 @@ def to_h
end

def remove(labels)
@counter_warmup.delete(labels)
@data.delete(labels)
end

def observe(increment = 1, labels = {})
warmup_counter(labels)
@data[labels] ||= 0
@data[labels] += increment
end

def increment(labels = {}, value = 1)
warmup_counter(labels)
@data[labels] ||= 0
@data[labels] += value
end

def decrement(labels = {}, value = 1)
warmup_counter(labels)
@data[labels] ||= 0
@data[labels] -= value
end

def reset(labels = {}, value = 0)
warmup_counter(labels)
@data[labels] = value
end

private

def warmup_counter(labels)
if Counter.counter_warmup && [email protected]_key?(labels)
@counter_warmup[labels] = 0
end
end

def warmup_counter_value(labels)
@counter_warmup.delete(labels) || @data[labels]
end
end
end
11 changes: 8 additions & 3 deletions lib/prometheus_exporter/server/process_collector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ def initialize
@process_metrics.filter = -> (new_metric, old_metric) do
new_metric["pid"] == old_metric["pid"] && new_metric["hostname"] == old_metric["hostname"]
end
@counter_metrics = {}
PROCESS_COUNTERS.each do |k, help|
k = k.to_s
@counter_metrics[k] = PrometheusExporter::Metric::Counter.new(k, help)
end
end

def type
Expand All @@ -43,18 +48,18 @@ def metrics
metric_key = (m["metric_labels"] || {}).merge("pid" => m["pid"], "hostname" => m["hostname"])
metric_key.merge!(m["custom_labels"]) if m["custom_labels"]

PROCESS_GAUGES.map do |k, help|
PROCESS_GAUGES.each do |k, help|
k = k.to_s
if v = m[k]
g = metrics[k] ||= PrometheusExporter::Metric::Gauge.new(k, help)
g.observe(v, metric_key)
end
end

PROCESS_COUNTERS.map do |k, help|
PROCESS_COUNTERS.each do |k, help|
k = k.to_s
if v = m[k]
c = metrics[k] ||= PrometheusExporter::Metric::Counter.new(k, help)
c = metrics[k] ||= @counter_metrics[k]
c.observe(v, metric_key)
end
end
Expand Down
8 changes: 7 additions & 1 deletion lib/prometheus_exporter/server/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def initialize(options = {})
@auth = nil
@realm = nil
@histogram = nil
@counter_warmup = nil

options.each do |k, v|
send("#{k}=", v) if self.class.method_defined?("#{k}=")
Expand All @@ -26,6 +27,7 @@ def initialize(options = {})
def start
PrometheusExporter::Metric::Base.default_prefix = prefix
PrometheusExporter::Metric::Base.default_labels = label
PrometheusExporter::Metric::Counter.counter_warmup = counter_warmup

if histogram
PrometheusExporter::Metric::Base.default_aggregation = PrometheusExporter::Metric::Histogram
Expand Down Expand Up @@ -54,7 +56,7 @@ def start
end

attr_accessor :unicorn_listen_address, :unicorn_pid_file
attr_writer :prefix, :port, :bind, :collector_class, :type_collectors, :timeout, :verbose, :server_class, :label, :auth, :realm, :histogram
attr_writer :prefix, :port, :bind, :collector_class, :type_collectors, :timeout, :verbose, :server_class, :label, :auth, :realm, :histogram, :counter_warmup

def auth
@auth || nil
Expand Down Expand Up @@ -109,6 +111,10 @@ def histogram
@histogram || false
end

def counter_warmup
@counter_warmup || false
end

private

def register_type_collectors
Expand Down
31 changes: 31 additions & 0 deletions test/metric/counter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,36 @@ module PrometheusExporter::Metric

assert_equal(counter.to_h, { sam: "ham" } => 5, { foo: "bar" } => 10)
end

describe "with counter warmup enabled" do
before do
Counter.counter_warmup = true
end

after do
Counter.counter_warmup = false
end

it "returns 0 on 1st read of text, actual value on 2nd read" do
counter.observe(5, sam: "ham")
counter.observe(10, foo: "bar")

text = <<~TEXT
# HELP a_counter my amazing counter
# TYPE a_counter counter
a_counter{sam="ham"} 0
a_counter{foo="bar"} 0
TEXT
assert_equal(counter.to_prometheus_text, text)

text = <<~TEXT
# HELP a_counter my amazing counter
# TYPE a_counter counter
a_counter{sam="ham"} 5
a_counter{foo="bar"} 10
TEXT
assert_equal(counter.to_prometheus_text, text)
end
end
end
end
6 changes: 5 additions & 1 deletion test/server/runner_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
require_relative '../test_helper'
require 'prometheus_exporter/server'

require 'ostruct'

class PrometheusRunnerTest < Minitest::Test
class MockerWebServer < OpenStruct
def start
Expand Down Expand Up @@ -70,7 +72,8 @@ def test_runner_custom_options
label: { environment: 'integration' },
auth: 'my_htpasswd_file',
realm: 'test realm',
histogram: true
histogram: true,
counter_warmup: true
)

assert_equal(runner.prefix, 'new_')
Expand All @@ -83,6 +86,7 @@ def test_runner_custom_options
assert_equal(runner.auth, 'my_htpasswd_file')
assert_equal(runner.realm, 'test realm')
assert_equal(runner.histogram, true)
assert_equal(runner.counter_warmup, true)

reset_base_metric_label
end
Expand Down