Class: Logtail::Integrations::Rack::HTTPEvents
- Inherits:
-
Middleware
- Object
- Middleware
- Logtail::Integrations::Rack::HTTPEvents
- Defined in:
- lib/logtail-rack/http_events.rb
Overview
A Rack middleware that is reponsible for capturing and logging HTTP server requests and response events. The Events::HTTPRequest and Events::HTTPResponse events respectively.
Constant Summary collapse
- CONTENT_LENGTH_KEY =
'Content-Length'.freeze
Class Method Summary collapse
-
.capture_request_body=(value) ⇒ Object
Allows you to capture the HTTP request body, default is off (false).
-
.capture_request_body? ⇒ Boolean
Accessor method for #capture_request_body=.
-
.capture_response_body=(value) ⇒ Object
Just like #capture_request_body= but for the Events::HTTPResponse event.
-
.capture_response_body? ⇒ Boolean
Accessor method for #capture_response_body=.
-
.collapse_into_single_event=(value) ⇒ Object
Collapse both the HTTP request and response events into a single log line event.
-
.collapse_into_single_event? ⇒ Boolean
Accessor method for #collapse_into_single_event=.
-
.http_header_filters ⇒ Object
Accessor method for #http_header_filters=.
-
.http_header_filters=(value) ⇒ Object
Filter sensitive HTTP headers (such as “Authorization: Bearer secret_token”).
- .normalize_header_name(name) ⇒ Object
-
.silence_request ⇒ Object
Accessor method for #silence_request=.
-
.silence_request=(proc) ⇒ Object
This setting allows you to silence requests based on any conditions you desire.
Instance Method Summary collapse
Methods inherited from Middleware
enabled=, enabled?, #initialize
Constructor Details
This class inherits a constructor from Logtail::Integrations::Rack::Middleware
Class Method Details
.capture_request_body=(value) ⇒ Object
Allows you to capture the HTTP request body, default is off (false).
Capturing HTTP bodies can be extremely helpful when debugging issues, but please proceed with caution:
-
Capturing HTTP bodies can use quite a bit of data (this can be mitigated, see below)
If you opt to capture bodies, you can also truncate the size to reduce the data captured. See Events::HTTPRequest.
31 32 33 |
# File 'lib/logtail-rack/http_events.rb', line 31 def capture_request_body=(value) @capture_request_body = value end |
.capture_request_body? ⇒ Boolean
Accessor method for #capture_request_body=
36 37 38 |
# File 'lib/logtail-rack/http_events.rb', line 36 def capture_request_body? @capture_request_body == true end |
.capture_response_body=(value) ⇒ Object
Just like #capture_request_body= but for the Events::HTTPResponse event. Please see #capture_request_body= for more details. The documentation there also applies here.
43 44 45 |
# File 'lib/logtail-rack/http_events.rb', line 43 def capture_response_body=(value) @capture_response_body = value end |
.capture_response_body? ⇒ Boolean
Accessor method for #capture_response_body=
48 49 50 |
# File 'lib/logtail-rack/http_events.rb', line 48 def capture_response_body? @capture_response_body == true end |
.collapse_into_single_event=(value) ⇒ Object
Collapse both the HTTP request and response events into a single log line event. While we don’t recommend this, it can help to reduce log volume if desired. The reason we don’t recommend this, is because the logging service you use should not be so expensive that you need to strip out useful logs. It should also provide the tools necessary to properly search your logs and reduce noise. Such as viewing logs for a specific request.
To provide an example. This setting turns this:
Started GET "/" for 127.0.0.1 at 2012-03-10 14:28:14 +0100
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
Into this:
Get "/" sent 200 OK in 79ms
The single event is still a Events::HTTPResponse event. Because we capture HTTP context, you still get the HTTP details, but you will not get all of the request details that the Events::HTTPRequest event would provide.
75 76 77 |
# File 'lib/logtail-rack/http_events.rb', line 75 def collapse_into_single_event=(value) @collapse_into_single_event = value end |
.collapse_into_single_event? ⇒ Boolean
Accessor method for #collapse_into_single_event=.
80 81 82 |
# File 'lib/logtail-rack/http_events.rb', line 80 def collapse_into_single_event? @collapse_into_single_event == true end |
.http_header_filters ⇒ Object
Accessor method for #http_header_filters=
117 118 119 |
# File 'lib/logtail-rack/http_events.rb', line 117 def http_header_filters @http_header_filters end |
.http_header_filters=(value) ⇒ Object
Filter sensitive HTTP headers (such as “Authorization: Bearer secret_token”)
Filtered HTTP header values will be sent to Better Stack as “[FILTERED]”
112 113 114 |
# File 'lib/logtail-rack/http_events.rb', line 112 def http_header_filters=(value) @http_header_filters = value.map { |header_name| normalize_header_name(header_name) } end |
.normalize_header_name(name) ⇒ Object
121 122 123 |
# File 'lib/logtail-rack/http_events.rb', line 121 def normalize_header_name(name) name.to_s.downcase.gsub("-", "_") end |
.silence_request ⇒ Object
Accessor method for #silence_request=
102 103 104 |
# File 'lib/logtail-rack/http_events.rb', line 102 def silence_request @silence_request end |
.silence_request=(proc) ⇒ Object
This setting allows you to silence requests based on any conditions you desire. We require a block because it gives you complete control over how you want to silence requests. The first parameter being the traditional Rack env hash, the second being a [Rack Request](www.rubydoc.info/gems/rack/Rack/Request) object.
93 94 95 96 97 98 99 |
# File 'lib/logtail-rack/http_events.rb', line 93 def silence_request=(proc) if proc && !proc.is_a?(Proc) raise ArgumentError.new("The value passed to #silence_request must be a Proc") end @silence_request = proc end |
Instance Method Details
#call(env) ⇒ Object
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 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/logtail-rack/http_events.rb', line 128 def call(env) request = Util::Request.new(env) if silenced?(env, request) if Config.instance.logger.respond_to?(:silence) Config.instance.logger.silence do @app.call(env) end else @app.call(env) end elsif collapse_into_single_event? request_start = Time.now status, headers, body = @app.call(env) request_end = Time.now Config.instance.logger.info do http_context = CurrentContext.fetch(:http) content_length = safe_to_i(headers[CONTENT_LENGTH_KEY]) duration_ms = (request_end - request_start) * 1000.0 http_response = HTTPResponse.new( content_length: content_length, headers: filter_http_headers(headers), http_context: http_context, request_id: request.request_id, status: status, duration_ms: duration_ms, ) { message: http_response., event: { http_response_sent: { body: http_response.body, content_length: http_response.content_length, headers_json: http_response.headers_json, request_id: http_response.request_id, service_name: http_response.service_name, status: http_response.status, duration_ms: http_response.duration_ms, } } } end [status, headers, body] else Config.instance.logger.info do event_body = capture_request_body? ? request.body_content : nil http_request = HTTPRequest.new( body: event_body, content_length: safe_to_i(request.content_length), headers: filter_http_headers(request.headers), host: Util::Encoding.force_utf8_encoding(request.host), method: request.request_method, path: request.path, port: request.port, query_string: Util::Encoding.force_utf8_encoding(request.query_string), request_id: request.request_id, scheme: Util::Encoding.force_utf8_encoding(request.scheme), ) { message: http_request., event: { http_request_received: { body: http_request.body, content_length: http_request.content_length, headers_json: http_request.headers_json, host: http_request.host, method: http_request.method, path: http_request.path, port: http_request.port, query_string: http_request.query_string, request_id: http_request.request_id, scheme: http_request.scheme, service_name: http_request.service_name, } } } end request_start = Time.now status, headers, body = @app.call(env) request_end = Time.now Config.instance.logger.info do event_body = capture_response_body? ? body : nil content_length = safe_to_i(headers[CONTENT_LENGTH_KEY]) duration_ms = (request_end - request_start) * 1000.0 http_response = HTTPResponse.new( body: event_body, content_length: content_length, headers: filter_http_headers(headers), request_id: request.request_id, status: status, duration_ms: duration_ms, ) { message: http_response., event: { http_response_sent: { body: http_response.body, content_length: http_response.content_length, headers_json: http_response.headers_json, request_id: http_response.request_id, service_name: http_response.service_name, status: http_response.status, duration_ms: http_response.duration_ms, } } } end [status, headers, body] end end |