File: process_utilization.rb

package info (click to toggle)
ruby-peek-performance-bar 1.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 128 kB
  • sloc: javascript: 129; ruby: 108; makefile: 4
file content (127 lines) | stat: -rw-r--r-- 3,944 bytes parent folder | download | duplicates (3)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
require "rack/body_proxy"

module Peek
  module Views
    class PerformanceBar
      # Middleware that tracks the amount of time this process spends processing
      # requests, as opposed to being idle waiting for a connection. Statistics
      # are dumped to rack.errors every 5 minutes.
      #
      # NOTE This middleware is not thread safe. It should only be used when
      # rack.multiprocess is true and rack.multithread is false.
      class ProcessUtilization
        class << self
          # The instance of this middleware in a single-threaded production server.
          # Useful for fetching stats about the current request:
          #
          #   o = Rack::ProcessUtilization.singleton
          #   time, calls = o.gc_stats if o.track_gc?
          attr_accessor :singleton
        end

        def initialize(app, opts={})
          @app = app
          @window = opts[:window] || 100
          @horizon = nil
          @requests = nil
          @active_time = nil
          @total_requests = 0

          self.class.singleton = self
        end

        # time when we began sampling. this is reset every once in a while so
        # averages don't skew over time.
        attr_accessor :horizon

        # total number of requests that have been processed by this worker since
        # the horizon time.
        attr_accessor :requests

        # decimal number of seconds the worker has been active within a request
        # since the horizon time.
        attr_accessor :active_time

        # total requests processed by this worker process since it started
        attr_accessor :total_requests

        # the amount of time since the horizon
        def horizon_time
          Time.now - horizon
        end

        # decimal number of seconds this process has been active since the horizon
        # time. This is the inverse of the active time.
        def idle_time
          horizon_time - active_time
        end

        # percentage of time this process has been active since the horizon time.
        def percentage_active
          (active_time / horizon_time) * 100
        end

        # percentage of time this process has been idle since the horizon time.
        def percentage_idle
          (idle_time / horizon_time) * 100
        end

        # number of requests processed per second since the horizon
        def requests_per_second
          requests / horizon_time
        end

        # average response time since the horizon in milliseconds
        def average_response_time
          (active_time / requests.to_f) * 1000
        end

        # called exactly once before the first request is processed by a worker
        def first_request
          reset_horizon
        end

        # reset various counters before the new request
        def reset_stats
          @start = Time.now
        end

        # resets the horizon and all dependent variables
        def reset_horizon
          @horizon = Time.now
          @active_time = 0.0
          @requests = 0
        end

        # called immediately after a request to record statistics, update the
        # procline, and dump information to the logfile
        def record_request
          now = Time.now
          diff = (now - @start)
          @active_time += diff
          @requests += 1

          reset_horizon if now - horizon > @window
        rescue => boom
          warn "ProcessUtilization#record_request failed: #{boom.inspect}"
        end

        # Rack entry point.
        def call(env)
          @env = env
          reset_stats

          @total_requests += 1
          first_request if @total_requests == 1

          env['process.request_start'] = @start.to_f
          env['process.total_requests'] = total_requests

          status, headers, body = @app.call(env)
          body = Rack::BodyProxy.new(body) { record_request }
          [status, headers, body]
        end
      end
    end
  end
end