Skip to content

unleashed/injectedlogger

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Gem Version

A logger injection gem

This gem can be used to inject several loggers in different parts of your Ruby project.

It will try to support as many methods and levels as the underlying object supports, and fall back to a supported level in case some levels are not available

How to use it

You have to declare the log levels that your code will be using, and InjectedLogger will make sure that the underlying object supports them.

The user of your code will just pass the logger and refer to either a module of yours or a symbolic name or string.

Suppose we have two projects, one of which has the other as dependency, and has a logger available. The other project wants to log whatever it does using a logger injected (provided) by the first project.

On the code injecting the logger:

Inject our logger on Dependency::Logger, which is the module used by Dependency for logging:

InjectedLogger.inject mylogger, on: Dependency::Logger

Inject our logger on whatever is identified by 'dependency-logger':

InjectedLogger.inject mylogger, on: 'dependency-logger'

Inject our logger on 'dependency-logger', and have it provide :debug, :info and :invented log levels, with whatever of those being actually unsupported by mylogger emulated with :info:

InjectedLogger.inject mylogger, levels: [:debug, :info, :invented], fallback: :info, on: 'dependency-logger'

Inject our logger on Dependency::Logger and prefix the messages with '[logger-for-dep]':

InjectedLogger.inject mylogger, prefix: '[logger-for-dep]', on: Dependency::Logger

You can also avoid depending on InjectedLogger if you call the inject method directly on the class/module where your dependency declares the logger:

Dependency::Logger.inject mylogger, prefix: '[logger-for-dep]'

On the code where you want a logger injected:

This sets up a module with a logger method identified as 'dependency-logger' and declaring requirements of :debug, :info, and :invented log levels. In case no logger gets injected by the time logger is needed, the standard Ruby logger with output on STDERR will be used. After a logger is finally available, this also makes sure the prefix for each message is '[dependency]' (overriding what the injector set up):

module Dependency
  module Logger
    InjectedLogger.use :debug, :info, :invented, as: 'dependency-logger' do
      # this gets executed if no logger has been injected at use time
      require 'logger'
      { logger: Logger.new(STDERR) } # optionally add parameters for `inject`
    end
    InjectedLogger.after_injection on: 'dependency-logger' do |logger|
      logger.prefix = '[dependency]'
    end
  end
end

Now you can use your module elsewhere in your project:

class WantsLogging
  include Dependency::Logger

  def some_method_needing_logging
    logger.info 'some info'
    # logger blocks are also supported
    logger.invented do
      find_answer_to_ultimate_question_of life, universe and ObjectSpace.each_object
      'using the invented log level is computationally expensive so ' \
      'I pass this computation as a block to avoid computing it in ' \
      'case the logger filters this level'
    end
  end
end

If you write this inside Dependency::Logger, you will get a mylogger method in Dependency::Logger requiring the :debug and :info levels and falling back to the Ruby logger, identified for injection with Dependency::Logger:

InjectedLogger.use :debug, :info, method_name: :mylogger do
  require 'logger'
  { logger: Logger.new(STDERR)
end

You can also force the definition of the method elsewhere:

InjectedLogger.use :debug, :info, method_name: :mylogger, on: Dependency::OtherLogger do
  [...]
end

If you omit the block, InjectedLogger will provide a default logger (note that we are forced to use the :on parameter if we have no block).

InjectedLogger.use :debug, :info, on: self

Note that you do not need to specify the :on parameter for InjectedLogger.use nor InjectedLogger.after_injection IFF you provide a block to those methods and want to imply on: self.

You can also get the logger invoking it directly on your module:

SomeOtherGem.logger = Dependency::Logger.logger

or... :)

OtherDependency.inject Dependency::Logger.logger, prefix: '[other-dependency]'

Generating the gem

Both bundler and rspec are required to build the gem:

$ gem install bundler rspec

Run rake -T to see available tasks. The gem can be built with:

$ rake build

Or, if you want to make sure everything works correctly:

$ bundle exec rake build

Installation

After generating the gem, install it using:

$ gem install pkg/injectedlogger-*.gem

About

An object that receives a logger for you to use

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages