class HTTP::Features::Logging


HTTP.use(logging: {logger: Logger.new(STDOUT), binary_formatter: :base64})
@example Custom binary formatter
- Proc — calls the proc with the raw binary string
- :base64 — logs BINARY DATA (N bytes)\n<base64>
- :stats (default) — logs BINARY DATA (N bytes)
of being dumped raw. Available formatters:
responses) are formatted using the binary_formatter option instead
Binary bodies (IO/Enumerable request sources and binary-encoded
HTTP.use(logging: {logger: Logger.new(STDOUT)}).get(“example.com/”)
‘debug`. Be sure to specify the logger when enabling the feature:
logged at `info`, and the headers and bodies of both are logged at
Log requests and responses. Request verb and uri, and Response status are

def format_binary(data)

Other tags:
    Api: - private

Returns:
  • (String) -
def format_binary(data)
  case @binary_formatter
  when :stats
    format("BINARY DATA (%d bytes)", data.bytesize)
  when :base64
    format("BINARY DATA (%d bytes)\n%s", data.bytesize, [data].pack("m0"))
  else
    @binary_formatter.call(data) # steep:ignore
  end
end

def initialize(logger: NullLogger.new, binary_formatter: :stats)

Other tags:
    Api: - public

Returns:
  • (Logging) -

Parameters:
  • binary_formatter (:stats, :base64, #call) -- how to log binary bodies
  • logger (#info, #debug) -- logger instance

Other tags:
    Example: With binary formatter -
def initialize(logger: NullLogger.new, binary_formatter: :stats)
  super()
  @logger = logger
  @binary_formatter = validate_binary_formatter!(binary_formatter)
end

def log_request_details(request)

Other tags:
    Api: - private

Returns:
  • (void) -
def log_request_details(request)
  headers = stringify_headers(request.headers)
  if request.body.loggable?
    source = request.body.source
    body = source.encoding.eql?(Encoding::BINARY) ? format_binary(source) : source
    logger.debug { "#{headers}\n\n#{body}" }
  else
    logger.debug { headers }
  end
end

def log_response_body_inline(response)

Other tags:
    Api: - private

Returns:
  • (HTTP::Response) -
def log_response_body_inline(response)
  body    = response.body
  headers = stringify_headers(response.headers)
  if body.respond_to?(:encoding) && body.encoding.eql?(Encoding::BINARY)
    logger.debug { "#{headers}\n\n#{format_binary(body)}" } # steep:ignore
  else
    logger.debug { "#{headers}\n\n#{body}" }
  end
  response
end

def logged_body(body)

Other tags:
    Api: - private

Returns:
  • (HTTP::Response::Body) -
def logged_body(body)
  formatter = (method(:format_binary) unless body.loggable?)
  stream = BodyLogger.new(body.instance_variable_get(:@stream), logger, formatter: formatter) # steep:ignore
  Response::Body.new(stream, encoding: body.encoding)
end

def logged_response_options(response)

Other tags:
    Api: - private

Returns:
  • (Hash) -
def logged_response_options(response)
  {
    status:        response.status,
    version:       response.version,
    headers:       response.headers,
    proxy_headers: response.proxy_headers,
    connection:    response.connection,
    body:          logged_body(response.body),
    request:       response.request
  }
end

def stringify_headers(headers)

Other tags:
    Api: - private

Returns:
  • (String) -
def stringify_headers(headers)
  headers.map { |name, value| "#{name}: #{value}" }.join("\n")
end

def validate_binary_formatter!(formatter)

Other tags:
    Api: - private

Raises:
  • (ArgumentError) - if the formatter is not a valid option

Returns:
  • (:stats, :base64, #call) -
def validate_binary_formatter!(formatter)
  return formatter if formatter.eql?(:stats) || formatter.eql?(:base64) || formatter.respond_to?(:call)
  raise ArgumentError,
        "binary_formatter must be :stats, :base64, or a callable " \
        "(got #{formatter.inspect})"
end

def wrap_request(request)

Other tags:
    Api: - public

Returns:
  • (HTTP::Request) -

Parameters:
  • request (HTTP::Request) --
def wrap_request(request)
  logger.info { format("> %s %s", String(request.verb).upcase, request.uri) }
  log_request_details(request)
  request
end

def wrap_response(response)

Other tags:
    Api: - public

Returns:
  • (HTTP::Response) -

Parameters:
  • response (HTTP::Response) --
def wrap_response(response)
  logger.info { "< #{response.status}" }
  return log_response_body_inline(response) unless response.body.is_a?(Response::Body)
  logger.debug { stringify_headers(response.headers) }
  return response unless logger.debug?
  Response.new(**logged_response_options(response)) # steep:ignore
end