class Gruf::Interceptors::Instrumentation::RequestLogging::Interceptor
Handles Rails-style request logging for gruf services.
This is added by default to gruf servers; if you have `Gruf.use_default_hooks = false`, you can add it back manually by doing:
Gruf::Instrumentation::Registry.add(:request_logging, Gruf::Instrumentation::RequestLogging::Hook)
Constants
- LOG_LEVEL_MAP
Default mappings of codes to log levels…
Public Instance Methods
Log the request, sending it to the appropriate formatter
@return [String]
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 64 def call(&block) return yield if options.fetch(:ignore_methods, [])&.include?(request.method_name) result = Gruf::Interceptors::Timer.time(&block) # Fetch log level options and merge with default... log_level_map = LOG_LEVEL_MAP.merge(options.fetch(:log_levels, {})) # A result is either successful, or, some level of feedback handled in the else block... if result.successful? type = log_level_map['GRPC::Ok'] || :debug status_name = 'GRPC::Ok' else type = log_level_map[result.message_class_name] || :error status_name = result.message_class_name end payload = {} if !request.client_streamer? && !request.bidi_streamer? payload[:params] = sanitize(request.message.to_h) if options.fetch(:log_parameters, false) payload[:message] = message(request, result) payload[:status] = status(result.message, result.successful?) else payload[:params] = {} payload[:message] = '' payload[:status] = GRPC::Core::StatusCodes::OK end payload[:service] = request.service_key payload[:method] = request.method_key payload[:action] = request.method_key payload[:grpc_status] = status_name payload[:duration] = result.elapsed_rounded payload[:thread_id] = Thread.current.object_id payload[:time] = Time.now.to_s payload[:host] = Socket.gethostname logger.send(type, formatter.format(payload, request: request, result: result)) raise result.message unless result.successful? result.message end
Private Instance Methods
Determine the appropriate formatter for the request logging
@return [Gruf::Instrumentation::RequestLogging::Formatters::Base]
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 146 def formatter unless @formatter fmt = options.fetch(:formatter, :plain) @formatter = case fmt when Symbol prefix = 'Gruf::Interceptors::Instrumentation::RequestLogging::Formatters::' klass = "#{prefix}#{fmt.to_s.capitalize}" fmt = klass.constantize.new when Class fmt = fmt.new else fmt end raise InvalidFormatterError unless fmt.is_a?(Formatters::Base) end @formatter end
Helper method to recursively redact the value of all hash keys
@param [Hash] hash Part of the hash of parameters to sanitize @param [String] redacted_string The custom redact string
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 210 def hash_deep_redact!(hash, redacted_string) hash.each_key do |key| if hash[key].is_a? Hash hash_deep_redact!(hash[key], redacted_string) else hash[key] = redacted_string end end end
@return [::Gruf::Logger]
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 113 def logger @logger ||= (options.fetch(:logger, nil) || Gruf.logger) end
Return an appropriate log message dependent on the status
@param [Gruf::Controllers::Request] request @param [Gruf::Interceptors::Timer::Result] result @return [String] The appropriate message body
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 124 def message(request, result) return "[GRPC::Ok] (#{request.method_name})" if result.successful? "[#{result.message_class_name}] (#{request.method_name}) #{result.message.message}" end
Helper method to recursively redact based on the blocklist
@param [Array] parts The blocklist. ex. 'data.schema' -> [:data, :schema] @param [Integer] idx The current index of the blocklist @param [Hash] params The hash of parameters to sanitize @param [String] redacted_string The custom redact string
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 188 def redact!(parts = [], idx = 0, params = {}, redacted_string = 'REDACTED') return unless parts.is_a?(Array) && params.is_a?(Hash) return if idx >= parts.size || !params.key?(parts[idx]) if idx == parts.size - 1 if params[parts[idx]].is_a? Hash hash_deep_redact!(params[parts[idx]], redacted_string) else params[parts[idx]] = redacted_string end return end redact!(parts, idx + 1, params[parts[idx]], redacted_string) end
Redact any blocklisted params and return an updated hash
@param [Hash] params The hash of parameters to sanitize @return [Hash] The sanitized params in hash form
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 170 def sanitize(params = {}) blocklists = (options.fetch(:blocklist, []) || []).map(&:to_s) redacted_string = options.fetch(:redacted_string, nil) || 'REDACTED' blocklists.each do |blocklist| parts = blocklist.split('.').map(&:to_sym) redact!(parts, 0, params, redacted_string) end params end
Return the proper status code for the response
@param [Object] response The response object @param [Boolean] successful If the response was successful @return [Boolean] The proper status code
# File lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb, line 137 def status(response, successful) successful ? GRPC::Core::StatusCodes::OK : response.code end