Skip to content

Dictionary Based Configuration

jduquennoy edited this page Nov 2, 2015 · 8 revisions

Configuring the logging system using dictionary

Pros

  • Dictionary can be easily stored in a file
  • Multiple dictionaries can be loaded, and will complement the previously loaded ones. The application can embed a base setup, and propose the user to create a simple configuration file to customize it.
  • A dictionary in a file can be more readable than code
  • Fully functional in objective-c

Cons

  • Less dynamic than code-base configuration

Anatomy of a configuration dictionary

A its root level, a root dictionary can contain 4 entries :

  • Formatters : an array of formatters that can be referenced by appenders
  • Appenders : an array of appenders that can be referenced by loggers
  • RootLogger : the setup of the root logger. The root logger is the only entry that does not need an identifier
  • Loggers : an array of loggers

Elements that you can find in the arrays and the root logger are all dictionaries. The keys of those dictionaries are defined in the following section.

Formatters

  • Identifier : The identifier that will be used to reference the formatter in the appenders. This entry is mandatory.
  • Class : The kind of formatter (the name of the class as defined in the provider formatters list). This entry is mandatory.
  • Formatter specific parameters : the different parameters of the specific formatter, as listed in the provided formatters list.

Appenders

  • Identifier : The identifier that will be used to reference the formatter in the appenders. This entry is mandatory.
  • Class : The kind of formatter (the name of the class as defined in the provided appenders list). This entry is mandatory.
  • ThresholdLevel : The log level name bellow which logs will be ignored. This entry is mandatory.
  • FormatterId : The identifier of the formatter to use for this appender. This entry is optional, if it exists, a formatter with the specified identifier must exist in the array of formatters.
  • Appender specific parameters : the different parameters of the specified appender kind, as listed in the provided appenders list.

Loggers

Loggers are quite different from other items for one reason, there is a hierarchy between them : when you define a logger, most properties are not mandatory. If no value is provided, the one from the closest parent will be used (see the logger factory details for a more in depth explanation of how the parent is found). Ultimately, the root logger will be used as parent. If it is not configured, its default values applies.
In this example bellow, logger1 will inherit the root logger's appenders, while logger2.son will inherit logger2's appenders, and the root logger's threshold level.

Here are the parameters for a logger definition :

  • Identifier : The UTI-style identifier of the logger. This entry is mandatory for all loggers but the root one.
  • ThresholdLevel : The log level name bellow which logs will be ignored. This entry is optional, the parent value will be inherited if not specified.
  • AppenderIds : An array of appender identifiers that will be used to issue the logs. This entry is optional, the parent value will be inherited if not specified. An appender with the specified identifier must exist in the array of appenders for all entries.
  • Asynchronous : A boolean indicating indicating if the logger should be asynchronous. By default, the logger will be synchronous.

The reader will sort logger definitions by identifier length : the shorter identifiers will get read first, so that you don't have to worry about the order in which you declare loggers, the hierarchy will always be respected.

The root logger default values

  • ThresholdLevel : Debug
  • Appenders : A single ConsoleAppender, with a Debug threshold level, and an Error ErrorThresholdLevel
  • Asynchronous : A boolean indicating indicating if the logger should be asynchronous. By default, the logger will be synchronous.

So by default, the root logger logs all messages bellow the Error level to stdout, and all messages at Error or Fatal level to stdErr.

Complete dictionary example

Here is a dictionary that will create a root logger that will log fatal, error and warning messages to the console, error and fatal messages to a specific file. A specific logger is added to enable debug logs in the console for a given feature of the software.
It is provided here in Yaml format, for its readability.

Formatters:
	- Identifier: defaultFormatter
	  Class: PatternFormatter
	  Pattern: %d - %m
Appenders:
	- Identifier: consoleAppender
	  Class: StdOutAppender
	  ThresholdLevel: Debug
	  ErrorThresholdLevel: Error
	  TextColors:
	  	  Error: Red
	  	  Warning: Orange
	- Identifier: errorFileAppender
	  Class: FileAppender
	  ThresholdLevel: Error
	  FilePath: ~/logs/errors.log
RootLogger:
	- ThresholdLevel: Warning
	- AppenderIds:
		- consoleAppender
		- errorFileAppender
Loggers:
	- Identifier: MySoftware.GreatFeature
	- ThresholdLevel: Debug
	- Asynchronous: true

As of today, Log4Swift can natively read plist files. Example plist are available in the source code, in the Log4swiftTests/Resources repository.

Example code

This proposed code will load the configuration from an embedded plist file called logConfiguration.plist, and will then try to load another external configuration file in the user home directory.

Keep in mind that accessing a file in the user directory will require some special entitlements if your application is sandboxed.

func setupLogSystem() throws {
	let loggerFactory = LoggerFactory.sharedInstance;
	
	let embeddedLogConfigurationPath = NSBundle.mainBundle().pathForResource("logConfiguration", ofType: "plist");
	if let embeddedLogConfigurationPath = embeddedLogConfigurationPath {
		try loggerFactory.readConfigurationFromPlistFile(embeddedLogConfigurationPath);
	}
    
	let userLogConfigurationPath = "~/logConfiguration.plist".stringByExpandingTildeInPath;
	if NSFileManager.defaultManager().fileExistsAtPath(userLogConfigurationPath) {
		try loggerFactory.readConfigurationFromPlistFile(userLogConfigurationPath);
	}
}