1 # -*- encoding: binary -*-
4 # Raindrops::Middleware is Rack middleware that allows snapshotting
5 # current activity from an HTTP request. For all operating systems,
6 # it returns at least the following fields:
8 # * calling - the number of application dispatchers on your machine
9 # * writing - the number of clients being written to on your machine
11 # Additional fields are available for \Linux users.
13 # It should be loaded at the top of Rack middleware stack before other
14 # middlewares for maximum accuracy.
16 # === Usage (Rainbows!/Unicorn preload_app=false)
18 # If you're using preload_app=false (the default) in your Rainbows!/Unicorn
19 # config file, you'll need to create the global Stats object before
23 # $stats ||= Raindrops::Middleware::Stats.new
25 # In your Rack config.ru:
27 # use Raindrops::Middleware, :stats => $stats
29 # === Usage (Rainbows!/Unicorn preload_app=true)
31 # If you're using preload_app=true in your Rainbows!/Unicorn
32 # config file, just add the middleware to your stack:
34 # In your Rack config.ru:
36 # use Raindrops::Middleware
38 # === Linux-only extras!
40 # To get bound listener statistics under \Linux, you need to specify the
41 # listener names for your server. You can even include listen sockets for
42 # *other* servers on the same machine. This can be handy for monitoring
43 # your nginx proxy as well.
45 # In your Rack config.ru, just pass the :listeners argument as an array of
46 # strings (along with any other arguments). You can specify any
47 # combination of TCP or Unix domain socket names:
49 # use Raindrops::Middleware, :listeners => %w(0.0.0.0:80 /tmp/.sock)
51 # If you're running Unicorn 0.98.0 or later, you don't have to pass in
52 # the :listeners array, Raindrops will automatically detect the listeners
53 # used by Unicorn master process. This does not detect listeners in
54 # different processes, of course.
56 # The response body includes the following stats for each listener
57 # (see also Raindrops::ListenStats):
59 # * active - total number of active clients on that listener
60 # * queued - total number of queued (pre-accept()) clients on that listener
64 # There is a server running this middleware (and Watcher) at
65 # http://raindrops-demo.bogomips.org/_raindrops
67 # Also check out the Watcher demo at http://raindrops-demo.bogomips.org/
69 # The demo server is only limited to 30 users, so be sure not to abuse it
70 # by using the /tail/ endpoint too much.
72 class Raindrops::Middleware
73 attr_accessor :app, :stats, :path, :tcp, :unix # :nodoc:
75 # A Raindrops::Struct used to count the number of :calling and :writing
76 # clients. This struct is intended to be shared across multiple processes
77 # and both counters are updated atomically.
79 # This is supported on all operating systems supported by Raindrops
80 class Stats < Raindrops::Struct.new(:calling, :writing)
84 PATH_INFO = "PATH_INFO"
85 require "raindrops/middleware/proxy"
88 # +app+ may be any Rack application, this middleware wraps it.
89 # +opts+ is a hash that understands the following members:
91 # * :stats - Raindrops::Middleware::Stats struct (default: Stats.new)
92 # * :path - HTTP endpoint used for reading the stats (default: "/_raindrops")
93 # * :listeners - array of host:port or socket paths (default: from Unicorn)
94 def initialize(app, opts = {})
96 @stats = opts[:stats] || Stats.new
97 @path = opts[:path] || "/_raindrops"
98 tmp = opts[:listeners]
99 if tmp.nil? && defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
100 tmp = Unicorn.listener_names
105 @tcp = tmp.grep(/\A.+:\d+\z/)
106 @unix = tmp.grep(%r{\A/})
107 @tcp = nil if @tcp.empty?
108 @unix = nil if @unix.empty?
112 # standard Rack endpoint
113 def call(env) # :nodoc:
114 env[PATH_INFO] == @path and return stats_response
118 status, headers, body = @app.call(env)
119 rv = [ status, headers, Proxy.new(body, @stats) ]
121 # the Rack server will start writing headers soon after this method
129 def stats_response # :nodoc:
130 body = "calling: #{@stats.calling}\n" \
131 "writing: #{@stats.writing}\n"
133 if defined?(Raindrops::Linux)
134 Raindrops::Linux.tcp_listener_stats(@tcp).each do |addr,stats|
135 body << "#{addr} active: #{stats.active}\n" \
136 "#{addr} queued: #{stats.queued}\n"
138 Raindrops::Linux.unix_listener_stats(@unix).each do |addr,stats|
139 body << "#{addr} active: #{stats.active}\n" \
140 "#{addr} queued: #{stats.queued}\n"
145 "Content-Type" => "text/plain",
146 "Content-Length" => body.size.to_s,
148 [ 200, headers, [ body ] ]