Skip to content

Commit

Permalink
Improve rack middlewares
Browse files Browse the repository at this point in the history
* Move from prometheus/client/rack/* to rack/middleware/*
* Use histogram instead of summary in collector middleware
* Spepicfy collector metrics by changing the prefix to http_server_*
* Remove HTTP Host header from default label builder
  • Loading branch information
grobie committed Sep 9, 2016
1 parent 5e16541 commit 271d793
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 200 deletions.
18 changes: 9 additions & 9 deletions README.md
Expand Up @@ -35,7 +35,7 @@ http_requests.increment
### Rack middleware

There are two [Rack][2] middlewares available, one to expose a metrics HTTP
endpoint to be scraped by a prometheus server ([Exporter][9]) and one to trace all HTTP
endpoint to be scraped by a Prometheus server ([Exporter][9]) and one to trace all HTTP
requests ([Collector][10]).

It's highly recommended to enable gzip compression for the metrics endpoint,
Expand All @@ -45,14 +45,14 @@ for example by including the `Rack::Deflater` middleware.
# config.ru

require 'rack'
require 'prometheus/client/rack/collector'
require 'prometheus/client/rack/exporter'
require 'prometheus/middleware/collector'
require 'prometheus/middleware/exporter'

use Rack::Deflater, if: ->(env, status, headers, body) { body.any? && body[0].length > 512 }
use Prometheus::Client::Rack::Collector
use Prometheus::Client::Rack::Exporter
use Rack::Deflater, if: ->(_, _, _, body) { body.any? && body[0].length > 512 }
use Prometheus::Middleware::Collector
use Prometheus::Middleware::Exporter

run ->(env) { [200, {'Content-Type' => 'text/html'}, ['OK']] }
run ->(_) { [200, {'Content-Type' => 'text/html'}, ['OK']] }
```

Start the server and have a look at the metrics endpoint:
Expand Down Expand Up @@ -179,5 +179,5 @@ rake
[6]: https://codeclimate.com/github/prometheus/client_ruby.png
[7]: https://coveralls.io/repos/prometheus/client_ruby/badge.png?branch=master
[8]: https://github.com/prometheus/pushgateway
[9]: lib/prometheus/client/rack/exporter.rb
[10]: lib/prometheus/client/rack/collector.rb
[9]: lib/prometheus/middleware/exporter.rb
[10]: lib/prometheus/middleware/collector.rb
23 changes: 18 additions & 5 deletions examples/rack/config.ru
@@ -1,9 +1,22 @@
require 'rack'
require 'prometheus/client/rack/collector'
require 'prometheus/client/rack/exporter'
require 'prometheus/middleware/collector'
require 'prometheus/middleware/exporter'

use Rack::Deflater, if: ->(_, _, _, body) { body.any? && body[0].length > 512 }
use Prometheus::Client::Rack::Collector
use Prometheus::Client::Rack::Exporter
use Prometheus::Middleware::Collector
use Prometheus::Middleware::Exporter

run ->(_) { [200, { 'Content-Type' => 'text/html' }, ['OK']] }
srand

app = lambda do |_|
case rand
when 0..0.8
[200, { 'Content-Type' => 'text/html' }, ['OK']]
when 0.8..0.95
[404, { 'Content-Type' => 'text/html' }, ['Not Found']]
else
raise NoMethodError, 'It is a bug!'
end
end

run app
82 changes: 0 additions & 82 deletions lib/prometheus/client/rack/collector.rb

This file was deleted.

91 changes: 0 additions & 91 deletions lib/prometheus/client/rack/exporter.rb

This file was deleted.

79 changes: 79 additions & 0 deletions lib/prometheus/middleware/collector.rb
@@ -0,0 +1,79 @@
# encoding: UTF-8

require 'prometheus/client'

module Prometheus
module Middleware
# Collector is a Rack middleware that provides a sample implementation of
# a HTTP tracer. The default label builder can be modified to export a
# different set of labels per recorded metric.
class Collector
attr_reader :app, :registry

def initialize(app, options = {}, &label_builder)
@app = app
@registry = options[:registry] || Client.registry
@label_builder = label_builder || DEFAULT_LABEL_BUILDER

init_request_metrics
init_exception_metrics
end

def call(env) # :nodoc:
trace(env) { @app.call(env) }
end

protected

DEFAULT_LABEL_BUILDER = proc do |env|
{
method: env['REQUEST_METHOD'].downcase,
path: env['PATH_INFO'].to_s,
}
end

def init_request_metrics
@requests = @registry.counter(
:http_server_requests_total,
'The total number of HTTP requests handled by the Rack application.',
)
@durations = @registry.histogram(
:http_server_request_latency_seconds,
'The HTTP response latency of the Rack application.',
)
end

def init_exception_metrics
@exceptions = @registry.counter(
:http_server_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
record(labels(env, response), duration)
end
rescue => exception
@exceptions.increment(exception: exception.class.name)
raise
end

def labels(env, response)
@label_builder.call(env).tap do |labels|
labels[:code] = response.first.to_s
end
end

def record(labels, duration)
@requests.increment(labels)
@durations.observe(labels, duration)
rescue
# TODO: log unexpected exception during request recording
nil
end
end
end
end

0 comments on commit 271d793

Please sign in to comment.