Class: Philiprehberger::Metric::Histogram

Inherits:
Object
  • Object
show all
Defined in:
lib/philiprehberger/metric/histogram.rb

Overview

A histogram metric that tracks value distributions across configurable buckets.

Constant Summary collapse

DEFAULT_BUCKETS =

Default histogram buckets matching Prometheus defaults.

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, help: '', buckets: DEFAULT_BUCKETS) ⇒ Histogram

Returns a new instance of Histogram.

Parameters:

  • name (String)

    the metric name

  • help (String) (defaults to: '')

    the help description

  • buckets (Array<Numeric>) (defaults to: DEFAULT_BUCKETS)

    bucket boundaries (default: Prometheus defaults)



50
51
52
53
54
55
56
# File 'lib/philiprehberger/metric/histogram.rb', line 50

def initialize(name, help: '', buckets: DEFAULT_BUCKETS)
  @name = name
  @help = help
  @buckets = buckets.sort.freeze
  @mutex = Mutex.new
  @observations = {}
end

Instance Attribute Details

#bucketsArray<Numeric> (readonly)

Returns the bucket boundaries.

Returns:

  • (Array<Numeric>)

    the bucket boundaries



45
46
47
# File 'lib/philiprehberger/metric/histogram.rb', line 45

def buckets
  @buckets
end

#helpString (readonly)

Returns the help description.

Returns:

  • (String)

    the help description



42
43
44
# File 'lib/philiprehberger/metric/histogram.rb', line 42

def help
  @help
end

#nameString (readonly)

Returns the metric name.

Returns:

  • (String)

    the metric name



39
40
41
# File 'lib/philiprehberger/metric/histogram.rb', line 39

def name
  @name
end

Class Method Details

.exponential_buckets(start:, factor:, count:) ⇒ Array<Numeric>

Build an exponential sequence of bucket boundaries.

Parameters:

  • start (Numeric)

    first bucket upper bound (must be > 0)

  • factor (Numeric)

    multiplier between successive buckets (must be > 1)

  • count (Integer)

    number of buckets to generate

Returns:

  • (Array<Numeric>)

Raises:

  • (Error)

    if arguments are invalid



30
31
32
33
34
35
36
# File 'lib/philiprehberger/metric/histogram.rb', line 30

def self.exponential_buckets(start:, factor:, count:)
  raise Error, 'count must be positive' if count <= 0
  raise Error, 'start must be positive' if start <= 0
  raise Error, 'factor must be greater than 1' if factor <= 1

  Array.new(count) { |i| start * (factor**i) }
end

.linear_buckets(start:, width:, count:) ⇒ Array<Numeric>

Build a linear sequence of bucket boundaries.

Parameters:

  • start (Numeric)

    first bucket upper bound

  • width (Numeric)

    distance between buckets

  • count (Integer)

    number of buckets to generate

Returns:

  • (Array<Numeric>)

Raises:

  • (Error)

    if count is not positive



17
18
19
20
21
# File 'lib/philiprehberger/metric/histogram.rb', line 17

def self.linear_buckets(start:, width:, count:)
  raise Error, 'count must be positive' if count <= 0

  Array.new(count) { |i| start + (width * i) }
end

Instance Method Details

#get(labels: {}) ⇒ Hash

Get a snapshot for a specific label set.

Parameters:

  • labels (Hash) (defaults to: {})

    the label set

Returns:

  • (Hash)

    with :buckets, :sum, :count keys



80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/philiprehberger/metric/histogram.rb', line 80

def get(labels: {})
  key = labels.sort.to_h
  @mutex.synchronize do
    entry = @observations[key]
    return { buckets: {}, sum: 0.0, count: 0 } unless entry

    {
      buckets: entry[:buckets].dup,
      sum: entry[:sum],
      count: entry[:count]
    }
  end
end

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

This method returns an undefined value.

Observe a value.

Parameters:

  • value (Numeric)

    the observed value

  • labels (Hash) (defaults to: {})

    optional labels



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/philiprehberger/metric/histogram.rb', line 63

def observe(value, labels: {})
  key = labels.sort.to_h
  @mutex.synchronize do
    @observations[key] ||= { buckets: Hash.new(0), sum: 0.0, count: 0 }
    entry = @observations[key]
    entry[:sum] += value
    entry[:count] += 1
    @buckets.each do |bound|
      entry[:buckets][bound] += 1 if value <= bound
    end
  end
end

#resetvoid

This method returns an undefined value.

Reset all observations.



112
113
114
# File 'lib/philiprehberger/metric/histogram.rb', line 112

def reset
  @mutex.synchronize { @observations.clear }
end

#snapshotHash

Return a snapshot of all observations.

Returns:

  • (Hash)

    labels => observation data



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/philiprehberger/metric/histogram.rb', line 97

def snapshot
  @mutex.synchronize do
    @observations.transform_values do |entry|
      {
        buckets: entry[:buckets].dup,
        sum: entry[:sum],
        count: entry[:count]
      }
    end
  end
end

#typeString

Returns the metric type name.

Returns:

  • (String)

    the metric type name



117
118
119
# File 'lib/philiprehberger/metric/histogram.rb', line 117

def type
  'histogram'
end