rack-timeout 0.1.0beta → 0.1.0beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/README.markdown +164 -3
- data/lib/rack-timeout.rb +6 -3
- data/lib/rack/timeout.rb +65 -33
- data/lib/rack/timeout/logger.rb +72 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZGY0YjFmYWYzMTAzY2E4NWUzMWQyM2ZlN2YwY2E2Yzk0ZGVkZGRlNA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MjE3NDM5ZTdlMWU4NTQ0OGEzNGYyYzU2MTc4MjczNTNhZDhhYWE0Mw==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YTVlYTViYjc0ZDdlYzBiYjNhYzJiOTc4ZTQ4OTVlYjNkOWM5MTFmMmRmNzIw
|
10
|
+
MTM0OWI3ZWVmODRiY2M3ZDAwMGY3OGViNmJkMTZiYzlkYzI0ZTJhYThhYTEy
|
11
|
+
MWJlNTUzZTZjYzc2Y2ViMThlMjJlMDdiMTM5Mzg0MzNjMzM0ZTc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTc3Yzk3MmE4YWE0NjQwNjliNjNmZmY1Zjc0ZTdjODYwYThhYmJjNjI0NTRh
|
14
|
+
ZTk5ZGU0YjE1YmQ3ZWY0YTg4N2I1MWUwOGExYTk4M2Q1ZGI0NWRmM2YxMjAw
|
15
|
+
MjFiMzJlOTQ0NjVkYWZmMWRhYzMzN2UwOGRmYjkwZDc0MTc0MjA=
|
data/README.markdown
CHANGED
@@ -7,7 +7,8 @@ Abort requests that are taking too long; a `Rack::Timeout::Error` will be raised
|
|
7
7
|
Usage
|
8
8
|
-----
|
9
9
|
|
10
|
-
Setup for current versions of Rails, Rack, Ruby, and Bundler. See
|
10
|
+
Setup for current versions of Rails, Rack, Ruby, and Bundler. See the Compatibility section at the
|
11
|
+
end for legacy versions.
|
11
12
|
|
12
13
|
### Rails apps
|
13
14
|
|
@@ -23,11 +24,171 @@ create an initializer file:
|
|
23
24
|
### Sinatra and other Rack apps
|
24
25
|
|
25
26
|
# config.ru
|
26
|
-
require "rack
|
27
|
+
require "rack-timeout"
|
27
28
|
use Rack::Timeout # Call as early as possible so rack-timeout runs before other middleware.
|
28
29
|
Rack::Timeout.timeout = 10 # This line is optional. If omitted, timeout defaults to 15 seconds.
|
29
30
|
|
30
31
|
|
32
|
+
Heroku Niceties
|
33
|
+
---------------
|
34
|
+
|
35
|
+
* Normally, Rack::Timeout always times out a request using the `Rack::Timeout.timeout` setting.
|
36
|
+
Heroku offers the [`X-Request-Start`][X-Request-Start] HTTP header, which is a timestamp
|
37
|
+
indicating the time the request first enters the routing infrastructure.
|
38
|
+
|
39
|
+
If the `X-Request-Start` HTTP header is present, Rack::Timeout will take the age of the request
|
40
|
+
into consideration when determining the timeout to use. If a request is older than 30 seconds,
|
41
|
+
it's dropped immediately. Otherwise, the timeout is the number of seconds left to 30 seconds,
|
42
|
+
or the value of `Rack::Timeout.timeout`, whichever is shorter.
|
43
|
+
|
44
|
+
So, if a request has been sitting in the queue for 25s, and `Rack::Timeout.timeout` is set to
|
45
|
+
10s, the timeout used will be 5s, because `30 − 25 = 5`, and `5 < 10`.
|
46
|
+
|
47
|
+
The reasoning for this behavior is that the Heroku router drops requests if no response is
|
48
|
+
received within 30s, so it makes no sense for the application to process a request it'll never
|
49
|
+
be able to respond to.
|
50
|
+
|
51
|
+
The 30s maximum age is set in in `Rack::Timeout::MAX_REQUEST_AGE`, and should generally not be
|
52
|
+
altered.
|
53
|
+
|
54
|
+
* With every line logged, Rack::Timeout includes a request ID. Generally it'll generate its own
|
55
|
+
ID, but before that, it'll look for the `Heroku-Request-ID` header. If present, this is the ID
|
56
|
+
that'll get logged.
|
57
|
+
|
58
|
+
`Heroku-Request-ID` is not present by default on Heroku apps, but can be enabled through the
|
59
|
+
[http-request-id labs feature][http-request-id]. It's recommended to enable http-request-id as
|
60
|
+
it allows one to correlate Rack::Timeout events with the Heroku router's events. There are no
|
61
|
+
downsides to enabling http-request-id.
|
62
|
+
|
63
|
+
[X-Request-Start]: https://devcenter.heroku.com/articles/http-routing#heroku-headers
|
64
|
+
[http-request-id]: https://devcenter.heroku.com/articles/http-request-id
|
65
|
+
|
66
|
+
Both these features are strictly reliant on the presence of the HTTP headers and make no effort to
|
67
|
+
determine if the app is actually running on Heroku.
|
68
|
+
|
69
|
+
|
70
|
+
Request Lifetime
|
71
|
+
----------------
|
72
|
+
|
73
|
+
Throughout a request's lifetime, Rack::Timeout keeps details about the request in
|
74
|
+
`env[Rack::Timeout::ENV_INFO_KEY]`, or, more explicitly, `env["rack-timeout.info"]`.
|
75
|
+
|
76
|
+
The value of that entry is an instance of `Rack::Timeout::RequestDetails`, which is a `Struct`
|
77
|
+
containing the following fields:
|
78
|
+
|
79
|
+
* `id`: a unique ID per request. Either `Heroku-Request-ID` or a random ID generated internally.
|
80
|
+
|
81
|
+
* `age`: time in seconds since `X-Request-Start` when the request is first seen by Rack::Timeout.
|
82
|
+
Only set if `X-Request-Start` is present.
|
83
|
+
|
84
|
+
* `timeout`: timeout to be used, in seconds. Generally `Rack::Timeout.timeout`, unless
|
85
|
+
`X-Request-Start` is present. See discussion above under the Heroku Niceties section.
|
86
|
+
|
87
|
+
* `duration`: set after a request completes (or times out). The time in seconds it took.
|
88
|
+
|
89
|
+
* `state`: the possible states are:
|
90
|
+
|
91
|
+
* `expired`: the request is considered too old and is skipped entirely. This happens when
|
92
|
+
`X-Request-Start` is present and older than 30s. When this happens, a
|
93
|
+
`Rack::Timeout::RequestExpiryError` exception is raised.
|
94
|
+
|
95
|
+
* `ready`: this is the initial state a request is in, before it's passed down the middleware
|
96
|
+
chain. After that, it'll either end up as `timed_out` or `completed`.
|
97
|
+
|
98
|
+
* `timed_out`: the request had run for longer than the determined timeout and was aborted. A
|
99
|
+
`Rack::Timeout::RequestTimeoutError` error is raised in the application when this occurs.
|
100
|
+
|
101
|
+
* `completed`: the request completed in time and Rack::Timeout is done with it. This does not
|
102
|
+
mean the request completed *successfully*. Rack::Timeout does not concern itself with that.
|
103
|
+
|
104
|
+
|
105
|
+
Errors
|
106
|
+
------
|
107
|
+
|
108
|
+
Rack::Timeout can raise two types of exceptions. Both descend from `Rack::Timeout::Error`, which
|
109
|
+
itself descends from `RuntimeError`. They are:
|
110
|
+
|
111
|
+
* `Rack::Timeout::RequestTimeoutError`: this is raised when a request has run for longer than the
|
112
|
+
specified timeout. This is raised in the application thread, as per the `::Timeout.timeout`
|
113
|
+
semantics, and can generally be caught within the application.
|
114
|
+
|
115
|
+
* `Rack::Timeout::RequestExpiryError`: this is raised when a request is skipped for being too old
|
116
|
+
(see the X-Request-Start bit under the Heroku Niceties section). This cannot generally be
|
117
|
+
rescued from in a Rails controller action as it happens before the request has a chance to reach
|
118
|
+
Rails.
|
119
|
+
|
120
|
+
This shouldn't be any different for other frameworks, unless you have something above
|
121
|
+
Rack::Timeout in the middleware stack, which you generally shouldn't.
|
122
|
+
|
123
|
+
You shouldn't generally care about rescuing from these errors. Instead, you can subscribe for state
|
124
|
+
change notifications with observers.
|
125
|
+
|
126
|
+
|
127
|
+
Observers
|
128
|
+
---------
|
129
|
+
|
130
|
+
Observers are objects or blocks that are notified about state changes during a request lifetime.
|
131
|
+
|
132
|
+
You can register an observer easily with a block:
|
133
|
+
|
134
|
+
Rack::Timeout.register_state_change_observer(:a_unique_name) { |env| do_things env }
|
135
|
+
|
136
|
+
or by passing an object that responds to `rack_timeout_request_did_change_state_in(env)`:
|
137
|
+
|
138
|
+
class MyObserver
|
139
|
+
def rack_timeout_request_did_change_state_in(env)
|
140
|
+
# ... do stuff ...
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
Rack::Timeout.register_state_change_observer(:another_name, MyObserver.new)
|
145
|
+
|
146
|
+
This is how logging is implemented, too. See `Rack::Timeout::StateChangeLogger`.
|
147
|
+
|
148
|
+
You can remove an observer with `unregister_state_change_observer`:
|
149
|
+
|
150
|
+
Rack::Timeout.unregister_state_change_observer(:a_unique_name)
|
151
|
+
|
152
|
+
Custom observers might be used to store statistics on request length, timeouts, etc., and
|
153
|
+
potentially do performance tuning on the fly.
|
154
|
+
|
155
|
+
|
156
|
+
Logging
|
157
|
+
-------
|
158
|
+
|
159
|
+
Rack::Timeout logs a line every time there's a change in state in a request's lifetime.
|
160
|
+
|
161
|
+
Changes into `timed_out` and `expired` are logged at the `ERROR` level, everything else is `INFO`.
|
162
|
+
|
163
|
+
The default log level for Rack::Timeout is `INFO`, but can be affected via:
|
164
|
+
|
165
|
+
* Unix environment variables. First `RACK_TIMEOUT_LOG_LEVEL` is checked, then `LOG_LEVEL`. Their
|
166
|
+
value must be name of a predefined constant in ruby's `Logger` class, e.g. `INFO` or `DEBUG`.
|
167
|
+
Case is not significant.
|
168
|
+
|
169
|
+
* By setting `Rack::Timeout.logger.level` directly, e.g.:
|
170
|
+
|
171
|
+
Rack::Timeout.logger.level = ::Logger::DEBUG
|
172
|
+
|
173
|
+
Logging is enabled by default if Rack::Timeout is loaded via the `rack-timeout` file (recommended),
|
174
|
+
but can be removed by unregistering its observer:
|
175
|
+
|
176
|
+
Rack::Timeout.unregister_state_change_observer(:logger)
|
177
|
+
|
178
|
+
Each log line is a set of `key=value` pairs, containing the entries from the
|
179
|
+
`env["rack-timeout.info"]` struct that are not `nil`. See the Request Lifetime section above for a
|
180
|
+
description of each field. Note that while the values for `age`, `timeout`, and `duration` are
|
181
|
+
stored internally as seconds, they are logged as milliseconds for readability.
|
182
|
+
|
183
|
+
A sample log excerpt might look like:
|
184
|
+
|
185
|
+
source=rack-timeout id=13793c age=369ms timeout=10000ms state=ready at=info
|
186
|
+
source=rack-timeout id=13793c age=369ms timeout=10000ms duration=15ms state=completed at=info
|
187
|
+
source=rack-timeout id=ea7bd3 age=371ms timeout=10000ms state=timed_out at=error
|
188
|
+
|
189
|
+
(IDs shortened for readability.)
|
190
|
+
|
191
|
+
|
31
192
|
Compatibility
|
32
193
|
-------------
|
33
194
|
|
@@ -39,7 +200,7 @@ For applications running Ruby 1.8.x and/or Rails 2.x, use [version 0.0.4][v0.0.4
|
|
39
200
|
[v0.0.4]: https://github.com/kch/rack-timeout/tree/v0.0.4
|
40
201
|
|
41
202
|
|
42
|
-
Here
|
203
|
+
Here Be Dragons
|
43
204
|
---------------
|
44
205
|
|
45
206
|
* Ruby's Timeout rely on threads. If your app or any of the libraries it depends on is not
|
data/lib/rack-timeout.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require 'rack/timeout'
|
3
|
+
require 'rack/timeout/logger'
|
3
4
|
|
4
5
|
if defined?(Rails) && [3,4].include?(Rails::VERSION::MAJOR)
|
5
6
|
class Rack::Timeout::Railtie < Rails::Railtie
|
6
|
-
initializer('rack-timeout.prepend')
|
7
|
-
initializer('rack-timeout.
|
7
|
+
initializer('rack-timeout.prepend') { |app| app.config.middleware.insert 0, Rack::Timeout }
|
8
|
+
initializer('rack-timeout.tracker') { |app| app.config.middleware.use Rack::Timeout::TimeoutTracker }
|
8
9
|
end
|
9
10
|
end
|
11
|
+
|
12
|
+
Rack::Timeout::StateChangeLogger.register!
|
data/lib/rack/timeout.rb
CHANGED
@@ -5,13 +5,14 @@ require 'securerandom'
|
|
5
5
|
module Rack
|
6
6
|
class Timeout
|
7
7
|
class Error < RuntimeError; end
|
8
|
-
class
|
9
|
-
class
|
8
|
+
class RequestExpiryError < Error; end
|
9
|
+
class RequestTimeoutError < Error; end
|
10
10
|
|
11
|
-
|
11
|
+
RequestDetails = Struct.new(:id, :age, :timeout, :duration, :state)
|
12
12
|
ENV_INFO_KEY = 'rack-timeout.info'
|
13
|
-
FRAMEWORK_ERROR_KEYS = %w(sinatra.error rack.exception)
|
14
|
-
FINAL_STATES = [:
|
13
|
+
FRAMEWORK_ERROR_KEYS = %w(sinatra.error rack.exception) # No idea what actually sets rack.exception but a lot of other libraries seem to reference it.
|
14
|
+
FINAL_STATES = [:expired, :timed_out, :completed]
|
15
|
+
ACCEPTABLE_STATES = [:ready] + FINAL_STATES
|
15
16
|
MAX_REQUEST_AGE = 30 # seconds
|
16
17
|
|
17
18
|
@timeout = 15
|
@@ -24,7 +25,7 @@ module Rack
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def call(env)
|
27
|
-
info = env[ENV_INFO_KEY] ||=
|
28
|
+
info = env[ENV_INFO_KEY] ||= RequestDetails.new
|
28
29
|
info.id ||= env['HTTP_HEROKU_REQUEST_ID'] || SecureRandom.hex
|
29
30
|
request_start = env['HTTP_X_REQUEST_START'] # unix timestamp in ms
|
30
31
|
request_start = Time.at(request_start.to_i / 1000) if request_start
|
@@ -33,57 +34,88 @@ module Rack
|
|
33
34
|
info.timeout = [self.class.timeout, time_left].compact.select { |n| n >= 0 }.min
|
34
35
|
|
35
36
|
if time_left && time_left <= 0
|
36
|
-
Rack::Timeout.
|
37
|
-
raise
|
37
|
+
Rack::Timeout._set_state! env, :expired
|
38
|
+
raise RequestExpiryError
|
38
39
|
end
|
39
40
|
|
40
|
-
Rack::Timeout.
|
41
|
-
::Timeout.timeout(info.timeout,
|
41
|
+
Rack::Timeout._set_state! env, :ready
|
42
|
+
::Timeout.timeout(info.timeout, RequestTimeoutError) do
|
42
43
|
ready_time = Time.now
|
43
|
-
response = Rack::Timeout.
|
44
|
+
response = Rack::Timeout._perform_block_tracking_timeout_to_env(env) { @app.call(env) }
|
44
45
|
info.duration = Time.now - ready_time
|
45
|
-
Rack::Timeout.
|
46
|
+
Rack::Timeout._set_state! env, :completed
|
46
47
|
response
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
+
# used in #call and TimeoutTracker
|
52
|
+
def self._perform_block_tracking_timeout_to_env(env)
|
51
53
|
yield
|
52
|
-
rescue
|
53
|
-
|
54
|
+
rescue RequestTimeoutError
|
55
|
+
timed_out = true
|
54
56
|
raise
|
55
57
|
ensure
|
56
|
-
|
58
|
+
# I do not appreciate having to handle framework business in a rack-level library, but can't see another way around sinatra's error handling.
|
59
|
+
timed_out ||= env.values_at(*FRAMEWORK_ERROR_KEYS).any? { |e| e.is_a? RequestTimeoutError }
|
60
|
+
_set_state! env, :timed_out if timed_out
|
57
61
|
end
|
58
62
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
def self.set_state_and_log!(info, state)
|
63
|
+
# used internally
|
64
|
+
def self._set_state!(env, state)
|
65
|
+
raise "Invalid state: #{state.inspect}" unless ACCEPTABLE_STATES.include? state
|
66
|
+
info = env[ENV_INFO_KEY]
|
64
67
|
return if FINAL_STATES.include? info.state
|
65
68
|
info.state = state
|
66
|
-
|
67
|
-
s = 'source=rack-timeout'
|
68
|
-
s << ' id=' << info.id if info.id
|
69
|
-
s << ' age=' << ms[info.age] if info.age
|
70
|
-
s << ' timeout=' << ms[info.timeout] if info.timeout
|
71
|
-
s << ' duration=' << ms[info.duration] if info.duration
|
72
|
-
s << ' state=' << info.state.to_s if info.state
|
73
|
-
s << "\n"
|
74
|
-
|
75
|
-
$stderr << s
|
69
|
+
notify_state_change_observers(env)
|
76
70
|
end
|
77
71
|
|
78
|
-
|
72
|
+
# A second middleware to be added last in rails; ensures timed_out states get intercepted properly.
|
73
|
+
# This works as long as it's after ActionDispatch::ShowExceptions and ActionDispatch::DebugExceptions in the middleware list, which happens normally when added via `app.config.middleware.use`.
|
74
|
+
class TimeoutTracker
|
79
75
|
def initialize(app)
|
80
76
|
@app = app
|
81
77
|
end
|
82
78
|
|
83
79
|
def call(env)
|
84
|
-
Rack::Timeout.
|
80
|
+
Rack::Timeout._perform_block_tracking_timeout_to_env(env) { @app.call(env) }
|
85
81
|
end
|
86
82
|
end
|
87
83
|
|
84
|
+
|
85
|
+
### state change notification-related methods
|
86
|
+
|
87
|
+
OBSERVER_CALLBACK_METHOD_NAME = :rack_timeout_request_did_change_state_in
|
88
|
+
@state_change_observers = {}
|
89
|
+
|
90
|
+
# Registers an object or a block to be called back when a request changes state in rack-timeout.
|
91
|
+
#
|
92
|
+
# `id` is anything that uniquely identifies this particular callback, mostly so it may be removed via `unregister_state_change_observer`.
|
93
|
+
#
|
94
|
+
# The second parameter can be either an object that responds to `rack_timeout_request_did_change_state_in(env)` or a block. The object and the block cannot be both specified at the same time.
|
95
|
+
#
|
96
|
+
# Example calls:
|
97
|
+
# Rack::Timeout.register_state_change_observer(:foo_reporter, FooStateReporter.new)
|
98
|
+
# Rack::Timeout.register_state_change_observer(:bar) { |env| do_bar_things(env) }
|
99
|
+
def self.register_state_change_observer(id, object = nil, &callback)
|
100
|
+
raise RuntimeError, "An observer with the id #{id.inspect} is already set." if @state_change_observers.key? id
|
101
|
+
raise ArgumentError, "Pass either a callback object or a block; never both." unless [object, callback].compact.length == 1
|
102
|
+
raise RuntimeError, "Object must respond to rack_timeout_request_did_change_state_in" if object && !object.respond_to?(OBSERVER_CALLBACK_METHOD_NAME)
|
103
|
+
callback.singleton_class.send :alias_method, OBSERVER_CALLBACK_METHOD_NAME, :call if callback
|
104
|
+
@state_change_observers[id] = object || callback
|
105
|
+
end
|
106
|
+
|
107
|
+
# Removes the observer with the given id
|
108
|
+
def self.unregister_state_change_observer(id)
|
109
|
+
@state_change_observers.delete id
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# Sends out the notifications. Called internally at the end of `set_state!`
|
116
|
+
def self.notify_state_change_observers(env)
|
117
|
+
@state_change_observers.values.each { |observer| observer.send(OBSERVER_CALLBACK_METHOD_NAME, env) }
|
118
|
+
end
|
119
|
+
|
88
120
|
end
|
89
121
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class Timeout
|
5
|
+
|
6
|
+
# convenience method so the current logger can be accessed via Rack::Timeout.logger
|
7
|
+
def self.logger
|
8
|
+
@state_change_observers[:logger]
|
9
|
+
end
|
10
|
+
|
11
|
+
class StateChangeLogger < ::Logger
|
12
|
+
SIMPLE_FORMATTER = ->(severity, timestamp, progname, msg) { "#{msg} at=#{severity.downcase}\n" }
|
13
|
+
DEFAULT_LEVEL = INFO
|
14
|
+
STATE_LOG_LEVEL = { ready: INFO,
|
15
|
+
completed: INFO,
|
16
|
+
expired: ERROR,
|
17
|
+
timed_out: ERROR,
|
18
|
+
}
|
19
|
+
|
20
|
+
|
21
|
+
# creates a logger and registers for state change notifications in Rack::Timeout
|
22
|
+
def self.register!(*a)
|
23
|
+
new(*a).register!
|
24
|
+
end
|
25
|
+
|
26
|
+
# registers for state change notifications in Rack::Timeout
|
27
|
+
def register!(target = ::Rack::Timeout)
|
28
|
+
target.register_state_change_observer(:logger, self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(device = $stderr, *a)
|
32
|
+
super(device, *a)
|
33
|
+
self.formatter = SIMPLE_FORMATTER
|
34
|
+
self.level = self.class.determine_level
|
35
|
+
end
|
36
|
+
|
37
|
+
# callback method from Rack::Timeout state change notifications
|
38
|
+
def rack_timeout_request_did_change_state_in(env)
|
39
|
+
log_state_change(env[ENV_INFO_KEY])
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# log level is, by precedence, one of: $RACK_TIMEOUT_LOG_LEVEL > $LOG_LEVEL > INFO
|
46
|
+
def self.determine_level
|
47
|
+
env_log_level = ENV.values_at("RACK_TIMEOUT_LOG_LEVEL", "LOG_LEVEL").compact.map(&:upcase).first
|
48
|
+
env_log_level = const_get(env_log_level) if env_log_level && const_defined?(env_log_level)
|
49
|
+
env_log_level || DEFAULT_LEVEL
|
50
|
+
end
|
51
|
+
|
52
|
+
# helper method used for formatting in #log_state_change
|
53
|
+
def ms(s)
|
54
|
+
'%.fms' % (s * 1000)
|
55
|
+
end
|
56
|
+
|
57
|
+
# generates the actual log string
|
58
|
+
def log_state_change(info)
|
59
|
+
add(STATE_LOG_LEVEL[info.state]) do
|
60
|
+
s = 'source=rack-timeout'
|
61
|
+
s << ' id=' << info.id if info.id
|
62
|
+
s << ' age=' << ms(info.age) if info.age
|
63
|
+
s << ' timeout=' << ms(info.timeout) if info.timeout
|
64
|
+
s << ' duration=' << ms(info.duration) if info.duration
|
65
|
+
s << ' state=' << info.state.to_s if info.state
|
66
|
+
s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-timeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.0beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Caio Chassot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Rack middleware which aborts requests that have been running for longer
|
14
14
|
than a specified timeout.
|
@@ -19,6 +19,7 @@ extra_rdoc_files: []
|
|
19
19
|
files:
|
20
20
|
- MIT-LICENSE
|
21
21
|
- README.markdown
|
22
|
+
- lib/rack/timeout/logger.rb
|
22
23
|
- lib/rack/timeout.rb
|
23
24
|
- lib/rack-timeout.rb
|
24
25
|
homepage: http://github.com/kch/rack-timeout
|