From d2117da89971c0d9b7e2f56ae0498cca38174892 Mon Sep 17 00:00:00 2001 From: Dick Davis Date: Sat, 10 Aug 2024 21:05:00 -0500 Subject: [PATCH] Refactor to allow configurable logger and formatter classes --- lib/event_logger_rails.rb | 20 +++++++++---- lib/event_logger_rails/emitter.rb | 10 +++---- lib/event_logger_rails/engine.rb | 11 ++++--- .../{json_logger.rb => event_logger.rb} | 9 ++---- lib/event_logger_rails/formatters/json.rb | 16 ++++++++++ spec/lib/event_logger_rails/emitter_spec.rb | 9 +++++- .../event_logger_rails/event_logger_spec.rb | 22 ++++++++++++++ .../formatters/json_spec.rb | 21 +++++++++++++ .../event_logger_rails/json_logger_spec.rb | 20 ------------- spec/lib/event_logger_rails_spec.rb | 30 +++++++++++++++++++ 10 files changed, 125 insertions(+), 43 deletions(-) rename lib/event_logger_rails/{json_logger.rb => event_logger.rb} (54%) create mode 100644 lib/event_logger_rails/formatters/json.rb create mode 100644 spec/lib/event_logger_rails/event_logger_spec.rb create mode 100644 spec/lib/event_logger_rails/formatters/json_spec.rb delete mode 100644 spec/lib/event_logger_rails/json_logger_spec.rb diff --git a/lib/event_logger_rails.rb b/lib/event_logger_rails.rb index ee8ccc0..0ca406a 100644 --- a/lib/event_logger_rails.rb +++ b/lib/event_logger_rails.rb @@ -2,14 +2,15 @@ require 'rails' require 'active_support/dependencies' -require 'event_logger_rails/engine' require 'event_logger_rails/current_request' -require 'event_logger_rails/event' +require 'event_logger_rails/engine' require 'event_logger_rails/emitter' +require 'event_logger_rails/event' +require 'event_logger_rails/event_logger' require 'event_logger_rails/exceptions/invalid_logger_level' require 'event_logger_rails/exceptions/unregistered_event' require 'event_logger_rails/extensions/loggable' -require 'event_logger_rails/json_logger' +require 'event_logger_rails/formatters/json' require 'event_logger_rails/message' require 'event_logger_rails/middleware/capture_request_details' require 'event_logger_rails/output' @@ -22,10 +23,18 @@ module EventLoggerRails # @return [Symbol] The default level of the events logged by EventLoggerRails. mattr_accessor :default_level + # @!attribute [r] formatter + # @return [Class] The formatter to use for logging. + mattr_accessor :formatter + # @!attribute [r] logdev # @return [IO, #write] The log device used by EventLoggerRails. mattr_accessor :logdev + # @!attribute [r] logger_class + # @return [Class] The logger class used by EventLoggerRails. + mattr_accessor :logger_class + # @!attribute [r] registered_events # @return [Array] The events registry defined in the config/event_logger_rails.yml file. mattr_accessor :registered_events @@ -48,10 +57,9 @@ def self.setup # Returns or initializes the Emitter instance for EventLoggerRails. # - # @note The emitter is initialized with the configured log device. # @return [Emitter] The Emitter instance used for logging events. def self.emitter - @emitter ||= Emitter.new(logdev:) + @emitter ||= Emitter.new end # Forwards the arguments to the Emitter's log method. @@ -59,7 +67,7 @@ def self.emitter # @example # EventLoggerRails.log('foo.bar.baz', level: :info, data: { foo: 'bar' }) # @param (see Emitter#log) - # @return [void] + # @return (see Emitter#log) def self.log(...) emitter.log(...) end diff --git a/lib/event_logger_rails/emitter.rb b/lib/event_logger_rails/emitter.rb index 196b408..86bc6a6 100644 --- a/lib/event_logger_rails/emitter.rb +++ b/lib/event_logger_rails/emitter.rb @@ -1,14 +1,12 @@ # frozen_string_literal: true module EventLoggerRails - ## # Processes events, sending data to logger. class Emitter ## Initializes the emitter using the given log device for log output. - # - # @param logdev [IO, #write] The log device for log output. - def initialize(logdev:) - @logger = JsonLogger.new(logdev) + def initialize + logdev = EventLoggerRails.logdev + @logger = EventLoggerRails.logger_class.constantize.new(logdev) end # Validates and logs an event with the given level and data. @@ -20,7 +18,7 @@ def initialize(logdev:) # @param data [Hash] Additional data to log. # @return [Integer] The number of bytes written to the log. # @example - # emitter = EventLoggerRails::Emitter.new(logdev: $stdout) + # emitter = EventLoggerRails::Emitter.new # emitter.log('foo.bar.baz', level: :info, data: { foo: 'bar' }) def log(event, level:, data: {}) Event.new(event).validate! do |validated_event| diff --git a/lib/event_logger_rails/engine.rb b/lib/event_logger_rails/engine.rb index 377cb57..efb320e 100644 --- a/lib/event_logger_rails/engine.rb +++ b/lib/event_logger_rails/engine.rb @@ -13,8 +13,9 @@ class Engine < ::Rails::Engine # Initialize the EventLoggerRails configuration. config.event_logger_rails = ActiveSupport::OrderedOptions.new + config.event_logger_rails.formatter = 'EventLoggerRails::Formatters::JSON' config.event_logger_rails.logdev = "log/event_logger_rails.#{Rails.env}.log" - config.event_logger_rails.logger_class = 'EventLoggerRails::JsonLogger' + config.event_logger_rails.logger_class = 'EventLoggerRails::EventLogger' config.event_logger_rails.default_level = :warn # Add the EventLoggerRails middleware. @@ -28,13 +29,15 @@ class Engine < ::Rails::Engine EventLoggerRails.setup do |engine| # Set the default logging level from the registration. engine.default_level = app.config.event_logger_rails.default_level - # Set the log device from the registration. + # Set the formatter. + engine.formatter = app.config.event_logger_rails.formatter + # Set the log device. engine.logdev = app.config.event_logger_rails.logdev - # Set the logger class from the registration. + # Set the logger class. engine.logger_class = app.config.event_logger_rails.logger_class # Set the registered events from the registration. engine.registered_events = Rails.application.config_for(:event_logger_rails) - # Set the sensitive fields from the registration. + # Set the sensitive fields. engine.sensitive_fields = app.config.filter_parameters end end diff --git a/lib/event_logger_rails/json_logger.rb b/lib/event_logger_rails/event_logger.rb similarity index 54% rename from lib/event_logger_rails/json_logger.rb rename to lib/event_logger_rails/event_logger.rb index aad2bb2..a1944d3 100644 --- a/lib/event_logger_rails/json_logger.rb +++ b/lib/event_logger_rails/event_logger.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module EventLoggerRails - # Writes log entries in JSON format - class JsonLogger < ::Logger + # Writes log entries in configured format. + class EventLogger < ::Logger include ActiveSupport::LoggerSilence # Initializes the logger with a JSON formatter. @@ -10,10 +10,7 @@ class JsonLogger < ::Logger # @param logdev [IO, #write] The log device for log output. def initialize(...) super(...) - @formatter = proc do |level, timestamp, _progname, message| - output = Output.new(level:, timestamp:, message:) - "#{output.to_json}\n" - end + @formatter = EventLoggerRails.formatter.constantize.new || EventLoggerRails::Formatters::JSON.new end end end diff --git a/lib/event_logger_rails/formatters/json.rb b/lib/event_logger_rails/formatters/json.rb new file mode 100644 index 0000000..3f5a513 --- /dev/null +++ b/lib/event_logger_rails/formatters/json.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module EventLoggerRails + module Formatters + # Writes log entries in JSON format + class JSON < ActiveSupport::Logger::SimpleFormatter + # Initializes the logger with a JSON formatter. + # + # @param logdev [IO, #write] The log device for log output. + def call(level, timestamp, _progname, message) + output = Output.new(level:, timestamp:, message:) + "#{output.to_json}\n" + end + end + end +end diff --git a/spec/lib/event_logger_rails/emitter_spec.rb b/spec/lib/event_logger_rails/emitter_spec.rb index f0183ed..a11457b 100644 --- a/spec/lib/event_logger_rails/emitter_spec.rb +++ b/spec/lib/event_logger_rails/emitter_spec.rb @@ -4,7 +4,14 @@ RSpec.describe EventLoggerRails::Emitter do subject(:emitter) do - described_class.new(logdev: File.open(File::NULL, 'w')) + described_class.new + end + + before do + EventLoggerRails.setup do |config| + config.formatter = 'EventLoggerRails::Formatters::JSON' + config.logger_class = 'EventLoggerRails::EventLogger' + end end describe '#log' do diff --git a/spec/lib/event_logger_rails/event_logger_spec.rb b/spec/lib/event_logger_rails/event_logger_spec.rb new file mode 100644 index 0000000..ce52c9a --- /dev/null +++ b/spec/lib/event_logger_rails/event_logger_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe EventLoggerRails::EventLogger do + subject(:event_logger) { described_class.new(File.open(File::NULL, 'w')) } + + let(:message) { { foo: 'bar' } } + let(:buffer) { StringIO.new } + + before do + allow(IO).to receive(:open).and_return(buffer) + end + + context 'when default formatter is configured' do + it 'outputs the provided message in JSON format' do + event_logger.info(message) + log_output = JSON.parse(buffer.string, symbolize_names: true) + expect(log_output).to include(**message) + end + end +end diff --git a/spec/lib/event_logger_rails/formatters/json_spec.rb b/spec/lib/event_logger_rails/formatters/json_spec.rb new file mode 100644 index 0000000..34333fb --- /dev/null +++ b/spec/lib/event_logger_rails/formatters/json_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe EventLoggerRails::Formatters::JSON do + subject(:formatter) { described_class.new } + + describe '#call' do + subject(:method_call) { formatter.call(level, timestamp, progname, message) } + + let(:level) { :foo } + let(:timestamp) { Time.now } + let(:progname) { 'bar' } + let(:message) { { foo: 'bar' } } + + it 'returns the event reserved for the exception' do + expected_output = EventLoggerRails::Output.new(level:, timestamp:, message:).to_json + expect(method_call).to eq("#{expected_output}\n") + end + end +end diff --git a/spec/lib/event_logger_rails/json_logger_spec.rb b/spec/lib/event_logger_rails/json_logger_spec.rb deleted file mode 100644 index d653a55..0000000 --- a/spec/lib/event_logger_rails/json_logger_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EventLoggerRails::JsonLogger do - subject(:json_logger) { described_class.new(File.open(File::NULL, 'w')) } - - let(:message) { { foo: 'bar' } } - let(:buffer) { StringIO.new } - - before do - allow(IO).to receive(:open).and_return(buffer) - end - - it 'outputs the provided message in JSON format' do - json_logger.info(message) - log_output = JSON.parse(buffer.string, symbolize_names: true) - expect(log_output).to include(**message) - end -end diff --git a/spec/lib/event_logger_rails_spec.rb b/spec/lib/event_logger_rails_spec.rb index e460149..e4c9fbd 100644 --- a/spec/lib/event_logger_rails_spec.rb +++ b/spec/lib/event_logger_rails_spec.rb @@ -36,6 +36,36 @@ end end + describe 'formatter' do + subject(:engine_setup) do + engine.setup do |config| + config.formatter = formatter_class + end + end + + let(:formatter_class) { 'EventLoggerRails::Formatters::JSON' } + + it 'configures the formatter to use for initializing the logger' do + engine_setup + expect(described_class.formatter).to eq(formatter_class) + end + end + + describe 'logger_class' do + subject(:engine_setup) do + engine.setup do |config| + config.logger_class = logger_class + end + end + + let(:logger_class) { 'EventLoggerRails::EventLogger' } + + it 'configures the output device to use for initializing the logger' do + engine_setup + expect(described_class.logger_class).to eq(logger_class) + end + end + describe 'registered events' do subject(:engine_setup) do engine.setup do |config|