gems: include vendored metrisk without hitime dependency

hitime is only needed for Metriks::Timer and requires native extensions.
Those do not compile with default settings under Ubuntu 26.04 or Alpine 3.24.
This commit is contained in:
cn authored and Christian Nicolai committed 2026-11-13 17:35:46 +01:00
1 parent 645469f41e
commit 7fa9f499e1
10 files changed
+502 -29

No files matched your search

+6
View File
@@ -2,9 +2,15 @@ plugins:
- rubocop-rake
- rubocop-rspec
inherit_mode:
merge: [Exclude]
AllCops:
TargetRubyVersion: '3.0'
NewCops: enable
Exclude:
- 'lib/metriks/**/*'
- 'lib/metriks.rb'
Gemspec/DevelopmentDependencies:
EnforcedStyle: gemspec
-1
View File
@@ -31,7 +31,6 @@ Gem::Specification.new do |s|
s.add_dependency 'async-dns', '~> 1.3.0'
s.add_dependency 'base64', '~> 0.3.0' # needed for async
s.add_dependency 'logger', '>= 1.6', '< 1.8'
s.add_dependency 'metriks'
s.add_dependency 'opentelemetry-exporter-jaeger', '~> 0.22.0'
s.add_dependency 'opentelemetry-instrumentation-rack', '~> 0.22.0'
s.add_dependency 'opentelemetry-sdk', '~> 1.2.0'
-28
View File
@@ -58,34 +58,6 @@ module Dyndnsd
:count, :one_minute_rate, :five_minute_rate,
:fifteen_minute_rate, :mean_rate
]
when Metriks::Counter
write_metric f, name, metric, [
:count
]
when Metriks::UtilizationTimer
write_metric f, name, metric, [
:count, :one_minute_rate, :five_minute_rate,
:fifteen_minute_rate, :mean_rate,
:min, :max, :mean, :stddev,
:one_minute_utilization, :five_minute_utilization,
:fifteen_minute_utilization, :mean_utilization
], [
:median, :get_95th_percentile
]
when Metriks::Timer
write_metric f, name, metric, [
:count, :one_minute_rate, :five_minute_rate,
:fifteen_minute_rate, :mean_rate,
:min, :max, :mean, :stddev
], [
:median, :get_95th_percentile
]
when Metriks::Histogram
write_metric f, name, metric, [
:count, :min, :max, :mean, :stddev
], [
:median, :get_95th_percentile
]
end
end
end
+27
View File
@@ -0,0 +1,27 @@
# Vendored from metriks 0.9.9.8 — https://github.com/eric/metriks
# Copyright (c) 2012 Eric Lindvall, MIT licensed.
# Trimmed to the subset dyndnsd uses (meters + registry + reporters);
# counter/gauge/timer/utilization_timer/histogram support was removed,
# and the 'atomic'/'avl_tree'/'hitimes' dependencies were replaced or dropped.
module Metriks
VERSION = '0.9.9.8'
def self.get(name)
Registry.default.get(name)
end
def self.meter(name)
Registry.default.meter(name)
end
def self.registry
Registry.default
end
end
require 'metriks/atomic'
require 'metriks/ewma'
require 'metriks/meter'
require 'metriks/registry'
require 'metriks/reporter/proc_title'
+34
View File
@@ -0,0 +1,34 @@
# Minimal pure-Ruby replacement for the 'atomic' gem, covering the exact API
# the vendored metriks code uses (Atomic.new, #value, #value=, #update,
# #compare_and_swap). Mutex-guarded instead of relying on a native extension.
module Metriks
class Atomic
def initialize(value = nil)
@value = value
@mutex = Mutex.new
end
def value
@mutex.synchronize { @value }
end
def value=(new_value)
@mutex.synchronize { @value = new_value }
end
def update
@mutex.synchronize { @value = yield(@value) }
end
def compare_and_swap(expected, new_value)
@mutex.synchronize do
if @value == expected
@value = new_value
true
else
false
end
end
end
end
end
+66
View File
@@ -0,0 +1,66 @@
# Vendored from metriks 0.9.9.8 — https://github.com/eric/metriks
# Copyright (c) 2012 Eric Lindvall, MIT licensed.
# Trimmed to the subset dyndnsd uses (meters + registry + reporters).
module Metriks
class EWMA
INTERVAL = 5.0
SECONDS_PER_MINUTE = 60.0
ONE_MINUTE = 1
FIVE_MINUTES = 5
FIFTEEN_MINUTES = 15
M1_ALPHA = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / ONE_MINUTE)
M5_ALPHA = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / FIVE_MINUTES)
M15_ALPHA = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / FIFTEEN_MINUTES)
def self.new_m1
new(M1_ALPHA, INTERVAL)
end
def self.new_m5
new(M5_ALPHA, INTERVAL)
end
def self.new_m15
new(M15_ALPHA, INTERVAL)
end
def initialize(alpha, interval)
@alpha = alpha
@interval = interval
@initialized = false
@rate = Atomic.new(0.0)
@uncounted = Atomic.new(0)
end
def clear
@initialized = false
@rate.value = 0.0
@uncounted.value = 0
end
def update(value)
@uncounted.update { |v| v + value }
end
def tick
count = @uncounted.swap(0)
instant_rate = count / @interval.to_f
if @initialized
@rate.update { |v| v + @alpha * (instant_rate - v) }
else
@rate.value = instant_rate
@initialized = true
end
end
def rate
@rate.value
end
end
end
+88
View File
@@ -0,0 +1,88 @@
# Vendored from metriks 0.9.9.8 — https://github.com/eric/metriks
# Copyright (c) 2012 Eric Lindvall, MIT licensed.
# Trimmed to the subset dyndnsd uses (meters + registry + reporters).
require 'metriks/ewma'
module Metriks
class Meter
TICK_INTERVAL = 5.0
def initialize(averager_klass = Metriks::EWMA)
@count = Atomic.new(0)
@start_time = Time.now.to_f
@last_tick = Atomic.new(@start_time)
@m1_rate = averager_klass.new_m1
@m5_rate = averager_klass.new_m5
@m15_rate = averager_klass.new_m15
end
def clear
@count.value = 0
@start_time = Time.now.to_f
@last_tick.value = @start_time
@m1_rate.clear
@m5_rate.clear
@m15_rate.clear
end
def tick
@m1_rate.tick
@m5_rate.tick
@m15_rate.tick
end
def tick_if_nessesary
old_tick = @last_tick.value
new_tick = Time.new.to_f
age = new_tick - old_tick
if age > TICK_INTERVAL && @last_tick.compare_and_swap(old_tick, new_tick)
required_ticks = age / TICK_INTERVAL
required_ticks.to_i.times do
tick
end
end
end
def mark(val = 1)
tick_if_nessesary
@count.update { |v| v + val }
@m1_rate.update(val)
@m5_rate.update(val)
@m15_rate.update(val)
end
def count
@count.value
end
def one_minute_rate
tick_if_nessesary
@m1_rate.rate
end
def five_minute_rate
tick_if_nessesary
@m5_rate.rate
end
def fifteen_minute_rate
tick_if_nessesary
@m15_rate.rate
end
def mean_rate
if count == 0
return 0.0
else
elapsed = Time.now.to_f - @start_time
count / elapsed
end
end
def stop
end
end
end
+127
View File
@@ -0,0 +1,127 @@
# Vendored from metriks 0.9.9.8 — https://github.com/eric/metriks
# Copyright (c) 2012 Eric Lindvall, MIT licensed.
# Trimmed to the subset dyndnsd uses (meters + registry + reporters).
require 'metriks/meter'
module Metriks
# Public: A collection of metrics
class Registry
# Public: The default registry for the process.
#
# Returns the default Registry for the process.
def self.default
@default ||= new
end
# Public: Initializes a new Registry.
def initialize
@mutex = Mutex.new
@metrics = {}
end
# Public: Clear all of the metrics in the Registry. This ensures all
# metrics that have been added are stopped.
#
# Returns nothing.
def clear
@mutex.synchronize do
@metrics.each do |key, metric|
metric.stop if metric.respond_to?(:stop)
end
@metrics = {}
end
end
# Public: Clear all of the metrics in the Registry. This has the same
# effect as calling #clear.
#
# Returns nothing.
def stop
clear
end
# Public: Iterate over all of the counters.
#
# Examples
#
# registry.each do |name, metric|
# puts name
# end
#
# Returns nothing.
def each(&block)
metrics = @mutex.synchronize do
@metrics.dup
end
metrics.each(&block)
end
# Public: Fetch or create a new meter metric. Meters are a counter that
# tracks throughput along with the count.
#
# name - The String name of the metric to define or fetch
#
# Examples
#
# registry.meter('resque.calls')
#
# Returns the Metriks::Meter identified by the name.
def meter(name)
add_or_get(name, Metriks::Meter)
end
# Public: Fetch an existing metric.
#
# name - The String name of the metric to fetch
#
# Examples
#
# registry.get('rack.utilization')
#
# Returns the metric or nil.
def get(name)
@mutex.synchronize do
@metrics[name]
end
end
# Public: Add a new metric.
#
# name - The String name of the metric to add
# metric - The metric instance to add
#
# Examples
#
# registry.add('method.calls', Metriks::Counter.new)
#
# Returns nothing.
# Raises RuntimeError if the metric name is already defined
def add(name, metric)
@mutex.synchronize do
if @metrics[name]
raise "Metric '#{name}' already defined"
else
@metrics[name] = metric
end
end
end
protected
def add_or_get(name, klass, &create_metric)
@mutex.synchronize do
if metric = @metrics[name]
if !metric.is_a?(klass)
raise "Metric already defined as '#{metric.class}'"
else
return metric
end
else
@metrics[name] = create_metric ? create_metric.call : klass.new
end
end
end
end
end
+91
View File
@@ -0,0 +1,91 @@
# Vendored from metriks 0.9.9.8 — https://github.com/eric/metriks
# Copyright (c) 2012 Eric Lindvall, MIT licensed.
# Trimmed to the subset dyndnsd uses (meters + registry + reporters).
require 'socket'
module Metriks::Reporter
class Graphite
attr_reader :host, :port
def initialize(host, port, options = {})
@host = host
@port = port
@prefix = options[:prefix]
@registry = options[:registry] || Metriks::Registry.default
@interval = options[:interval] || 60
@on_error = options[:on_error] || proc { |ex| }
end
def socket
@socket = nil if @socket && @socket.closed?
@socket ||= TCPSocket.new(@host, @port)
end
def start
@thread ||= Thread.new do
loop do
sleep @interval
Thread.new do
begin
write
rescue Exception => ex
@on_error[ex] rescue nil
end
end
end
end
end
def stop
@thread.kill if @thread
@thread = nil
end
def restart
stop
start
end
def write
@registry.each do |name, metric|
case metric
when Metriks::Meter
write_metric name, metric, [
:count, :one_minute_rate, :five_minute_rate,
:fifteen_minute_rate, :mean_rate
]
end
end
end
def write_metric(base_name, metric, keys, snapshot_keys = [])
time = Time.now.to_i
base_name = base_name.to_s.gsub(/ +/, '_')
if @prefix
base_name = "#{@prefix}.#{base_name}"
end
keys.flatten.each do |key|
name = key.to_s.gsub(/^get_/, '')
value = metric.send(key)
socket.write("#{base_name}.#{name} #{value} #{time}\n")
end
unless snapshot_keys.empty?
snapshot = metric.snapshot
snapshot_keys.flatten.each do |key|
name = key.to_s.gsub(/^get_/, '')
value = snapshot.send(key)
socket.write("#{base_name}.#{name} #{value} #{time}\n")
end
end
rescue Errno::EPIPE
socket.close
end
end
end
+63
View File
@@ -0,0 +1,63 @@
# Vendored from metriks 0.9.9.8 — https://github.com/eric/metriks
# Copyright (c) 2012 Eric Lindvall, MIT licensed.
# Trimmed to the subset dyndnsd uses (meters + registry + reporters).
module Metriks::Reporter
class ProcTitle
def initialize(options = {})
@rounding = options[:rounding] || 1
@prefix = options[:prefix] || $0.dup
@interval = options[:interval] || 5
@on_error = options[:on_error] || proc { |ex| }
@metrics = []
end
def add(name, suffix = nil, &block)
@metrics << [ name, suffix, block ]
end
def empty?
@metrics.empty?
end
def start
@thread ||= Thread.new do
loop do
begin
unless @metrics.empty?
title = generate_title
if title && !title.empty?
$0 = "#{@prefix} #{title}"
end
end
rescue Exception => ex
@on_error[ex] rescue nil
end
sleep @interval
end
end
end
def stop
@thread.kill if @thread
@thread = nil
end
def restart
stop
start
end
protected
def generate_title
@metrics.collect do |name, suffix, block|
val = block.call
val = "%.#{@rounding}f" % val if val.is_a?(Float)
"#{name}: #{val}#{suffix}"
end.join(' ')
end
end
end