Skip to content

2.3 Logging

Marjan Nikolovski edited this page Apr 9, 2021 · 1 revision

Purpose

Logging is the act of recording events that occur in our software, in the simplest case, to a single log file.

A transaction log is a file of the communications between a system and the users of that system, or a data collection method that automatically captures the type, content, or time of transactions made by a person with that system.

Signals offers single implementation for the Logging aspect out of the box:

  • Signals.Aspects.Logging.NLog

Only one implementation can be active at a time.

How to use

When we implement any type of process a logger is automatically injected in the process context. Every time there is a failed result or an exception in some process, the faulted result is logged automatically.

public class MyProcess : BusinessProcess<VoidResult>
{
    /// <summary>
    /// Authenticate process
    /// </summary>
    /// <returns></returns>
    public override VoidResult Auth()
    {
        return Ok();
    }

    /// <summary>
    /// Validate process
    /// </summary>
    /// <returns></returns>
    public override VoidResult Validate()
    {
        return Ok();
    }

    /// <summary>
    /// Handle process
    /// </summary>
    /// <returns></returns>
    public override VoidResult Handle()
    {
        // add data in log instance to describe the log entry
        var log = new LogEntry();

        Context.Logger.Trace(log);
        Context.Logger.Info(log);
        Context.Logger.Warn(log);
        Context.Logger.Fatal(log);

        return Fail(); // will write an info with failed result in log
    }
}

Other more manual way of using the logger is through the ILogger instance.

public class MyClass
{
    public void LogData()
    {
        var logger = SystemBootstrapper.GetInstance<ILogger>();

        // add data in log instance to describe the log entry
        var log = new LogEntry();

        logger.Trace(log);
        logger.Info(log);
        logger.Warn(log);
        logger.Fatal(log);
    }
}

Configuration

We configure the logging aspect by using an instance of Signals.Aspects.Logging.Configurations.ILoggingConfiguration which we pass in the ApplicationBootstrapConfiguration instance (web or background) at startup.

Properties explanation

ConsoleLoggingConfiguration

  • MessageTemplate: Log message template. Default "${longdate} ${logger} ${message}${exception:format=ToString}"

DatabaseLoggingConfiguration

  • Host: Ip address or host name of the database
  • Database: Database name
  • TableName: Logs table name
  • Username: Database username
  • Password: Database password
  • DataProvider: Database driver type. Default DataProvider.SqlClient

FileLoggingConfiguration

  • MessageTemplate: Log message template. Default "${longdate} ${logger} ${message}${exception:format=ToString}"
  • MaxArchiveFiles: Number of maxinum days and number of files to be retained. Default 90
  • LogFileDirectory: Path to log files. Default "${basedir}"

Examples

Using Signals.Aspects.Logging.NLog

ConsoleLoggingConfiguration

services
    .AddSignals(config =>
    {
        config.LoggingConfiguration = new ConsoleLoggingConfiguration
        {
            MessageTemplate = "${longdate} ${logger} ${message}${exception:format=ToString}"
        };
    });

DatabaseLoggingConfiguration

services
    .AddSignals(config =>
    {
        config.LoggingConfiguration = new DatabaseLoggingConfiguration
        {
            Host = "localhost",
            Database = "my.db",
            TableName = "LogEntry",
            Username = "my.user",
            Password = "my.password",
            DataProvider = DataProvider.SqlClient,
        };
    });

FileLoggingConfiguration

services
    .AddSignals(config =>
    {
        config.LoggingConfiguration = new FileLoggingConfiguration
        {
            MessageTemplate = "${longdate} ${logger} ${message}${exception:format=ToString}",
            MaxArchiveFiles = 90,
            LogFileDirectory = "${basedir}"
        };
    });

Extending logging

  1. Install package Signals.Aspects.Logging
  2. Create class with implementation of Signals.Aspects.Logging.Configurations.ILoggingConfiguration
/// <summary>
/// Logging configuration contract
/// </summary>
public class MyLoggingConfiguration : ILoggingConfiguration
{
    /// <summary>
    /// Custom property
    /// </summary>
    public string MyProperty { get; set; }
}
  1. Create class with implementation of Signals.Aspects.Logging.ILogger
/// <summary>
/// Logging contract
/// </summary>
public class MyLogger : ILogger
{
    private MyLoggingConfiguration _configuraiton;

    /// <summary>
    /// CTOR
    /// </summary>
    /// <param name="configuraiton"></param>
    public MyLogger(MyLoggingConfiguration configuraiton)
    {
        _configuraiton = configuraiton;
    }


    /// <summary>
    /// Log an message consisting of args with log level debug
    /// </summary>
    /// <param name="logEntry"></param>
    void Debug(T logEntry);

    /// <summary>
    /// Log an message with log level debug
    /// </summary>
    /// <param name="message"></param>
    void Debug(string message);

    /// <summary>
    /// Create string description of the log entry
    /// </summary>
    /// <param name="logEntry"></param>
    string DescribeLogEntry(T logEntry);

    /// <summary>
    /// Log an message consisting of args with log level error
    /// </summary>
    /// <param name="logEntry"></param>
    void Error(T logEntry);

    /// <summary>
    /// Log an message with log level error
    /// </summary>
    /// <param name="args"></param>
    void Error(params object[] args);

    /// <summary>
    /// Log an exception
    /// </summary>
    /// <param name="message"></param>
    /// <param name="exception"></param>
    void Exception(string message, Exception exception);

    /// <summary>
    /// Log an message consisting of args with log level fatal
    /// </summary>
    /// <param name="logEntry"></param>
    void Fatal(T logEntry);

    /// <summary>
    /// Log an message consisting of args with log level info
    /// </summary>
    /// <param name="logEntry"></param>
    void Info(T logEntry);

    /// <summary>
    /// Log an message consisting of args
    /// </summary>
    /// <param name="args"></param>
    void Info(params object[] args);

    /// <summary>
    /// Log an message consisting of args with log level trace
    /// </summary>
    /// <param name="logEntry"></param>
    void Trace(T logEntry);

    /// <summary>
    /// Log an message consisting of args with log level warn
    /// </summary>
    /// <param name="logEntry"></param>
    void Warn(T logEntry);
}
  1. Use our implementation of ILoggerConfiguration when configuring our application
public static IServiceProvider AddSignals(this IServiceCollection services)
{
    services
        .AddSignals(config =>
        {
            config.LoggerConfiguration = new MyLoggerConfiguration
            {
                MyProperty = "my_value"
            };;
        });
}
Clone this wiki locally