Class: Prometheus::Client::Histogram

Inherits:
Metric
  • Object
show all
Defined in:
lib/prometheus/client/histogram.rb

Overview

A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets. It also provides a total count and sum of all observed values.

Constant Summary collapse

DEFAULT_BUCKETS =

DEFAULT_BUCKETS are the default Histogram buckets. The default buckets are tailored to broadly measure the response time (in seconds) of a network service. (From DefBuckets client_golang)

[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
2.5, 5, 10].freeze

Instance Attribute Summary collapse

Attributes inherited from Metric

#docstring, #labels, #name, #preset_labels

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, docstring:, labels: [], preset_labels: {}, buckets: DEFAULT_BUCKETS, store_settings: {}) ⇒ Histogram

Offer a way to manually specify buckets

Raises:

  • (ArgumentError)


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/prometheus/client/histogram.rb', line 20

def initialize(name,
               docstring:,
               labels: [],
               preset_labels: {},
               buckets: DEFAULT_BUCKETS,
               store_settings: {})
  raise ArgumentError, 'Unsorted buckets, typo?' unless sorted?(buckets)

  @buckets = buckets
  super(name,
        docstring: docstring,
        labels: labels,
        preset_labels: preset_labels,
        store_settings: store_settings)
end

Instance Attribute Details

#bucketsObject (readonly)

Returns the value of attribute buckets.



17
18
19
# File 'lib/prometheus/client/histogram.rb', line 17

def buckets
  @buckets
end

Class Method Details

.exponential_buckets(start:, factor: 2, count:) ⇒ Object



40
41
42
# File 'lib/prometheus/client/histogram.rb', line 40

def self.exponential_buckets(start:, factor: 2, count:)
  count.times.map { |idx| start.to_f * factor ** idx }
end

.linear_buckets(start:, width:, count:) ⇒ Object



36
37
38
# File 'lib/prometheus/client/histogram.rb', line 36

def self.linear_buckets(start:, width:, count:)
  count.times.map { |idx| start.to_f + idx * width }
end

Instance Method Details

#get(labels: {}) ⇒ Object

Returns a hash with all the buckets plus +Inf (count) plus Sum for the given label set



88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/prometheus/client/histogram.rb', line 88

def get(labels: {})
  base_label_set = label_set_for(labels)

  all_buckets = buckets + ["+Inf", "sum"]

  @store.synchronize do
    all_buckets.each_with_object({}) do |upper_limit, acc|
      acc[upper_limit.to_s] = @store.get(labels: base_label_set.merge(le: upper_limit.to_s))
    end.tap do |acc|
      accumulate_buckets(acc)
    end
  end
end

#init_label_set(labels) ⇒ Object



117
118
119
120
121
122
123
124
125
# File 'lib/prometheus/client/histogram.rb', line 117

def init_label_set(labels)
  base_label_set = label_set_for(labels)

  @store.synchronize do
    (buckets + ["+Inf", "sum"]).each do |bucket|
      @store.set(labels: base_label_set.merge(le: bucket.to_s), val: 0)
    end
  end
end

#observe(value, labels: {}) ⇒ Object

Records a given value. The recorded value is usually positive or zero. A negative value is accepted but prevents current versions of Prometheus from properly detecting counter resets in the sum of observations. See prometheus.io/docs/practices/histograms/#count-and-sum-of-observations for details.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/prometheus/client/histogram.rb', line 69

def observe(value, labels: {})
  bucket = buckets.find {|upper_limit| upper_limit >= value  }
  bucket = "+Inf" if bucket.nil?

  base_label_set = label_set_for(labels)

  # This is basically faster than doing `.merge`
  bucket_label_set = base_label_set.dup
  bucket_label_set[:le] = bucket.to_s
  sum_label_set = base_label_set.dup
  sum_label_set[:le] = "sum"

  @store.synchronize do
    @store.increment(labels: bucket_label_set, by: 1)
    @store.increment(labels: sum_label_set, by: value)
  end
end

#typeObject



59
60
61
# File 'lib/prometheus/client/histogram.rb', line 59

def type
  :histogram
end

#valuesObject

Returns all label sets with their values expressed as hashes with their buckets



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/prometheus/client/histogram.rb', line 103

def values
  values = @store.all_values

  result = values.each_with_object({}) do |(label_set, v), acc|
    actual_label_set = label_set.reject{|l| l == :le }
    acc[actual_label_set] ||= @buckets.map{|b| [b.to_s, 0.0]}.to_h
    acc[actual_label_set][label_set[:le].to_s] = v
  end

  result.each do |(_label_set, v)|
    accumulate_buckets(v)
  end
end

#with_labels(labels) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/prometheus/client/histogram.rb', line 44

def with_labels(labels)
  new_metric = self.class.new(name,
                              docstring: docstring,
                              labels: @labels,
                              preset_labels: preset_labels.merge(labels),
                              buckets: @buckets,
                              store_settings: @store_settings)

  # The new metric needs to use the same store as the "main" declared one, otherwise
  # any observations on that copy with the pre-set labels won't actually be exported.
  new_metric.replace_internal_store(@store)

  new_metric
end