Introducing LibLog

14 Apr 2015

LibLog (Library Logging) has actually been baking for a few years now, since before 2011 if I recall correctly, and the current version is already at 4.2.1. It's fair to say it's been battle tested at this point.

As a library developer you will often want to your library to support logging. The first and easiest route is to simply take a dependency on a specific logging framework. If you work in a small company / team and ship to yourselves, you can probably get away with this. But things get messy fast when the consumers of your library want to use a different framework and now they have to adapt output of one to the other or somehow configure them both. Then things get real messy when one of the logging frameworks change their signing key in a patch release breaking stuff left, right and centre.

I think at one point I had NLog, Log4Net (2 versions), EntLib logging be pulled into a single project. That's when I had enough.

Another approach is have your library depend on an logging abstraction - Common.Logging. This way at least means your library is independent of a specific logging library. But are you better off? Common.Logging is strongly named and has had dozen or so releases; so what do you think happens when you have 2, 3 or more libraries that depend on different versions of Common.Logging are pulled into the same project? A world of pain with assembly loading errors and tweaking binding redirects.

And the adaptor packages aren't in a great state either: Common Logging Adapters

Some libraries, like NEventStore, define their own logging abstraction. This puts the burden on the consumer to write their own adapter but it's just a single class file without adding scores of references and dependencies to a project. The adapters can put in the project's wiki/docs or a gist for users to copy and paste. Simples.

This is my preferred approach and is the foremost thing LibLog aims to make easier for you. As a source code only package (it will never be a dll) it will add a few public interfaces and classes to your project in {ProjectRootNameSpace}.Logging namespace:

  • A ILogProvider the defines a 3 methods: get a logger and open nested/mapped diagnostic contexts.
  • A single Logger delegate through which all logging functionally can be channelled including structured messages, checking if a log level is enabled, exceptions etc. This is contrast to Common.Logging's ILog which has about 69 members.
  • A static LogProvider for your library to get a logger and for consumers to set the LogProvider.
  • Is PCL compatible with conditional compilation symbol LIBLOG_PORTABLE

The Icing on the Cake

LibLog gives you more though. LibLog will detect the presence of Serilog, NLog, Log4Net, LoupeLogging and EntLib Logging (in that preferential order) in your consumers project and automatically log to them, without the consumer of your library having to do anything at all.

While LibLog uses reflection to do this (and caches the delegates for performance reasons), it turns out these logging frameworks have really stable APIs. An early variation has been in RavenDB for a few years and has yet to break because of new version of NLog or Log4Net. So for me, it feels a safe enough approach. But don't worry if it does break though; when that happens LibLog will catch the problem and just disable logging. In that scenario, if you can't update your lib, or your consumer can't update, the consumer can always fall back on supplying their own {YourLibRootNamespace}.Logging.ILog to get things working again.

You can see where else LibLog is being used in the wild. If you know of any more places, please update the wiki :)

The project is on github, is licensed under MIT and further documentation is in the wiki.

Footnote: It may seem I'm bashing Common.Logging but I'm not - this is a general problem in .NET, particularly when strong naming comes into play. Let's take a moment to lament the lack of structural typing where maybe none of this would be necessary in the first place.

comments powered by Disqus