Class: Raindrops::Middleware
- Inherits:
-
Object
- Object
- Raindrops::Middleware
- Defined in:
- lib/raindrops/middleware.rb
Overview
Raindrops::Middleware is Rack middleware that allows snapshotting current activity from an HTTP request. For all operating systems, it returns at least the following fields:
- calling - the number of application dispatchers on your machine
- writing - the number of clients being written to on your machine
Additional fields are available for Linux users.
It should be loaded at the top of Rack middleware stack before other middlewares for maximum accuracy.
Usage (Rainbows!/Unicorn preload_app=false)
If you're using preload_app=false (the default) in your Rainbows!/Unicorn config file, you'll need to create the global Stats object before forking.
require 'raindrops'
$stats ||= Raindrops::Middleware::Stats.new
In your Rack config.ru:
use Raindrops::Middleware, :stats => $stats
Usage (Rainbows!/Unicorn preload_app=true)
If you're using preload_app=true in your Rainbows!/Unicorn config file, just add the middleware to your stack:
In your Rack config.ru:
use Raindrops::Middleware
Linux-only extras!
To get bound listener statistics under Linux, you need to specify the listener names for your server. You can even include listen sockets for other servers on the same machine. This can be handy for monitoring your nginx proxy as well.
In your Rack config.ru, just pass the :listeners argument as an array of strings (along with any other arguments). You can specify any combination of TCP or Unix domain socket names:
use Raindrops::Middleware, :listeners => %w(0.0.0.0:80 /tmp/.sock)
If you're running Unicorn 0.98.0 or later, you don't have to pass in the :listeners array, Raindrops will automatically detect the listeners used by Unicorn master process. This does not detect listeners in different processes, of course.
The response body includes the following stats for each listener (see also Raindrops::ListenStats):
- active - total number of active clients on that listener
- queued - total number of queued (pre-accept()) clients on that listener
Demo Server
There is a server running this middleware (and Watcher) at https://yhbt.net/raindrops-demo/_raindrops
Also check out the Watcher demo at https://yhbt.net/raindrops-demo/
The demo server is only limited to 30 users, so be sure not to abuse it by using the /tail/ endpoint too much.
Defined Under Namespace
Classes: Proxy
Constant Summary collapse
- Stats =
A Raindrops::Struct used to count the number of :calling and :writing clients. This struct is intended to be shared across multiple processes and both counters are updated atomically.
This is supported on all operating systems supported by Raindrops
Raindrops::Struct.new(:calling, :writing)
Instance Attribute Summary collapse
-
#app ⇒ Object
:nodoc:.
-
#path ⇒ Object
:nodoc:.
-
#stats ⇒ Object
:nodoc:.
-
#tcp ⇒ Object
:nodoc:.
-
#unix ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#call(env) ⇒ Object
standard Rack endpoint.
-
#initialize(app, opts = {}) ⇒ Middleware
constructor
appmay be any Rack application, this middleware wraps it. -
#stats_response ⇒ Object
:nodoc:.
Constructor Details
#initialize(app, opts = {}) ⇒ Middleware
app may be any Rack application, this middleware wraps it.
opts is a hash that understands the following members:
- :stats - Raindrops::Middleware::Stats struct (default: Stats.new)
- :path - HTTP endpoint used for reading the stats (default: "/_raindrops")
- :listeners - array of host:port or socket paths (default: from Unicorn)
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/raindrops/middleware.rb', line 92 def initialize(app, opts = {}) @app = app @stats = opts[:stats] || Stats.new @path = opts[:path] || "/_raindrops" tmp = opts[:listeners] if tmp.nil? && defined?(Unicorn) && Unicorn.respond_to?(:listener_names) tmp = Unicorn.listener_names end @tcp = @unix = nil if tmp @tcp = tmp.grep(/\A.+:\d+\z/) @unix = tmp.grep(%r{\A/}) @tcp = nil if @tcp.empty? @unix = nil if @unix.empty? end end |
Instance Attribute Details
#app ⇒ Object
:nodoc:
73 74 75 |
# File 'lib/raindrops/middleware.rb', line 73 def app @app end |
#path ⇒ Object
:nodoc:
73 74 75 |
# File 'lib/raindrops/middleware.rb', line 73 def path @path end |
#stats ⇒ Object
:nodoc:
73 74 75 |
# File 'lib/raindrops/middleware.rb', line 73 def stats @stats end |
#tcp ⇒ Object
:nodoc:
73 74 75 |
# File 'lib/raindrops/middleware.rb', line 73 def tcp @tcp end |
#unix ⇒ Object
:nodoc:
73 74 75 |
# File 'lib/raindrops/middleware.rb', line 73 def unix @unix end |
Instance Method Details
#call(env) ⇒ Object
standard Rack endpoint
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/raindrops/middleware.rb', line 111 def call(env) # :nodoc: env['PATH_INFO'] == @path and return stats_response begin @stats.incr_calling status, headers, body = @app.call(env) rv = [ status, headers, Proxy.new(body, @stats) ] # the Rack server will start writing headers soon after this method @stats.incr_writing rv ensure @stats.decr_calling end end |
#stats_response ⇒ Object
:nodoc:
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/raindrops/middleware.rb', line 127 def stats_response # :nodoc: body = "calling: #{@stats.calling}\n" \ "writing: #{@stats.writing}\n" if defined?(Raindrops::Linux.tcp_listener_stats) Raindrops::Linux.tcp_listener_stats(@tcp).each do |addr,stats| body << "#{addr} active: #{stats.active}\n" \ "#{addr} queued: #{stats.queued}\n" end if @tcp Raindrops::Linux.unix_listener_stats(@unix).each do |addr,stats| body << "#{addr} active: #{stats.active}\n" \ "#{addr} queued: #{stats.queued}\n" end if @unix end headers = { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s, } [ 200, headers, [ body ] ] end |