Skip to content
Ioan Crișan edited this page Oct 23, 2020 · 13 revisions

Aims of the logging infrastructure

  • Provide implementation agnostic logging contracts.
  • Make loggers available through the composition.

The log manager

The log manager is a service registered in the ambient services, with the ILogManager service contract. It provides the following method:

  • GetLogger(loggerName): ILogger: retrieves the logger with the provided name.

The logger

A logger implementes the ILogger contract, is identified by its name and is created by the log manager. For composition purposes, a generic ILogger<TService> service contract is provided to be imported by parts involved in composition.

It provides a single method which logs the provided arguments at the indicated level:

  • Log(level: LogLevel, exception: Exception, messageFormat: string, params args: object[]). Note to implementors: the exception may be null, so be cautious and handle this case too. For example, the LoggerExtensions.Log extension method passes a null exception.

For convenience, however, extension methods are provided to log fatal errors, errors, warnings, information, debug and trace data.

Consuming logging services

There are two kinds of components which can consume logging services:

  • Composable components
  • Non-composable components and static classes

To log anything, each consumer must firstly receive the logger it requires to consume its services by using specific means. Recommendations:

  • Do not use directly specific loggers, like log4net or NLog, instead prefer the ILogger interface. Reason: someday the circumstances may dictate to change the logging framework, and it will be a lot easier to change only the logger implementation compared to all logger specific code.

Composable components

Composable components should just import the logger either through the constructor or as a writable property, with the type ILogger<ComponentType>, where ComponentType is the type of the composable component.

Example

    [SingletonAppServiceContract]
    public interface IModelContainer
    {
        //...
    }

    public class ModelContainer : IModelContainer
    {
        /// <summary>
        /// Gets or sets the logger.
        /// </summary>
        public ILogger<ModelContainer> Logger { get; set; }
    }

Non-composable components and static classes

Non-composable components and static classes should use the extension method ambientServices.GetLogger<TypeToLog>() to get the logger for their type.

Example

    public static class ReflectionHelper
    {
        /// <summary>
        /// Logger instance.
        /// </summary>
        private static readonly ILogger Logger = AmbientServices.Instance.GetLogger<ReflectionHelper>();
	
        //...
    }

Caution: Make sure that at the time of calling GetLogger, the ambient services are properly initialized with the desired log manager, otherwise a logger doing nothing will be provided.

Specific implementations

By default, a NullLoggerManager will be used, if not overwritten with a more meaningful implementation like the following:

  • Kephas.Logging.NLog: use ambientServices.WithNLogManager().
  • Kephas.Logging.Serilog: use ambientServices.WithSerilogManager().
  • Kephas.Logging.Log4Net: use ambientServices.WithLog4NetManager().

Clone this wiki locally