forked from klippx/client_ruby
-
Notifications
You must be signed in to change notification settings - Fork 1
/
collector.rb
103 lines (90 loc) · 3.1 KB
/
collector.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# encoding: UTF-8
require 'prometheus/client'
module Prometheus
module Middleware
# Collector is a Rack middleware that provides a sample implementation of a
# HTTP tracer.
#
# By default metrics are registered on the global registry. Set the
# `:registry` option to use a custom registry.
#
# By default metrics all have the prefix "http_server". Set to something
# else if you like.
#
# The request counter metric is broken down by code, method and path by
# default. Set the `:counter_label_builder` option to use a custom label
# builder.
#
# The request duration metric is broken down by method and path by default.
# Set the `:duration_label_builder` option to use a custom label builder.
class Collector
attr_reader :app, :registry
def initialize(app, options = {})
@app = app
@registry = options[:registry] || Client.registry
@metrics_prefix = options[:metrics_prefix] || 'http_server'
@counter_lb = options[:counter_label_builder] || COUNTER_LB
@duration_lb = options[:duration_label_builder] || DURATION_LB
init_request_metrics
init_exception_metrics
end
def call(env) # :nodoc:
trace(env) { @app.call(env) }
end
protected
# rubocop:disable Metrics/LineLength
aggregation = lambda do |str|
str
.gsub(%r{/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(/|$)}, '/:uuid\\1')
.gsub(%r{/\d+(/|$)}, '/:id\\1')
end
# rubocop:enable Metrics/LineLength
COUNTER_LB = proc do |env, code|
{
code: code,
method: env['REQUEST_METHOD'].downcase,
path: aggregation.call(env['PATH_INFO']),
}
end
DURATION_LB = proc do |env, _|
{
method: env['REQUEST_METHOD'].downcase,
path: aggregation.call(env['PATH_INFO']),
}
end
def init_request_metrics
@requests = @registry.counter(
:"#{@metrics_prefix}_requests_total",
'The total number of HTTP requests handled by the Rack application.',
)
@durations = @registry.histogram(
:"#{@metrics_prefix}_request_duration_seconds",
'The HTTP response duration of the Rack application.',
)
end
def init_exception_metrics
@exceptions = @registry.counter(
:"#{@metrics_prefix}_exceptions_total",
'The total number of exceptions raised by the Rack application.',
)
end
def trace(env)
start = Time.now
yield.tap do |response|
duration = [(Time.now - start).to_f, 0.0].max
record(env, response.first.to_s, duration)
end
rescue => exception
@exceptions.increment(exception: exception.class.name)
raise
end
def record(env, code, duration)
@requests.increment(@counter_lb.call(env, code))
@durations.observe(@duration_lb.call(env, code), duration)
rescue
# TODO: log unexpected exception during request recording
nil
end
end
end
end