Class: SecApi::Middleware::Instrumentation
- Inherits:
-
Faraday::Middleware
- Object
- Faraday::Middleware
- SecApi::Middleware::Instrumentation
- Includes:
- CallbackHelper
- Defined in:
- lib/sec_api/middleware/instrumentation.rb
Overview
Authorization headers are automatically sanitized from on_request callbacks to prevent API key leakage in logs.
External request_id: If you pre-set env via upstream middleware, this middleware will preserve it (uses ||= operator). This enables distributed tracing integration with Datadog, New Relic, OpenTelemetry, and Rails request IDs.
Faraday middleware that provides instrumentation callbacks for request/response lifecycle.
This middleware captures request timing and invokes configurable callbacks for:
-
on_request: Before the request is sent (for logging, tracing)
-
on_response: After the response is received (for metrics, latency tracking)
-
on_error: When request ultimately fails after all retries exhausted (for error tracking)
Position in middleware stack: FIRST (before Retry, RateLimiter, ErrorHandler) This ensures all requests are instrumented, including retried requests. Being first also allows capturing exceptions after all retries are exhausted.
Instance Method Summary collapse
-
#call(env) ⇒ Faraday::Response
Processes the request and invokes instrumentation callbacks.
-
#initialize(app, options = {}) ⇒ Instrumentation
constructor
Initializes the instrumentation middleware.
Methods included from CallbackHelper
Constructor Details
#initialize(app, options = {}) ⇒ Instrumentation
Initializes the instrumentation middleware.
72 73 74 75 |
# File 'lib/sec_api/middleware/instrumentation.rb', line 72 def initialize(app, = {}) super(app) @config = [:config] end |
Instance Method Details
#call(env) ⇒ Faraday::Response
Processes the request and invokes instrumentation callbacks.
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 |
# File 'lib/sec_api/middleware/instrumentation.rb', line 81 def call(env) # Generate request_id if not already set (allows upstream middleware to set it) env[:request_id] ||= SecureRandom.uuid # Capture start time using monotonic clock for accurate duration. # Why monotonic? Time.now can jump backward (NTP sync, DST) causing negative durations. # CLOCK_MONOTONIC is guaranteed to increase, essential for accurate latency metrics. start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) # Invoke on_request callback BEFORE request is sent invoke_on_request(env) # Execute the request through downstream middleware @app.call(env).on_complete do |response_env| # Calculate duration in milliseconds end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) duration_ms = ((end_time - start_time) * 1000).round # Store duration in env for potential use by other middleware response_env[:duration_ms] = duration_ms # Invoke on_response callback AFTER response is received invoke_on_response(response_env, duration_ms) end rescue SecApi::Error => e # Invoke on_error callback for errors that escape after all retries exhausted. # This catches both TransientError (NetworkError, ServerError, RateLimitError) # and PermanentError (AuthenticationError, NotFoundError, ValidationError). # PermanentError on_error is also invoked by ErrorHandler for immediate failures, # but we invoke here too for consistency (both paths call on_error exactly once). # Note: ErrorHandler only invokes on_error for PermanentError, not TransientError. invoke_on_error(env, e) raise end |