Class: RightSupport::Rack::RequestLogger
- Defined in:
- lib/right_support/rack/request_logger.rb
Overview
A Rack middleware that logs information about every HTTP request received and every exception raised while processing a request.
The middleware can be configured to use its own logger, but defaults to using env for logging if it is present. If ‘rack.logger’ is not set, this middleware will set it before calling the next middleware. Therefore, RequestLogger can be used standalone to fulfill all logging needs, or combined with Rack::Logger or another middleware that provides logging services.
Constant Summary collapse
- BACKTRACE_LIMIT =
limit exception backtrace length; usually only the first few lines are significant. the same error dumped repeatedly can make grepping difficult.
10- DEBUG_MODE_ENV_KEY =
for debug-mode only logging. debug-mode can also be enabled by the “X-Debug=<anything>” header.
'rack.debug-mode'.freeze
- DEBUG_MODE =
::ENV['DEBUG_MODE'] == 'true'
- DEBUG_MODE_HTTP_HEADER =
'HTTP_X_DEBUG'- RACK_LOGGER =
defaults to STDERR by Rack but usually overridden to use syslog, etc.
'rack.logger'.freeze
- RACK_LOGGING_ENABLED =
indicates whether current request is enabled for logging by filter, etc.
'rack.logging.enabled'.freeze
- NORMALIZE_40X =
list of status codes that are ‘normalized’ when :normalize_40x=true normalization means stripping any developer-provided details to avoid leaking information about the service. overridden by DEBUG_MODE=true but specifically not affected by the ‘X-Debug’ header.
[401, 403, 404]
Class Method Summary collapse
-
.canonicalize_header_key(key) ⇒ Object
provides a canonical representation of a header key that is acceptable to Rack, etc.
-
.format_exception_message(e) ⇒ String
Formats a concise error message with limited backtrace, etc.
-
.header_value_get(headers, key) ⇒ String
Header value by canonical key name, if present.
-
.header_value_set(headers, key, value) ⇒ Hash
safely sets a header value by first locating any existing key by canonical search.
-
.request_uuid_from_env(env) ⇒ String
Accepted/generated request UUID or else ‘missing’ to indicate that the RequestTracker middleware is missing.
Instance Method Summary collapse
-
#call(env) ⇒ Object
Add a logger to the Rack environment and call the next middleware.
-
#initialize(app, options = {}) ⇒ RequestLogger
constructor
Initialize an instance of the middleware, optionally providing a whitelist (:only) or a blacklist (:except) of path regexps to which request logging will apply.
Constructor Details
#initialize(app, options = {}) ⇒ RequestLogger
Initialize an instance of the middleware, optionally providing a whitelist
(:only) or a blacklist (:except) of path regexps to which request logging
will apply.
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/right_support/rack/request_logger.rb', line 82 def initialize(app, = {}) @app = app @only = [:only] || [] @except = [:except] || [] # by default we don't log the query string contents because it may have sensitive data. @include_query_string = [:include_query_string] || false @logger = [:logger] @normalize_40x = !![:normalize_40x] @normalize_40x_set_cookie = [:normalize_40x_set_cookie] @normalize_40x_set_cookie = @normalize_40x if @normalize_40x_set_cookie.nil? end |
Class Method Details
.canonicalize_header_key(key) ⇒ Object
provides a canonical representation of a header key that is acceptable to Rack, etc.
186 187 188 |
# File 'lib/right_support/rack/request_logger.rb', line 186 def self.canonicalize_header_key(key) key.downcase.gsub('_', '-') end |
.format_exception_message(e) ⇒ String
Formats a concise error message with limited backtrace, etc.
229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/right_support/rack/request_logger.rb', line 229 def self.(e) trace = e.backtrace || [] if trace.size > BACKTRACE_LIMIT trace = trace[0, BACKTRACE_LIMIT] << '...' end kind = e.class.name if (msg = e.) && !msg.empty? && msg != kind kind = "#{kind}: #{msg}" end [kind, *trace].join("\n") end |
.header_value_get(headers, key) ⇒ String
Returns header value by canonical key name, if present.
191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/right_support/rack/request_logger.rb', line 191 def self.header_value_get(headers, key) return nil unless headers key = canonicalize_header_key(key) value = nil headers.each do |k, v| if canonicalize_header_key(k) == key value = v break end end value end |
.header_value_set(headers, key, value) ⇒ Hash
safely sets a header value by first locating any existing key by canonical search.
211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/right_support/rack/request_logger.rb', line 211 def self.header_value_set(headers, key, value) headers ||= {} key = canonicalize_header_key(key) headers.each do |k, v| if canonicalize_header_key(k) == key key = k break end end headers[key] = value headers end |
.request_uuid_from_env(env) ⇒ String
Returns accepted/generated request UUID or else ‘missing’ to indicate that the RequestTracker middleware is missing.
243 244 245 |
# File 'lib/right_support/rack/request_logger.rb', line 243 def self.request_uuid_from_env(env) env[RequestTracker::REQUEST_UUID_ENV_NAME] || 'missing' end |
Instance Method Details
#call(env) ⇒ Object
Add a logger to the Rack environment and call the next middleware.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/right_support/rack/request_logger.rb', line 109 def call(env) # forward-declare in case of exception. enabled = true # override logger, if necessary. if @logger logger = env[RACK_LOGGER] = @logger else logger = env[RACK_LOGGER] end # determine if debug-mode is enabled. env[DEBUG_MODE_ENV_KEY] = debug_enabled?(env) # log request only when enabled. env[RACK_LOGGING_ENABLED] = enabled = logging_enabled?(logger, env) began_at = Time.now log_request_begin(logger, env) if enabled # next status, headers, body = @app.call(env) # log exception, if necessary. if env['sinatra.error'] && !env['sinatra.error.handled'] if !enabled # after the fact but better than logging exception without its request. log_request_begin(logger, env) enabled = true end log_exception(logger, env['sinatra.error']) elsif !enabled && env[RACK_LOGGING_ENABLED] # controller can conditionally enable logging for the current request # even though by default such requests are not logged. an example is a # periodic request for a listing (scheduled tasks, etc.) that is only # logged when not empty. log_request_begin(logger, env) enabled = true end # respond identically to some 40Xs; do not reveal any internals by # varying the response headers or body (as developers tend to do for # debugging reasons). public APIs will receive such requests frequently # but this is generally not needed for internal APIs. # # note that it is important to *not* accept the "X-Debug" header override # and show more information in response ;) if @normalize_40x && !DEBUG_MODE && NORMALIZE_40X.include?(status) # note that ::Rack::CONTENT_LENGTH was not defined before rack v1.6+ :@ headers = { 'Content-Length' => '0' } body = [] env[DEBUG_MODE_ENV_KEY] = false # disable any verbose debugging end # defeat global session renew, update and set-cookie for normalized 40x. # has no effect if the app is not using global session middleware. if @normalize_40x_set_cookie && NORMALIZE_40X.include?(status) env['global_session.req.renew'] = false env['global_session.req.update'] = false end # log response only when enabled. if enabled log_request_end(logger, env, status, headers, body, began_at) end return [status, headers, body] rescue Exception => e if !enabled # after the fact but better than logging exception without its request. log_request_begin(logger, env) end log_exception(logger, e) raise end |