class Pliny::Middleware::CanonicalLogLine

Emits a “canonical log line”, i.e. a single log line that contains as much relevant information about a request as possible and which makes for a single convenient reference point to understand all the vitals of any single request.

This default implementation contains some useful data to get a project started, but it’s usually recommended to vendor this middleware into your project and start adding some custom fields. Some examples of those might be:

* ID and email of an authenticated user.
* ID of API key used, OAuth application and scope.
* Remaining and total rate limits.
* Name of the service, HEAD revision, release number.
* Name of the internal system that initiated the request.

Public Class Methods

new(app, options) click to toggle source
# File lib/pliny/middleware/canonical_log_line.rb, line 61
def initialize(app, options)
  @app = app
  @emitter = options.fetch(:emitter)
end

Public Instance Methods

call(env) click to toggle source
# File lib/pliny/middleware/canonical_log_line.rb, line 66
def call(env)
  begin
    start = Time.now
    status, headers, response = @app.call(env)
  ensure
    begin
      line = LogLine.new

      #
      # error
      #

      if error = env["pliny.error"]
        line.error_class = error.class.name
        line.error_message = error.message
        if error.is_a?(Pliny::Errors::Error)
          line.error_id = error.id.to_s
        end
      end

      #
      # request
      #

      request = Rack::Request.new(env)
      line.request_id = env["REQUEST_ID"]
      line.request_ip = request.ip
      line.request_method = request.request_method
      line.request_path = request.path_info
      line.request_user_agent = request.user_agent
      if route = env["sinatra.route"]
        line.request_route_signature = route.split(" ").last
      end

      #
      # response
      #

      if length = headers["Content-Length"]
        line.response_length = length.to_i
      end
      line.response_status = status
      line.serializer_arity = env["pliny.serializer_arity"]

      #
      # timing
      #

      line.timing_total_elapsed = (Time.now - start).to_f
      line.timing_serializer = env["pliny.serializer_timing"]

      @emitter.call(line.to_h)
    rescue => e
      # We hope that a canonical log line never fails, but in case it
      # does, do not fail the request because it did.
      Pliny.log(message: "Failed to emit canonical log line")
      Pliny::ErrorReporters.notify(e, rack_env: env)
    end
  end

  [status, headers, response]
end