Skip to content

Commit

Permalink
Provide YARD documentation for gem
Browse files Browse the repository at this point in the history
  • Loading branch information
dickdavis committed Aug 11, 2024
1 parent 410473a commit 1a783db
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 17 deletions.
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
36 changes: 34 additions & 2 deletions lib/event_logger_rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,58 @@
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] logdev
# @return [IO, #write] The log device used by EventLoggerRails.
mattr_accessor :logdev

# @!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.
#
# @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:)
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 [void]
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
22 changes: 22 additions & 0 deletions lib/event_logger_rails/emitter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,24 @@ 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)
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(logdev: $stdout)
# 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
16 changes: 14 additions & 2 deletions lib/event_logger_rails/engine.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
# 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.logdev = "log/event_logger_rails.#{Rails.env}.log"
config.event_logger_rails.logger_class = 'EventLoggerRails::JsonLogger'
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 log device from the registration.
engine.logdev = app.config.event_logger_rails.logdev
# Set the logger class from the registration.
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.
engine.sensitive_fields = app.config.filter_parameters
end
end
Expand Down
81 changes: 79 additions & 2 deletions lib/event_logger_rails/event.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# frozen_string_literal: true

module EventLoggerRails
##
# Models an event for logging.
class Event
# Contains the default event registration.
DEFAULT_EVENTS = {
'event_logger_rails.logger_level.invalid' => {
description: 'Indicates provided level was invalid.',
Expand All @@ -20,51 +20,128 @@ class Event
}.freeze
private_constant :DEFAULT_EVENTS

attr_reader :identifier, :description, :level
# @!attribute [r] identifier
# @return [String] The identifier of the event.
attr_reader :identifier

# @!attribute [r] description
# @return [String] The description of the event.
attr_reader :description

# @!attribute [r] level
# @return [Symbol] The configured logging level of the event.
attr_reader :level

# Initializes the event using the provided identifier to determine its properties from
# either the default registration (for default events) or the user-defined registry.
#
# @param provided_identifier [EventLoggerRails::Event, String] The event or its identifier.
def initialize(provided_identifier)
@provided_identifier = provided_identifier.to_s

# Attempt to find default registration for event
if (default_event = DEFAULT_EVENTS[@provided_identifier])
default_registration = [@provided_identifier, *default_event&.values]
end

# Fallback to user-defined registration if default not found.
# Deconstruct registration to set identifier, description, and level attributes.
@identifier, @description, @level = default_registration || config_registration
end

# Converts the event into a hash and merges the given hash into it.
#
# @param kwargs [Hash] The hash to merge into the event.
# @return [Hash] The merged hash.
# @example
# event = EventLoggerRails::Event.new('event_logger_rails.event.testing')
# event.merge(foo: 'bar')
# # {
# # event_identifier: 'event_logger_rails.event.testing',
# # event_description: 'Event reserved for testing',
# # foo: 'bar'
# # }
def merge(...)
to_hash.merge(...)
end

# Determines if the event is valid.
#
# @return [Boolean] true if the event is valid, false otherwise.
# @example
# valid_event = EventLoggerRails::Event.new('event_logger_rails.event.testing')
# valid_event.valid? # => true
# invalid_event = EventLoggerRails::Event.new('foo.bar.baz')
# invalid_event.valid? # => false
def valid?
identifier.present?
end

# Validates the event and yields it to the given block.
#
# @note This only validates the event registration. Logger level is validated at the time of logging.
# @yield [self] Yields the event to the given block.
# @raise [EventLoggerRails::Exceptions::UnregisteredEvent] If the event is not registered.
# @example
# event = EventLoggerRails::Event.new('event_logger_rails.event.testing')
# event.validate! do |validated_event|
# puts "Event: #{validated_event}"
# end
def validate!
raise Exceptions::UnregisteredEvent.new(unregistered_event: self) unless valid?

yield(self)
end

# Returns a hash representation of the event.
#
# @return [Hash] The event as a hash.
# @example
# event = EventLoggerRails::Event.new('event_logger_rails.event.testing')
# event.to_hash
# # {
# # event_identifier: 'event_logger_rails.event.testing',
# # event_description: 'Event reserved for testing'
# # }
def to_hash
{
event_identifier: identifier,
event_description: description
}
end

# Returns a string representation of the event.
# The provided identifier is returned if the event is not registered.
#
# @return [String] The event as a string.
# @example
# event = EventLoggerRails::Event.new('event_logger_rails.event.testing')
# event.to_s # => 'event_logger_rails.event.testing'
def to_s
identifier&.to_s || provided_identifier.to_s
end

# Determines if the event is equivalent to the given object through string comparison.
#
# @param other [EventLoggerRails::Event] The event to compare.
# @return [Boolean] true if the event is equal to the given object, false otherwise.
# @example
# event = EventLoggerRails::Event.new('event_logger_rails.event.testing')
# event == 'event_logger_rails.event.testing' # => true
def ==(other)
to_s == other.to_s
end

private

# @!attribute [r] provided_identifier
# @return [String] The identifier provided when the event was initialized.
attr_reader :provided_identifier

# Parses the event identifier and looks up the details from the user-defined registry.
#
# @return [Array<String, String, Symbol>] The identifier, description, and level of the event.
# If the event is not registered, each array element will be nil.
def config_registration
parsed_event = provided_identifier.split('.').map(&:to_sym)
config = EventLoggerRails.registered_events.dig(*parsed_event)
Expand Down
9 changes: 8 additions & 1 deletion lib/event_logger_rails/exceptions/invalid_logger_level.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,31 @@

module EventLoggerRails
module Exceptions
##
# Indicates invalid log level provided.
class InvalidLoggerLevel < StandardError
# @!attribute [r] event
# @return [EventLoggerRails::Event] The default invalid logging level event.
attr_reader :event

# Initializes the exception with the given logger level.
def initialize(logger_level:)
super
@event = Event.new('event_logger_rails.logger_level.invalid')
@logger_level = logger_level
end

# Provides an informative error message.
#
# @return [String] The error message.
def message
"Invalid logger level provided: '#{logger_level.to_sym}'. " \
'Valid levels: :debug, :info, :warn, :error, :unknown.'
end

private

# @!attribute [r] logger_level
# @return [Symbol] The invalid logger level.
attr_reader :logger_level
end
end
Expand Down
11 changes: 10 additions & 1 deletion lib/event_logger_rails/exceptions/unregistered_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@

module EventLoggerRails
module Exceptions
##
# Indicates event provided not registered.
class UnregisteredEvent < StandardError
# @!attribute [r] event
# @return [EventLoggerRails::Event] The default event for unregistered events.
attr_reader :event

# Initializes the exception with the given unregistered event.
#
# @param unregistered_event [EventLoggerRails::Event] The unregistered event.
def initialize(unregistered_event:)
super()
@event = Event.new('event_logger_rails.event.unregistered')
@unregistered_event = unregistered_event
end

# Provides an informative error message.
#
# @return [String] The error message.
def message
"Event provided not registered: #{unregistered_event}"
end

private

# @!attribute [r] unregistered_event
# @return [EventLoggerRails::Event] The unregistered event.
attr_reader :unregistered_event
end
end
Expand Down
Loading

0 comments on commit 1a783db

Please sign in to comment.