roidrage / lograge

An attempt to tame Rails' default policy to log everything.
http://www.paperplanes.de/2012/3/14/on-notifications-logsubscribers-and-bringing-sanity-to-rails-logging.html
MIT License
3.47k stars 300 forks source link

Add formatter for the logstash-logger gem #374

Open Nowaker opened 1 year ago

Nowaker commented 1 year ago

This is https://github.com/roidrage/lograge/pull/303 rebased against master.

Not that my feedback from https://github.com/roidrage/lograge/pull/303#issuecomment-1284884046 still applies - it's not an ideal implementation... but it works. It's been in production of a huge site for one year now.

For reference, RequestStore.store[:lograge_event_payload] is what this original PR adds to Lograge core, and then formatters or other custom loggers. Like my logger that is set as Rails.logger and therefore each non-Lograge line includes what Lograge logs for that request.

# config/application.rb
    config.log_level = :debug
    require_relative '../lib/exceptions'
    config.logger = ::Exceptions::Logger.new(STDOUT)
# lib/exceptions.rb
class Exceptions
  def self.to_s_truncated e, source
    unless e.is_a?(Exception)
      return '(not an exception)'
    end

    truncated_backtrace = []
    truncated_backtrace << "#{e.class.name}: #{e}"

    full_stack_size = e.backtrace.size
    truncated_backtrace += e.backtrace.first(5)
    if full_stack_size > 5
      truncated_backtrace << "(truncated; total frames: #{full_stack_size})"
    end

    truncated_backtrace.join("\n")
  end

  def self.to_s_one_line e, source
    unless e.is_a?(Exception)
      return '(not an exception)'
    end

    s = "#{e.class.name}: #{e}"

    if first_frame = e.backtrace.first
      s << " - #{first_frame}"
    end

    if source
      s << " via #{source}"
    end

    s
  end

  def self.to_lograge e, hash, event
    hash[:error] = Exceptions.to_s_one_line(e, 'lograge')
    hash[:appsignal] = "#{e.class.name} #{event.payload[:controller]}##{event.payload[:action]}"
  end

  class Logger < ::Logger
    %w(unknown fatal error warn info debug).each do |level|
      level_const = Logger.const_get level.upcase

      define_method level do |msg, controller = nil, &block|
        if msg.is_a? Exception
          msg = Exceptions.to_s_one_line(msg, 'logger')
        end

        if msg && controller
          event = RequestStore.store[:lograge_event_payload] || {}
          event.define_singleton_method :payload do
            event
          end

          if controller
            event.merge! Rails.configuration.lograge.custom_payload_method.call(controller)
            event.merge! Rails.configuration.lograge.custom_options.call(event, controller)
          end

          unless controller.is_a?(Rack::Attack::Request)
            event.merge! type: 'logger'
          end

          lograge_line = Rails.configuration.lograge.formatter.call(event)

          msg = "#{lograge_line} --- #{msg}"
        end

        add(level_const, msg, progname, &block)
      end
    end
  end
end