Skip to content

Commit

Permalink
Improve documentation and configurability (#47)
Browse files Browse the repository at this point in the history
* Use YARD for documentation

* Provide YARD documentation for gem

* Refactor to allow configurable logger and formatter classes

* Update README
  • Loading branch information
dickdavis authored Aug 11, 2024
1 parent 0be9300 commit 5ca73b1
Show file tree
Hide file tree
Showing 27 changed files with 426 additions and 63 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@
/spec/dummy/storage/
/spec/dummy/tmp/
*.gem
.yardoc/
docs/
3 changes: 3 additions & 0 deletions .yardopts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--output-dir docs/
--private
--markup=markdown
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ gemspec

group :development do
gem 'overcommit'
gem 'redcarpet'
gem 'reek'
gem 'rubocop'
gem 'rubocop-performance', require: false
gem 'rubocop-rspec', require: false
gem 'solargraph'
gem 'solargraph-rails'
gem 'sqlite3'
gem 'yard'
end

group :test do
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ GEM
nokogiri (1.15.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nokogiri (1.15.4-x86_64-linux)
racc (~> 1.4)
overcommit (0.58.0)
childprocess (>= 0.6.3, < 5)
iniparse (~> 1.4)
Expand Down Expand Up @@ -167,6 +169,7 @@ GEM
zeitwerk (~> 2.5)
rainbow (3.0.0)
rake (13.0.6)
redcarpet (3.6.0)
reek (6.0.6)
kwalify (~> 0.7.0)
parser (~> 3.0.0)
Expand Down Expand Up @@ -251,6 +254,7 @@ DEPENDENCIES
debug
event_logger_rails!
overcommit
redcarpet
reek
rspec-rails
rubocop
Expand All @@ -260,6 +264,7 @@ DEPENDENCIES
solargraph-rails
sqlite3
warning
yard

BUNDLED WITH
2.2.21
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,14 @@ Rails.application.configure do |config|
end
```

You can configure a custom formatter. Reference `EventLoggerRails::Formatters::JSON` for an example.

```ruby
Rails.application.configure do |config|
config.event_logger_rails.formatter = 'MyCustomFormatterClass'
end
```

By default, `EventLoggerRails` outputs to a separate log file (`log/event_logger_rails.#{Rails.env}.log`) from normal Rails log output, allowing
you to ingest these logs independently. If you wish to set an alternative log device to capture output, you can configure it in `config/application.rb`:

Expand All @@ -344,13 +352,21 @@ Rails.application.configure do |config|
end
```

You can also configure the Rails logger to use `EventLoggerRails::JsonLogger` to render structured logs in JSON format with the additional app and request data.
You can configure a custom logger. Reference `EventLoggerRails::EventLogger` for an example.

```ruby
Rails.application.configure do |config|
config.event_logger_rails.logger_class = 'MyCustomLoggerClass'
end
```

You can also configure the Rails logger to use `EventLoggerRails::EventLogger` to render structured logs in JSON format with the additional app and request data.

```ruby
Rails.application.configure do
config.colorize_logging = false
config.log_level = ENV.fetch('RAILS_LOG_LEVEL', :info)
logger = EventLoggerRails::JsonLogger.new($stdout)
logger = EventLoggerRails::EventLogger.new($stdout)
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module LoggableController
extend ActiveSupport::Concern
include EventLoggerRails::Extensions::Loggable

# Includes the controller name and action in the log output.
#
# @return [Hash] The data to include in log output.
def optional_data
{
action: action_name,
Expand Down
3 changes: 3 additions & 0 deletions app/models/concerns/event_logger_rails/loggable_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module LoggableModel
extend ActiveSupport::Concern
include EventLoggerRails::Extensions::Loggable

# Includes the model name and instance ID in the log output.
#
# @return [Hash] The data to include in log output.
def optional_data
{
model: self.class.name,
Expand Down
52 changes: 46 additions & 6 deletions lib/event_logger_rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,79 @@

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'
require 'event_logger_rails/version'

##
# Namespace for EventLoggerRails gem
# Provides configurable state and public API for EventLoggerRails.
# Also serves as the namespace for the gem.
module EventLoggerRails
# @!attribute [r] default_level
# @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<Hash>] The events registry defined in the config/event_logger_rails.yml file.
mattr_accessor :registered_events

# @!attribute [r] sensitive_fields
# @return [Array<Symbol>] The fields which may contain sensitive data that EventLoggerRails should filter.
mattr_accessor :sensitive_fields

# Provides a method for configuring EventLoggerRails.
#
# @yield [self] Gives the class itself to the block for configuration.
# @example
# EventLoggerRails.setup do |config|
# config.default_level = :info
# end
# @return [void]
def self.setup
yield self
end

# Returns or initializes the Emitter instance for EventLoggerRails.
#
# @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.
#
# @example
# EventLoggerRails.log('foo.bar.baz', level: :info, data: { foo: 'bar' })
# @param (see Emitter#log)
# @return (see Emitter#log)
def self.log(...)
emitter.log(...)
end

# Resets the Emitter instance.
#
# @return [void]
def self.reset
@emitter = nil
end
Expand Down
14 changes: 13 additions & 1 deletion lib/event_logger_rails/current_request.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
# frozen_string_literal: true

module EventLoggerRails
##
# Provides global state with request details
class CurrentRequest < ActiveSupport::CurrentAttributes
# @note Defines the attributes for the current request object.
# @!attribute [rw] id
# @return [String] The ID of the request.
# @!attribute [rw] format
# @return [Symbol] The format of the request.
# @!attribute [rw] method
# @return [String] The HTTP method of the request.
# @!attribute [rw] parameters
# @return [Hash] The parameters of the request.
# @!attribute [rw] path
# @return [String] The path of the request.
# @!attribute [rw] remote_ip
# @return [String] The remote IP of the request.
attribute :id, :format, :method, :parameters, :path, :remote_ip
end
end
28 changes: 25 additions & 3 deletions lib/event_logger_rails/emitter.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
# frozen_string_literal: true

module EventLoggerRails
##
# Processes events, sending data to logger.
class Emitter
def initialize(logdev:)
@logger = JsonLogger.new(logdev)
# Initializes the emitter.
# It references the configured log device for log output.
# It references the configured logger class for logging, falling back to EventLogger.
def initialize
logdev = EventLoggerRails.logdev
@logger = EventLoggerRails.logger_class.constantize.new(logdev) || EventLoggerRails::EventLogger.new(logdev)
end

# Validates and logs an event with the given level and data.
# If an error is raised, it recursively calls itself with the error's event.
#
# @note Prefer to use the public API provided by `EventLoggerRails.log()`.
# @param event [EventLoggerRails::Event, String] The event to log. Can be a string or an Event object.
# @param level [Symbol] The level of the event.
# @param data [Hash] Additional data to log.
# @return [Integer] The number of bytes written to the log.
# @example
# 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|
message = Message.new(event: validated_event, data:)
Expand All @@ -20,8 +34,16 @@ def log(event, level:, data: {})

private

# @!attribute [r] logger
# @return [JsonLogger] The logger instance used for log output.
attr_reader :logger

# Logs a message with the given level.
#
# @param message [String] The message to log.
# @param level [Symbol] The level of the message.
# @return [Integer] The number of bytes written to the log.
# @raise [EventLoggerRails::Exceptions::InvalidLoggerLevel] If the level is invalid.
def log_message(message, level)
logger.send(level) { message }
rescue NoMethodError
Expand Down
19 changes: 17 additions & 2 deletions lib/event_logger_rails/engine.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
# frozen_string_literal: true

module EventLoggerRails
##
# Engine for plugging into Rails
# Engine for plugging into Rails.
class Engine < ::Rails::Engine
# Use the EventLoggerRails namespace.
isolate_namespace EventLoggerRails

# Use the rspec test framework.
config.generators do |generator|
generator.test_framework :rspec
end

# 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::EventLogger'
config.event_logger_rails.default_level = :warn

# Add the EventLoggerRails middleware.
initializer 'event_logger_rails.add_middleware' do |app|
# Use middleware to capture the request details.
app.middleware.use Middleware::CaptureRequestDetails
end

# Configure EventLoggerRails
config.after_initialize do |app|
EventLoggerRails.setup do |engine|
# Set the default logging level from the registration.
engine.default_level = app.config.event_logger_rails.default_level
# 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.
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.
engine.sensitive_fields = app.config.filter_parameters
end
end
Expand Down
Loading

0 comments on commit 5ca73b1

Please sign in to comment.