raindrops.git  about / heads / tags
real-time stats for preforking Rack servers
blob f5cbb89cd6d260c7c84d67b459f2e799ea74f962 2053 bytes (raw)
$ git show v0.4.1:lib/raindrops/middleware.rb	# shows this blob on the CLI

 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
 
# -*- encoding: binary -*-
require 'raindrops'

# Raindrops middleware should be loaded at the top of Rack
# middleware stack before other middlewares for maximum accuracy.
class Raindrops
class Middleware < ::Struct.new(:app, :stats, :path, :tcp, :unix)

  # :stopdoc:
  Stats = Raindrops::Struct.new(:calling, :writing)
  PATH_INFO = "PATH_INFO"
  # :startdoc:

  def initialize(app, opts = {})
    super(app, opts[:stats] || Stats.new, opts[:path] || "/_raindrops")
    tmp = opts[:listeners]
    if tmp.nil? && defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
      tmp = Unicorn.listener_names
    end

    if tmp
      self.tcp = tmp.grep(/\A[^:]+:\d+\z/)
      self.unix = tmp.grep(%r{\A/})
      self.tcp = nil if tcp.empty?
      self.unix = nil if unix.empty?
    end
  end

  # standard Rack endpoint
  def call(env)
    env[PATH_INFO] == path ? stats_response : dup._call(env)
  end

  def _call(env)
    stats.incr_calling
    status, headers, self.app = app.call(env)

    # the Rack server will start writing headers soon after this method
    stats.incr_writing
    [ status, headers, self ]
    ensure
      stats.decr_calling
  end

  # yield to the Rack server here for writing
  def each(&block)
    app.each(&block)
  end

  # the Rack server should call this after #each (usually ensure-d)
  def close
    stats.decr_writing
    app.close if app.respond_to?(:close)
  end

  def stats_response
    body = "calling: #{stats.calling}\n" \
           "writing: #{stats.writing}\n"

    if defined?(Linux)
      Linux.tcp_listener_stats(tcp).each do |addr,stats|
        body << "#{addr} active: #{stats.active}\n" \
                "#{addr} queued: #{stats.queued}\n"
      end if tcp
      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

end
end

git clone https://yhbt.net/raindrops.git