Class: RightSupport::Rack::RequestTracker
- Defined in:
- lib/right_support/rack/request_tracker.rb
Overview
middleware to detect a request ID header or generate a new ID for each incoming request. the purpose is to track a request lineage throughout a chain of internal API calls and, in some cases, out to external APIs that also support the X-Request-ID header as a de-facto standard (google it).
Constant Summary collapse
- Generator =
shorthand
::RightSupport::Data::Token
- REQUEST_ID_HEADER =
used by many public services to represent a client-generated request ID that can be tracked and safely logged throughout the lineage of a request. this is also supported by goa middleware.
'X-Request-Id'.freeze
- HTTP_REQUEST_ID_HEADER =
incoming header as found in Rack env hash, if any.
'HTTP_X_REQUEST_ID'.freeze
- REQUEST_UUID_HEADER =
LEGACY: still supported but new code should use the standard (see above).
"X-Request-Uuid".freeze
- HTTP_REQUEST_UUID_HEADER =
LEGACY: incoming header as found in Rack env hash, if any.
'HTTP_X_REQUEST_UUID'.freeze
- REQUEST_UUID_ENV_NAME =
LEGACY: refers to the generated or passed-in request ID. the key has been hardcoded in some places so is not easy to change and/or there is not much value to finding and replacing with _id in all cases.
'rack.request_uuid'.freeze
- API_CALL_COUNTER_ENV_KEY =
records count of additional API calls made by an application while handling a request, etc. this is useful for generating an incremental request [UU]ID to be forwarded to other services for additional API calls.
'request_tracker.api_call_counter'
- REQUEST_LINEAGE_UUID_HEADER =
Deprecated.
do not send the lineage header as support may go away.
'HTTP_X_REQUEST_LINEAGE_UUID'.freeze
- UUID_SEPARATOR =
Deprecated.
do not send the lineage header as support may go away.
' '.freeze
- MAX_REQUEST_UUID_LENGTH =
limit request [UU]ID to something reasonable to keep our logs from overflowing on bad user-provided IDs. we do not want to be too restrictive in case people are encoding some useful information, etc. the issue is that the request ID will tend to show up a lot in logs.
128
Class Method Summary collapse
-
.copy_request_uuid(from_env, to_headers, options = {}) ⇒ Hash|Array
copies the request [UU]ID from the request environment to the given hash, if present.
-
.detect_request_uuid(env) ⇒ Array
detects whether the incoming env hash contains a request ID is some form and generates a new ID when missing.
-
.generate_request_uuid ⇒ String
generates a token (nicer than an actual UUID for logging) but we continue to refer to it as the “request UUID” for legacy reasons.
-
.next_request_uuid(env, base_request_uuid = nil) ⇒ String
Next request [UU]ID.
Instance Method Summary collapse
-
#call(env) ⇒ Array
request tracking.
-
#initialize(app) ⇒ RequestTracker
constructor
A new instance of RequestTracker.
Constructor Details
#initialize(app) ⇒ RequestTracker
Returns a new instance of RequestTracker.
72 73 74 |
# File 'lib/right_support/rack/request_tracker.rb', line 72 def initialize(app) @app = app end |
Class Method Details
.copy_request_uuid(from_env, to_headers, options = {}) ⇒ Hash|Array
copies the request [UU]ID from the request environment to the given hash, if present.
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 |
# File 'lib/right_support/rack/request_tracker.rb', line 146 def self.copy_request_uuid(from_env, to_headers, = {}) = { generator: nil, modifier: nil, return_request_uuid: false }.merge() from_env ||= {} to_headers ||= {} # keep existing ID value, if any. request_uuid = ::RightSupport::Data::HashTools.header_value_get( to_headers, REQUEST_ID_HEADER) unless request_uuid # attempt to find ID key in environment hash. if request_uuid = from_env[REQUEST_UUID_ENV_NAME] # use modifier, if any. if modifier = [:modifier] request_uuid = modifier.call(request_uuid) end else # use generator, if any. if generator = [:generator] request_uuid = generator.call end end # note that we will always forward the _ID header as the standard. # none of the RS code ever actually accepted the _UUID header so that # is a non-issue. to_headers[REQUEST_ID_HEADER] = request_uuid if request_uuid end return [to_headers, request_uuid] if [:return_request_uuid] to_headers end |
.detect_request_uuid(env) ⇒ Array
detects whether the incoming env hash contains a request ID is some form and generates a new ID when missing.
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 |
# File 'lib/right_support/rack/request_tracker.rb', line 96 def self.detect_request_uuid(env) request_uuid = '' response_header_name = nil { HTTP_REQUEST_ID_HEADER => REQUEST_ID_HEADER, HTTP_REQUEST_UUID_HEADER => REQUEST_UUID_HEADER, REQUEST_LINEAGE_UUID_HEADER => REQUEST_UUID_HEADER }.each do |in_key, out_key| if env.has_key?(in_key) request_uuid = env[in_key].to_s.strip response_header_name = out_key break end end # for legacy reasons we default to the -UUID header in response for newly- # generated IDs. we will use the -ID standard if that was passed-in. once # all apps are updated you will mostly see -ID in response except for API # calls initiated by browser code. the javascript can gradually be changed # to send -ID with requests as well. if request_uuid.empty? request_uuid = generate_request_uuid response_header_name = REQUEST_UUID_HEADER else # truncate, if necessary. request_uuid = request_uuid[0, MAX_REQUEST_UUID_LENGTH] end return request_uuid, response_header_name end |
.generate_request_uuid ⇒ String
generates a token (nicer than an actual UUID for logging) but we continue to refer to it as the “request UUID” for legacy reasons.
186 187 188 |
# File 'lib/right_support/rack/request_tracker.rb', line 186 def self.generate_request_uuid Generator.generate end |
.next_request_uuid(env, base_request_uuid = nil) ⇒ String
Returns next request [UU]ID.
191 192 193 194 195 196 |
# File 'lib/right_support/rack/request_tracker.rb', line 191 def self.next_request_uuid(env, base_request_uuid = nil) base_request_uuid ||= env[REQUEST_UUID_ENV_NAME] || 'missing' env[API_CALL_COUNTER_ENV_KEY] ||= 0 n = env[API_CALL_COUNTER_ENV_KEY] += 1 "#{base_request_uuid}_#{n}" end |
Instance Method Details
#call(env) ⇒ Array
request tracking.
81 82 83 84 85 86 87 88 89 |
# File 'lib/right_support/rack/request_tracker.rb', line 81 def call(env) request_uuid, response_header_name = self.class.detect_request_uuid(env) env[REQUEST_UUID_ENV_NAME] = request_uuid status, headers, body = @app.call(env) headers[response_header_name] = request_uuid [status, headers, body] end |