Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add formatter for the logstash-logger gem #374

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Nowaker
Copy link

@Nowaker Nowaker commented Sep 14, 2023

This is #303 rebased against master.

Not that my feedback from #303 (comment) 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

Allows for the added payload fields in lograge to surface in logs
outside of just controller logs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants