Class: A2A::Server::Middleware::LoggingMiddleware
- Inherits:
-
Object
- Object
- A2A::Server::Middleware::LoggingMiddleware
- Includes:
- Utils::RailsDetection
- Defined in:
- lib/a2a/server/middleware/logging_middleware.rb
Constant Summary collapse
- FORMATS =
Logging formats
%i[simple detailed structured json].freeze
Instance Attribute Summary collapse
-
#format ⇒ Object
readonly
Returns the value of attribute format.
-
#level ⇒ Object
readonly
Returns the value of attribute level.
-
#logger ⇒ Object
Returns the value of attribute logger.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
-
#add_filtered_param(param) ⇒ Object
Add filtered parameter.
-
#call(request, context) { ... } ⇒ Object
Process logging for a request.
-
#default_logger ⇒ Object
private
Get default logger.
-
#filter_sensitive_data(data) ⇒ Object
private
Filter sensitive data from parameters/responses.
-
#format_structured_log(data) ⇒ String
private
Format structured log data.
-
#generate_request_id(request, _context) ⇒ String
private
Generate a unique request ID.
-
#initialize(logger: nil, level: :info, format: :detailed, **options) ⇒ LoggingMiddleware
constructor
Initialize logging middleware.
-
#log_detailed_error(request, _context, error, duration, request_id) ⇒ Object
private
Log detailed request error.
-
#log_detailed_start(request, context, request_id) ⇒ Object
private
Log detailed request start.
-
#log_detailed_success(request, _context, result, duration, request_id) ⇒ Object
private
Log detailed request success.
- #log_json_error(request, context, error, duration, request_id) ⇒ Object private
-
#log_json_start(request, context, request_id) ⇒ Object
private
Log JSON format (same as structured but always JSON).
- #log_json_success(request, context, result, duration, request_id) ⇒ Object private
-
#log_request_error(request, context, error, duration, request_id) ⇒ Object
private
Log request error.
-
#log_request_start(request, context, request_id) ⇒ Object
private
Log request start.
-
#log_request_success(request, context, result, duration, request_id) ⇒ Object
private
Log successful request completion.
-
#log_structured_error(request, _context, error, duration, request_id) ⇒ Object
private
Log structured request error.
-
#log_structured_start(request, context, request_id) ⇒ Object
private
Log structured request start.
-
#log_structured_success(request, _context, result, duration, request_id) ⇒ Object
private
Log structured request success.
-
#remove_filtered_param(param) ⇒ Object
Remove filtered parameter.
-
#validate_format! ⇒ Object
private
Validate the logging format.
Methods included from Utils::RailsDetection
#rails_application, #rails_available?, #rails_development?, #rails_environment, #rails_logger, #rails_production?, #rails_version, #rails_version_requires_validation?, #rails_version_supported?
Constructor Details
#initialize(logger: nil, level: :info, format: :detailed, **options) ⇒ LoggingMiddleware
Initialize logging middleware
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 44 def initialize(logger: nil, level: :info, format: :detailed, **) @logger = logger || default_logger @level = level @format = format @options = { log_params: true, log_response: false, log_errors: true, filtered_params: %w[password token api_key secret], include_context: true, include_timing: true }.merge() validate_format! end |
Instance Attribute Details
#format ⇒ Object (readonly)
Returns the value of attribute format.
28 29 30 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 28 def format @format end |
#level ⇒ Object (readonly)
Returns the value of attribute level.
28 29 30 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 28 def level @level end |
#logger ⇒ Object
Returns the value of attribute logger.
27 28 29 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 27 def logger @logger end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
28 29 30 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 28 def @options end |
Instance Method Details
#add_filtered_param(param) ⇒ Object
Add filtered parameter
102 103 104 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 102 def add_filtered_param(param) @options[:filtered_params] << param.to_s end |
#call(request, context) { ... } ⇒ Object
Process logging for a request
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 67 def call(request, context) start_time = Time.now request_id = generate_request_id(request, context) # Log request start log_request_start(request, context, request_id) begin # Execute next middleware/handler result = yield # Log successful completion duration = Time.now - start_time log_request_success(request, context, result, duration, request_id) result rescue StandardError => e # Log error duration = Time.now - start_time log_request_error(request, context, e, duration, request_id) # Re-raise the error raise end end |
#default_logger ⇒ Object (private)
Get default logger
126 127 128 129 130 131 132 133 134 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 126 def default_logger if rails_logger rails_logger else Logger.new($stdout).tap do |logger| logger.level = Logger::INFO end end end |
#filter_sensitive_data(data) ⇒ Object (private)
Filter sensitive data from parameters/responses
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 341 def filter_sensitive_data(data) case data when Hash filtered = {} data.each do |key, value| filtered[key] = if @options[:filtered_params].include?(key.to_s.downcase) "[FILTERED]" else filter_sensitive_data(value) end end filtered when Array data.map { |item| filter_sensitive_data(item) } else data end end |
#format_structured_log(data) ⇒ String (private)
Format structured log data
327 328 329 330 331 332 333 334 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 327 def format_structured_log(data) if @format == :json JSON.generate(data) else # Key=value format for structured logs data.map { |k, v| "#{k}=#{v.inspect}" }.join(" ") end end |
#generate_request_id(request, _context) ⇒ String (private)
Generate a unique request ID
142 143 144 145 146 147 148 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 142 def generate_request_id(request, _context) # Use existing request ID if available return request.id.to_s if request.id # Generate a new ID "req_#{Time.now.to_f}_#{rand(10_000)}" end |
#log_detailed_error(request, _context, error, duration, request_id) ⇒ Object (private)
Log detailed request error
241 242 243 244 245 246 247 248 249 250 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 241 def log_detailed_error(request, _context, error, duration, request_id) = "A2A Request Failed - Method: #{request.method}, ID: #{request_id}, Duration: #{duration.round(3)}s" += ", Error: #{error.class.name}: #{error.}" if @options[:log_errors] && error.respond_to?(:backtrace) && error.backtrace += ", Backtrace: #{error.backtrace.first(3).join(' | ')}" end @logger.error() end |
#log_detailed_start(request, context, request_id) ⇒ Object (private)
Log detailed request start
213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 213 def log_detailed_start(request, context, request_id) = "A2A Request Started - Method: #{request.method}, ID: #{request_id}" if @options[:log_params] && !request.params.empty? filtered_params = filter_sensitive_data(request.params) += ", Params: #{filtered_params.inspect}" end += ", Authenticated: true" if @options[:include_context] && context.authenticated? @logger.send(@level, ) end |
#log_detailed_success(request, _context, result, duration, request_id) ⇒ Object (private)
Log detailed request success
228 229 230 231 232 233 234 235 236 237 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 228 def log_detailed_success(request, _context, result, duration, request_id) = "A2A Request Completed - Method: #{request.method}, ID: #{request_id}, Duration: #{duration.round(3)}s" if @options[:log_response] && result filtered_result = filter_sensitive_data(result) += ", Response: #{filtered_result.inspect}" end @logger.send(@level, ) end |
#log_json_error(request, context, error, duration, request_id) ⇒ Object (private)
318 319 320 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 318 def log_json_error(request, context, error, duration, request_id) log_structured_error(request, context, error, duration, request_id) end |
#log_json_start(request, context, request_id) ⇒ Object (private)
Log JSON format (same as structured but always JSON)
310 311 312 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 310 def log_json_start(request, context, request_id) log_structured_start(request, context, request_id) end |
#log_json_success(request, context, result, duration, request_id) ⇒ Object (private)
314 315 316 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 314 def log_json_success(request, context, result, duration, request_id) log_structured_success(request, context, result, duration, request_id) end |
#log_request_error(request, context, error, duration, request_id) ⇒ Object (private)
Log request error
198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 198 def log_request_error(request, context, error, duration, request_id) case @format when :simple @logger.error("A2A Error: #{request.method} - #{error.class.name}: #{error.}") when :detailed log_detailed_error(request, context, error, duration, request_id) when :structured log_structured_error(request, context, error, duration, request_id) when :json log_json_error(request, context, error, duration, request_id) end end |
#log_request_start(request, context, request_id) ⇒ Object (private)
Log request start
156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 156 def log_request_start(request, context, request_id) case @format when :simple @logger.send(@level, "A2A Request: #{request.method}") when :detailed log_detailed_start(request, context, request_id) when :structured log_structured_start(request, context, request_id) when :json log_json_start(request, context, request_id) end end |
#log_request_success(request, context, result, duration, request_id) ⇒ Object (private)
Log successful request completion
177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 177 def log_request_success(request, context, result, duration, request_id) case @format when :simple @logger.send(@level, "A2A Response: #{request.method} (#{duration.round(3)}s)") when :detailed log_detailed_success(request, context, result, duration, request_id) when :structured log_structured_success(request, context, result, duration, request_id) when :json log_json_success(request, context, result, duration, request_id) end end |
#log_structured_error(request, _context, error, duration, request_id) ⇒ Object (private)
Log structured request error
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 290 def log_structured_error(request, _context, error, duration, request_id) data = { event: "a2a_request_error", method: request.method, request_id: request_id, duration: duration.round(3), error_class: error.class.name, error_message: error., timestamp: Time.now.iso8601 } data[:backtrace] = error.backtrace.first(5) if @options[:log_errors] && error.respond_to?(:backtrace) && error.backtrace data[:error_code] = error.code if error.respond_to?(:code) @logger.error(format_structured_log(data)) end |
#log_structured_start(request, context, request_id) ⇒ Object (private)
Log structured request start
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 254 def log_structured_start(request, context, request_id) data = { event: "a2a_request_start", method: request.method, request_id: request_id, timestamp: Time.now.iso8601 } data[:params] = filter_sensitive_data(request.params) if @options[:log_params] && !request.params.empty? if @options[:include_context] data[:authenticated] = context.authenticated? data[:user] = context.user if context.user end @logger.send(@level, format_structured_log(data)) end |
#log_structured_success(request, _context, result, duration, request_id) ⇒ Object (private)
Log structured request success
274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 274 def log_structured_success(request, _context, result, duration, request_id) data = { event: "a2a_request_success", method: request.method, request_id: request_id, duration: duration.round(3), timestamp: Time.now.iso8601 } data[:response] = filter_sensitive_data(result) if @options[:log_response] && result @logger.send(@level, format_structured_log(data)) end |
#remove_filtered_param(param) ⇒ Object
Remove filtered parameter
110 111 112 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 110 def remove_filtered_param(param) @options[:filtered_params].delete(param.to_s) end |
#validate_format! ⇒ Object (private)
Validate the logging format
118 119 120 121 122 |
# File 'lib/a2a/server/middleware/logging_middleware.rb', line 118 def validate_format! return if FORMATS.include?(@format) raise ArgumentError, "Invalid format: #{@format}. Must be one of: #{FORMATS.join(', ')}" end |