Tag Archives: Logging

Logging Extension Updates

The logging extensions for MicroLite have been updated as follows and are available on nuget.org.

log4net

The log4net extension has been updated to target log4net 2.0.5 or later and a .NET 4.6 compiled build has been added.

NLog

The NLog extension has been updated to target NLog 4.4.0 or later and a .NET 4.6 compiled build has been added.

Serilog

The Serilog extension has been updated to target Serilog 2.3.0 or later, a .NET 4.6 compiled build has been added and the .NET 4.0 build has been removed.

MicroLite.Logging.Serilog

Since very early on MicroLite has had support for writing to your application log via the MicroLite.Logging.Log4Net or MicroLite.Logging.NLog.

With the release of MicroLite 6.1 we have added support for Serilog via the MicroLite.Logging.Serilog package.

You can see the details of how to configure it on the GitHub site.

Please note that whilst this will enable MicroLite to write to your log, it doesn’t support the usual Serilog format log messages so the statements will be formatted strings.

For example, you would see

Beginning a new Transaction with the IsolationLevel 'ReadCommitted'

In the log rather than

Beginning a new Transaction with the IsolationLevel '{isolationLevel}'

This is because MicroLite does not know which if any logging library it will be writing to at runtime so it has to use standard .NET format strings for its log messages.

Upgrading to MicroLite 6.1

There are a couple of changes in MicroLite 6.1 which you need to be aware of when updating from a previous version.

Listeners

In MicroLite 6.1, we have split the IListener interface into 3:

IDeleteListener containing the following methods:

void AfterDelete(object instance, int rowsAffected);
void BeforeDelete(object instance);

IInsertListener containing the following methods:

void AfterInsert(object instance, object executeScalarResult);
void BeforeInsert(object instance);

IUpdateListener containing the following methods:

void AfterUpdate(object instance, int rowsAffected);
void BeforeUpdate(object instance);

You can create as many listeners as you like implementing all or some of the above interfaces and register them in the appropriate collections.

Example – Create a listener which only cares about insert operations:

public class InsertOnlyListener : IInsertListener
{
   public void AfterInsert(object instance, int rowsAffected) { ... }
   public void BeforeInsert(object instance) { ... }
}

Listener.InsertListeners.Add(new MyInsertListener());

Example – Create a listener which cares about insert and update operations:

public class AuditListener : IInsertListener, IUpdateListener
{
   public void AfterInsert(object instance, int rowsAffected) { ... }
   public void AfterUpdate(object instance, int rowsAffected) { ... }
   public void BeforeInsert(object instance) { ... }
   public void BeforeUpdate(object instance) { ... }
}

var auditListener = new AuditListener();
Listener.InsertListeners.Add(auditListener);
Listener.UpdateListeners.Add(auditListener);

SQL Builder

The “write” builders (SqlBuilder.Delete and SqlBuilder.Update) now offer the same functionality for single columns as the read builder (SqlBuilder.Select):

  • Between(object lower, object upper);
  • In(params object[] args);
  • In(params SqlQuery[] subQueries);
  • In(SqlQuery subQuery);
  • IsEqualTo(object comparisonValue);
  • IsGreaterThan(object comparisonValue);
  • IsGreaterThanOrEqualTo(object comparisonValue);
  • IsLessThan(object comparisonValue);
  • IsLessThanOrEqualTo(object comparisonValue);
  • IsLike(object comparisonValue);
  • IsNotEqualTo(object comparisonValue);
  • IsNotLike(object comparisonValue);
  • IsNotNull();
  • IsNull();
  • NotBetween(object lower, object upper);
  • NotIn(params object[] args);
  • NotIn(params SqlQuery[] subQueries);
  • NotIn(SqlQuery subQuery);

MicroLite 5.0 – Changes to Logging

In MicroLite 5.0, we have updated the logging API. The information in this post supersedes the information in the Using Logging in Custom Listeners post.

The extension methods which have been removed and the LogManager has been updated so that it will return an empty logger if none is configured. This means that as long as you call MicroLite.Logging.LogManager.GetCurrentClassLog(); you will be returned with an ILog instance.

The ILog interface has been updated with the following properties which allow you to check whether the log is configured at a given level before calling the corresponding log method. This can save on unnecessary calls to resource files and string formatting etc if the log isn’t capturing certain levels of data.

bool IsDebug { get; }
bool IsInfo { get; }
bool IsWarn { get; }
bool IsError { get; }
bool IsFatal { get; }

The ValidationListener in the Using Logging in Custom Listeners post would be updated as follows:

using MicroLite.Logging;

public class ValidationListener : MicroLite.Listeners.Listener
{
    private static ILog log = LogManager.GetCurrentClassLog();

    protected override BeforeInsert(object instance)
    {
        // This ensures that we only pay the cost of calling
        // .GetType() if we want to log the value anyway.
        if (log.IsDebug)
        {
            log.Debug(
                "Validating an {0} before insert",
                instance.GetType().Name);
        }

        ValidateObject(instance);
    }

    protected override BeforeUpdate(object instance)
    {
        // This ensures that we only pay the cost of calling
        // .GetType() if we want to log the value anyway.
        if (log.IsDebug)
        {
            log.Debug(
                "Validating an {0} before update",
                instance.GetType().Name);
        }

        ValidateObject(instance);
    }

    private void ValidateObject(object instance)
    {
        var warnings = Validator.GetWarnings(instance);
        var errors = Validator.GetErrors(instance);

        foreach (var warning in warnings)
        {
            if (log.IsWarn) { log.Warn(warning); }
        }

        foreach (var error in errors)
        {
            if (log.IsError) { log.Error(error); }
        }

        if (warnings.Count > 0 || errors.Count > 0)
        {
            throw new ValidationException(warnings, errors);
        }
    }
}

Changes to Logging extensions

The MicroLite.Extensions.Log4Net and MicroLite.Extensions.NLog packages have been replaced by MicroLite.Logging.Log4Net and MicroLite.Logging.NLog.

The old packages have been updated to simply act as a redirect to the new ones so you shouldn’t be impacted by this change at all. However it would be worth while uninstalling the old package and installing the new one manually.

The old packages are still listed (but hidden) so things like Package Restore will still work for you.

The main reason for doing this is to get the logging extensions on a cleaner versioning and release process – the early builds were done before I fully understood the way nuget package dependencies worked therefore we ended up with new builds when we didn’t actually need them.

In future, the packages will only be updated if there are breaking changes in MicroLite, Log4Net or NLog that they need to cater for.

Using Logging in Custom Listeners

We covered recently Extending MicroLite with custom Listeners and as a result you may find that you create a Listener that could be distributed for used by other people.

Lets for example say that you created a Listener to perform validation and wanted to log any validation warnings and errors, you can utilise the logging abstraction provided by MicroLite.

Now since we cannot guarantee that there will be a logger present, we don’t want to call log directly as it may be null. There are a set of extension methods available which will check whether a logger is in use and you should call them instead. They are in the MicroLite.Logging namespace so if you add a using for MicroLite.Logging instead of fully qualifying the ILog and LogManager class they will show up in intellisense.

This example is to show you how you could log the warnings and errors (Validator and ValidationException are fictional classes used as an example).

Firstly, add a static field to the class which is of type MicroLite.Logging.ILog which is resolved by the MicroLite LogManager class.

Then call the TryLog.. extension methods so that we don’t have to check explicitly whether we have a logger or not.

using MicroLite.Logging;

public class ValidationListener : MicroLite.Listeners.Listener
{
    private static ILog log = LogManager.GetCurrentClassLog();

    protected override BeforeInsert(object instance)
    {
        log.TryLogDebug("Validating an object before insert");
        ValidateObject(instance);
    }

    protected override BeforeUpdate(object instance)
    {
        log.TryLogDebug("Validating an object before update");
        ValidateObject(instance);
    }

    private void ValidateObject(object instance)
    {
        var warnings = Validator.GetWarnings(instance);
        var errors = Validator.GetErrors(instance);

        foreach (var warning in warnings)
        {
            log.TryLogWarn(warning);
        }

        foreach (var error in errors)
        {
            log.TryLogError(error);
        }

        if (warnings.Count > 0 || errors.Count > 0)
        {
            throw new ValidationException(warnings, errors);
        }
    }
}

This article was written for version 2.1.0 of the MicroLite framework.

Creating a custom logging extension

As you will have seen in the Logging post, there are currently extension packages available to enable MicroLite to write to log4net or NLog. While these are the most popular logging frameworks, there are others available or you may use an in house logging solution.

If you do not use log4net or NLog, you can easily create your own extension for logging.

Start by creating a new class library project, the assembly naming convention used is MicroLite.Extensions.X where X is the functionality provider e.g. MicroLite.Extensions.Log4Net – log4net implementation of logging.

Then create a class which implements the MicroLite.Logging.ILog interface, the log4net and NLog extensions simply wrap the logger in that framework and forward the method call. However, depending on the logging framework you are using, you may have to do more than that.

internal class LogAdapter : MicroLite.Logging.ILog
{
    // implement interface as required.
    ...
}

Then, we need to add a class called ConfigurationExtensions in the MicroLite.Configuration namespace which extends IConfigureExtensions. The method name should describe the extension (e.g. WithLog4Net). The reason to put the class in the MicroLite.Configuration namespace is to make the extension visible without having to specify an additional namespace since the application startup will already be referencing MicroLite.Configuration.

namespace MicroLite.Configuration
{
    public static class ConfigurationExtensions
    {
          public static IConfigureExtensions WithX(this IConfigureExtensions configureExtensions)
          {
               // Implement logic here.

               return configureExtensions;
          }
     }
}

The IConfigureExtensions interface exposes a number of methods to integrate with MicroLite, however we are only interested in SetLogResolver at this time. As you can see from the method signature, it simply requires a delegate which can resolve a string into an implementation of the MicroLite.Logging.ILog interface.

void SetLogResolver(Func<string, ILog> logResolver);

We then need to call the SetLogResolver method, this is an example of how the log4net implementation works:

public static IConfigureExtensions WithLog4Net(this IConfigureExtensions configureExtensions)
{
    configureExtensions.SetLogResolver((string name) =>
        {
            var logger = log4net.LogManager.GetLogger(name);

            return new LogAdapter(logger);
        });

    return configureExtensions;
}

The code for the extensions is all open source and on GitHub if you want to see the full implementation details.

This article was written for version 2.0.0 of the MicroLite framework.