10. Logging

Copland has an integrated logging system based on a slight variation of Ruby’s own “logger” library. In addition to the features of that library, Copland’s logging system provides:

  • A logger factory for creating shared logger instances
  • YAML-based configuration of the factory
  • Customizable message formatting

10.1. Log Factory

Each registry instance has its own instance of Copland::LogFactory. This provides a way to map a name to a logger instance, and allows each service point (for example) to have its own logger instance. This allows various logger settings to be tweaked per service-point.

For example, if I want debugging information to be logged for one service point, but not for another, I can specify the logging priority level for the one service point to include debug messages, and have such messages excluded for the other service point.

The log factory allows default values to be set for the priority level, date format string, and message format string, which will be applied to any logger that does not have a specific value set for those values. The log factory also allows configuration of the IO device to log to, the filename to log to, and various other (Logger-specific) data items (see the API documentation for Copland::LogFactory for more information).

If you ever need to access the current log factory instance, you can get it from the registry as the copland.LogFactory service.

10.2. Configuration

There are three ways to configure loggers. The first is programmatically, manually tweaking each log instance directly. The second is to pass configuration options to the registry itself when it is built, and the third is to use a configuration file.

Programmatic Configuration

To programmatically configure a logger, just obtain a handle to a logger instance and then tweak it via the accessor methods of the logger:

  factory = registry.service( "copland.LogFactory" )
  log = factory.get( "some.log.name" )

  # Standard Logger attributes
  log.level = Logger::INFO
  log.progname = "different.log.name" 
  log.datetime_format = "%Y%m%d" 

  # Extended Logger attributes (provided by Copland)
  log.message_format = "[%-5p] %d %c: %m" 

Note: programmatically changing the logger progname does not change the name by which the logger is known to the factory!

In general, programmatic tweaking of the logger attributes as demonstrated above is not going to be the best way to do it. First of all, it is tedious, and bug prone. Secondly, it is difficult to know where the best place to do such work is, since you need to set those attributes before any other service tries to access that log.

Configuration via Registry.build

The second approach is to pass logger configuration options to Registry.build, when you create a registry instance:

  registry = Copland::Registry.build(
    :log_device => STDOUT,
    :log_default_level => Logger::INFO,

This gives you much greater flexibility than the previous approach, since it allows you to specify a greater array of options. The allowed options are:

:log_config_file This is the name of the configuration file to use to configure the log factory. It defaults to “logger.yml”. (See the next section for more information.)
:log_device This is the IO (or pseudo-IO) object to write log messages to. If this option is specified, :log_filename must not be specified (and vice versa). This allows you to log messages to arbitrary IO streams.
:log_filename This is the name of the file to write log messages to. If this option is specified, :log_device must not be specified (and vice versa). This defaults to ”./copland.log”.
:log_roll_age This specifies the number of days before the log should be rolled. (This option is only useful when used with :log_filename.)
:log_roll_frequency This should be either nil, “daily”, “weekly”, or “monthly”, and specifies how frequently the log should be rolled. This option cannot be used with :log_roll_age.
:log_roll_size This specifies the maximum size of a log file. Once the log file gets larger than this value, the log will be rolled. (This option is only useful when used with :log_filename.)
:log_default_date_format This is the default date format string to use for the loggers that are created. It may be any string that is understood by Time#strftime. If nil (the default), then the default Logger date format string will be used.
:log_default_message_format This is the default message format string to use for the loggers that are created. It is a printf-styled string with special format specifiers—see the API documentation for Copland::Logger#message_format= for the available specifiers.
:log_default_level This is the default level for each logger. Messages that are logged below this level (also “priority” or “severity”) will not be logged. By default, all messages are logged.
:log_levels This is a hash. Each key should be a string containing a regular expression. For every logger whose name matches one of these patterns, the corresponding value will be used to define specific values for that logger. The value for each key must either be a string (in which case it is the name of the severity level to use), or a hash (containing any of the keys “level”, “date-format”, or “message-format”).

Configuration via YAML

The third option (and the most flexible) is to use a YAML configuration file to define the parameters of the log factory and its loggers. By default, this file is called “logger.yml”, but you can specify a different logger configuration file via the :log_config_file option to Copland::Registry.build. By default, the configuration file must reside in current working directory, but you can specify an explicit path in the string you give to :log_config_file.

The file has options that correspond to the configuration parameters described above:

  filename: log/filename.log
  roll-age: 5
  default-date-format: %Y%m%d%H%M%S
  default-message-format: %p %d %t %C: %m (%l)
  default-level: DEBUG
    copland.*: WARN
      level: INFO
      date-format: %Y%m%d::%H%M%S
      message-format: %p %d: %m