Ads by Lake Quincy Media
Gibraltar - Learn about the best analysis tool for NLog

Archive for the ‘NLog v2’ Category

NLog nightly builds are now available

Nightly builds of NLog are now available on CodePlex. They are generated every night around 1AM PST which is 10AM in most of Europe. Builds are generated using CruiseControl.NET which is pretty awesome piece of software – except the fact that they don’t use NLog yet :) .

After each build, binaries are automatically pushed to CodePlex servers using CodePlex Web Services API. Once the release is uploaded, a direct link to it is also placed on the Download page. I still haven’t figured out how to delete older releases automatically because CodePlex API does not support this, so I’ll be doing that manually for now.

You can subscribe to the RSS feed at http://feeds.feedburner.com/nlogreleases to be notified about new builds.

Ads by Lake Quincy Media

Asynchronous Makeover – NLog Edition

I just checked in code to enable asynchronous execution of wrappers in NLog. This is the last big architectural change before bug-fixing and stabilization for 2.0 release.

New features

  • Exceptions thrown from wrappers such as AsyncTargetWrapper, BufferingTargetWrapper, AspNetBufferingTargetWrapper are now handled as opposed to being swallowed.
  • LogReceiverWebServiceTarget now handles asynchronous exceptions.
  • Exceptions from batch-aware targets are now handled for each item separately. It means that if you write 100 messages to a BufferedWrapper which wraps File target and one of those file writes fails, you will get 99 successes and 1 failure as opposed to one massive failure which no wrapper can reasonably handle. The same thing applies to email, etc.

There has been a small change to the design described in the last post. Asynchronous continuations are represented as delegates instead of interface. It makes makes it much easier to use ad-hoc continuations in code without sacrificing composability.

Downloads

The source code is available in GitHub repository. I’ve published a private build from the master branch: http://nlog-project.org/public/asyncpreview1/.

I would appreciate people giving this build a spin and reporting any anomalies. Given the scope of changes and lack of unit tests in some areas I expect many things to be broken, so any usage in production is highly discouraged.

Asynchronous logging in NLog 2.0

NLog 1.0 supports asynchronous logging, but there is no good support for asynchronous exception handling. This is because wrappers targets are not capable of receiving exceptions which are raised on other threads.

Since NLog 2.0 is going to support Silverlight where entire networking stack is completely asynchronous, it is critical to enable wrappers for those scenarios. Without it some important wrapper-based features, such as load balancing or failover would not work properly.

This post will present new APIs to support asynchronous logging features that are coming in the next release of NLog.

Exception Handling in NLog 1.0

NLog 1.0 uses very simple, synchronous exception handling pattern:

try
{
    // do something
}
catch (Exception ex)
{
    // handle exception
}

The problem arises if the code block inside try { } clause performs an asynchronous operation such as network call which may result in an exception, as in the following example:

try
{
    WebClient client = new WebClient();
    client.DownloadStringCompleted += (sender, e) =>
    {
        // this event will be raised asynchronously
        // on another thread, long after try/catch block completes

        // any exceptions raised here will not be handled by the catch {} block below
    }

    client.DownloadStringAsync(new Uri("http://example.com"));
}
catch (Exception ex)
{
    // handle exception
}

NLog cannot handle exceptions in such cases, since the original stack frame is gone, so it just swallows exceptions raised asynchronously and logs them to the internal log. Not catching exceptions on background threads would be fatal and might result in application termination.

You can probably see why swallowing exceptions prevents wrappers, such as RetryingWrapper from working. If you write declare the following wrappers in your configuration file, the outermost wrapper will never implement any retry logic, since AsyncWrapper will never pass any asynchronous exceptions to RetryingWrapper.

<target type="RetryingWrapper" ...>
   <target type="AsyncWrapper" ...>
      <target type="File" ...>
   </target>
</target>

Asynchronous Exception Handling in NLog 2.0

In order to implement proper asynchronous exception handling we need to let asynchronous methods know what to do in case of success and failure. This is typically done through continuation functions. There are many ways to represent continuation information, I’ve decided to represent it as an interface with two methods:

public interface IAsyncContinuation
{
    void OnSuccess();
    void OnException(Exception exception);
}

The Target.Write() API will be refactored to look like this:

public void WriteLogEvent(LogEventInfo logEvent, IAsyncContinuation asyncContinuation)
{
    try
    {
        this.Write(logEvent, asyncContinuation);
    }
    catch (Exception ex)
    {
        asyncContinuation.OnException(ex);
    }
}

protected virtual void Write(LogEventInfo logEvent, IAsyncContinuation asyncContinuation)
{
    try
    {
        this.Write(logEvent);
    }
    catch (Exception ex)
    {
        asyncContinuation.OnException(ex);
        return;
    }

    asyncContinuation.OnSuccess();
}

protected abstract void Write(LogEventInfo logEvent);

As you can see, by default the asynchronous code gets forwarded to the synchronous Write method. This lets us keep the existing extensibility interface for targets. If you want to implement asynchronous target, you need to override both synchronous and asynchronous write methods:

public class MyAsyncTarget : TargetWithLayout
{
    [RequiredParameter]
    public Uri TargetUri { get; set; }

    protected override void Write(LogEventInfo logEvent)
    {
        throw new NotSupportedException("Synchronous write operation is not supported.");
    }

    protected override void Write(LogEventInfo logEvent, IAsyncContinuation asyncContinuation)
    {
        var wc = new WebClient();
        wc.UploadDataCompleted += (sender, e) =>
            {
                wc.Dispose();
                if (e.Error != null)
                {
                    asyncContinuation.OnException(e.Error);
                    return;
                }

                asyncContinuation.OnSuccess();
            };

        byte[] data = Encoding.UTF8.GetBytes(this.Layout.GetFormattedMessage(logEvent));
        wc.UploadDataAsync(this.TargetUri, data);
    }
}

Target.Flush() method will be changed in a similar way, except it will be asynchronous only:

public void Flush(IAsyncContinuation asyncContinuation)
{
    try
    {
        this.FlushAsync(asyncContinuation);
    }
    catch (Exception ex)
    {
        asyncContinuation.OnException(ex);
    }
}

protected virtual void FlushAsync(IAsyncContinuation asyncContinuation)
{
    asyncContinuation.OnSuccess();
}

LogManager and LogFactory will also be enhanced with asynchronous Flush() methods. Their synchronous overloads will not be available in Silverlight, since there is no way to wait on a potential network call without causing a deadlock:

public class LogFactory
{
#if !SILVERLIGHT
  void Flush();
  void Flush(TimeSpan timeout);
  void Flush(int timeoutMilliseconds);
#endif

  void Flush(IAsyncContinuation asyncContinuation);
  void Flush(IAsyncContinuation asyncContinuation, TimeSpan timeout);
  void Flush(IAsyncContinuation asyncContinuation, int timeoutMilliseconds);
}

Working with continuations

NLog 2.0 will provide default implementation of continuations creatable through AsyncHelpers.MakeContinuation() factory method:

IAsyncContinuation continuation = AsyncHelpers.MakeContinuation(
    () => { /* code to execute on success */ }
    ex => { /* code to execute on failure */ });

In addition to this I am planning to expose helpers which will make working with and composing continuations easier:

public delegate void AsynchronousAction(IAsyncContinuation asyncContinuation);
public delegate void AsynchronousAction<T>(IAsyncContinuation asyncContinuation, T argument);

public static class AsyncHelpers
{
  public static void RunSequentially<T>(IEnumerable<T> values, IAsyncContinuation asyncContinuation, AsynchronousAction<T> callback);
  public static void RunInParallel<T>(IEnumerable<T> values, IAsyncContinuation asyncContinuation, AsynchronousAction<T> action);
  public static void Repeat(int repeatCount, IAsyncContinuation asyncContinuation, AsynchronousAction action);
  public static IAsyncContinuation FollowedBy(this IAsyncContinuation asyncContinuation, AsynchronousAction action);
  public static IAsyncContinuation WithTimeout(this IAsyncContinuation asyncContinuation, TimeSpan timeout);
  public static void RunSynchronously(AsynchronousAction action);
}

Impact on wrappers

Because of the way the API is designed, the impact on existing targets should be very limited. Unfortunately this does not apply to wrappers, which have to be completely rewritten to be fully asynchronous. Asynchronous code tends to be larger and more difficult to read and follow, as demonstrated in the following example:

For example, the code for retrying wrapper in NLog 1.0 looked like this:

protected internal override void Write(LogEventInfo logEvent)
{
    for (int i = 0; i < RetryCount; ++i)
    {
        try
        {
            if (i > 0)
                InternalLogger.Warn("Retry #{0}", i);
            WrappedTarget.Write(logEvent);
            // success, return
            return;
        }
        catch (Exception ex)
        {
            InternalLogger.Warn("Error while writing to '{0}': {1}", WrappedTarget, ex);
            if (i == RetryCount - 1)
                throw ex;
            System.Threading.Thread.Sleep(RetryDelayMilliseconds);
        }
    }
}

The code for the same operation in NLog 2.0 is much more complex:

protected override void Write(LogEventInfo logEvent, IAsyncContinuation asyncContinuation)
{
    FailureAction failure = null;
    int counter = 0;

    failure = ex =>
        {
            InternalLogger.Warn("Error while writing to '{0}': {1}", this.WrappedTarget, ex);
            int retryNumber = Interlocked.Increment(ref counter);

            // exceeded retry count
            if (retryNumber == this.RetryCount)
            {
                asyncContinuation.OnException(ex);
                return;
            }

            // sleep and try again
            Thread.Sleep(this.RetryDelayMilliseconds);
            InternalLogger.Warn("Retry #{0}", retryNumber);

            this.WrappedTarget.WriteLogEvent(logEvent, AsyncHelpers.MakeContinuation(asyncContinuation.OnSuccess, failure));
        };

    this.WrappedTarget.WriteLogEvent(logEvent, AsyncHelpers.MakeContinuation(asyncContinuation.OnSuccess, failure));
}

Summary

Asynchronous processing is a very difficult matter, and it is very difficult to write correct and robust asynchronous code. I am hoping that proposed APIs and abstraction level are the right ones and will not make the source code too difficult to read and maintain.

Any comments or suggestions are welcome.

NLog 2.0 installer is available for testing

I’ve spent last couple nights working on MSI installer for NLog 2.0 and I have first version of MSI packages ready for testing. They should be reasonably usable – the code passes all unit tests but did not get much integration testing yet.

There are 5 installer packages:

Each package comes with Visual Studio integration, which supports VS2010, VS2008 and VS2005 (completely untested) and should have the same functionality as in NLog 1.0:

  • Code snippets for C# and VB (just type ‘nlogger’ and it will include logger declaration)
  • Item templates for empty, console and typical log file
  • XSD for intellisense

Note that unlike in NLog 1.0, the installer installs those VS items for all users on the machine, not for the user who ran the installer.

Please also note that debug symbols (pdb/mdb), binaries and documentation for some older frameworks, such as Silverlight 2.0 and .NET Framework 2.0 are excluded by default – use custom or full installation to enable them.

Please give the installer a try on as many platform configurations as you can and report success/failure along with your configuration information as comments to this post. I’m particularly interested in testing the following dimensions:

  • various combinations of Visual Studio SKUs installed on the same machine (with or without add-on packages such as Silverlight SDK, Resharper, etc.)
  • different versions of Windows
  • x86 and x64 CPUs
  • non-English versions of Windows
  • non-English versions of Visual Studio

Thanks in advance for all your help.

NLog packaging options

I’m trying to figure out what kind of packaging would be most appropriate for NLog 2.0 and I would like to hear your opinion on this matter.

Given that there are going to be at least 9 supported frameworks (and new ones will likely be added – such as Silverlight for Windows Mobile), putting everything into a single exe/msi is not practical. The size would be huge (about 16 MB in compressed msi/30 MB expanded on disk) and that’s mostly because of API documentation in CHM format which adds around 2MB per framework. The library itself is and will likely remain small and because it’s MSIL it compresses really well.

So here are the options I’m considering (for simplicity I’m omitting the fact that there will be two flavors: Release and Debug and some common packages such as sources):

  1. One package with binaries and documentation for each framework
    1. NLog-2.0-NetFx20.msi / zip
    2. NLog-2.0-NetFx35.msi / zip
    3. NLog-2.0-NetFx40.msi / zip
    4. NLog-2.0-SL2.msi / zip
    5. NLog-2.0-SL3.msi / zip
    6. NLog-2.0-SL4.msi / zip
    7. NLog-2.0-NetCf20.msi / zip
    8. NLog-2.0-NetCf35.msi / zip
    9. NLog-2.0-Mono.msi / zip
  2. One package with binaries and documentation for each version of Visual Studio which would include all the frameworks it supports + separate downloads for individual frameworks:
    1. NLog-2.0-VS2008.msi / zip (would include NetFx20,NetFx35,SL2,SL3,NetCF20,NetCF35)
    2. NLog-2.0-VS2010.msi / zip (would include NetFx20,NetFx35,NetFx40,SL3,SL4)
    3. NLog-2.0-NetFx20.msi / zip
    4. NLog-2.0-NetFx35.msi / zip
    5. NLog-2.0-NetFx40.msi / zip
    6. NLog-2.0-SL2.msi / zip
    7. NLog-2.0-SL3.msi / zip
    8. NLog-2.0-SL4.msi / zip
    9. NLog-2.0-NetCf20.msi / zip
    10. NLog-2.0-NetCf35.msi / zip
    11. NLog-2.0-Mono.msi / zip
  3. One package with binaries and documentation for each family of frameworks:
    1. NLog-2.0-NetFx.msi / zip (would include NetFx20, NetFx35, NetFx40)
    2. NLog-2.0-Silverlight.msi / zip (would include SL2,SL3,SL4)
    3. NLog-2.0-CompactFramework.msi / zip (would include NetCf20, NetCf35)
    4. NLog-2.0-Mono.zip
  4. Only one package with binaries and documentation which would include the most common frameworks only, other frameworks would be available as separate downloads as in option#1
    1. NLog-2.0.msi / zip (would include NetFx35, NetFx40, SL4)
    2. NLog-2.0-NetFx20.msi / zip
    3. NLog-2.0-NetFx35.msi / zip
    4. NLog-2.0-NetFx40.msi / zip
    5. NLog-2.0-SL2.msi / zip
    6. NLog-2.0-SL3.msi / zip
    7. NLog-2.0-SL4.msi / zip
    8. NLog-2.0-NetCf20.msi / zip
    9. NLog-2.0-NetCf35.msi / zip
    10. NLog-2.0-Mono.msi / zip
  5. One big package (msi/zip) with all binaries, but without documentation (about 2.5MB msi, 11MB installed). Documentation would be available online or as a separate download.

Which one of these would you prefer?

NLog 2.0 for Silverlight 4 and .NET Framework 4.0 preview builds

This week .NET Framework 4.0 and Silverlight 4 have been released. I’ve updated NLog 2.0 to support them and published a new build – very experimental – on CodePlex. One of the biggest updates in Silverlight 4 is support for out-of-browser applications with elevated permissions, which means applications that can access the filesystem.

I’ve put together a tiny sample that shows how to use NLog in Silverlight application. Basically since Silverlight does not have a concept of application configuration file you should configure Silverlight at application startup. In my case I’ve added this code in App.xaml.cs:

private void Application_Startup(object sender, StartupEventArgs e)
{
    InitializeNLog();
    this.RootVisual = new MainPage();
}

private void InitializeNLog()
{
    SimpleConfigurator.ConfigureForTargetLogging(
        new FileTarget()
        {
            FileName = "${specialfolder:MyDocuments}/log.${shortdate}.txt",
            Layout = new CsvLayout()
            {
                Columns =
                {
                    new CsvColumn("Time", "${longdate}"),
                    new CsvColumn("Level", "${level}"),
                    new CsvColumn("Lessage", "${message}"),
                    new CsvColumn("Logger", "${logger}"),
                },
            }
        },
        LogLevel.Debug);
}

The application will produce CSV-formatted log file in Documents folder. The name of the file will be log.CURRENTDATE.txt.

Usage of NLog stays unchanged:

public partial class MainPage : UserControl
{
    private static Logger logger = LogManager.GetCurrentClassLogger();

    public MainPage()
    {
        InitializeComponent();

        // log some events
        this.Loaded += (sender, e) => logger.Info("Page loaded");
        this.LayoutUpdated += (sender, e) => logger.Debug("Layout updated");
        this.SizeChanged += (sender, e) => logger.Debug("Size changed to {0}x{1}", e.NewSize.Width, e.NewSize.Height);
        this.KeyDown += (sender, e) => logger.Debug("Key down '{0}'", e.Key);
        this.Unloaded += (sender, e) => logger.Info("Unloaded");
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        logger.Info("Button clicked");
    }
}

Why not give NLog for Silverlight a try? Go to http://nlog.codeplex.com/releases/view/43702 and download preview bits today and report back any issues.

NLog tutorials added to the Wiki

I did a web search today looking for 3rd-party NLog tutorials and I’ve put some of the links on the NLog Wiki. Some of the articles I found are pretty advanced, and they showcase how NLog can be a part of modern application architectures using AOP, dependency injection and other good engineering techniques.

Enjoy.

NLog 2.0 build and release process explained

I’ve spent last couple weeks working on tools to support NLog 2.0 release process. Releasing library such as NLog which targets 8 frameworks is not an easy task and requires some pretty sophisticated automation support.

Each NLog release consists of several files:

  • NLog.dll and NLog.Extended.dll – NLog library
  • NLog.xml and NLog.Extended.xml – API documentation extracted from source code comments
  • NLog.xsd – schema information used to author and validate NLog configuration files
  • NLog.chm – documentation for each platform

Because each supported platform is slightly different (in terms of targets, layout renderers, etc.) a separate version of each artifact must be produced for every build.

Let’s take a look at the process which produces all of these:

image

Step 1: Compilation

At the very beginning, source files are compiled using csc.exe to produce NLog.dll and NLog.xml (additionally NLog.Extended.dll and NLog.Extended.xml for platforms which need it). This is using standard C# compiler functionality and there is nothing really fancy here. Maybe except for the fact that StyleCop is being used to make sure that in-source documentation is consistent and follows some established patterns.

Step 2: API Dump

The next step is to analyze output assemblies using reflection and extract information about targets, layout renderers, etc. that have been compiled in. This is done using DumpApiXml.exe and the result is a file called NLog.api – one for each supported platform. The reason why a dump file is being used is to simplify further processing: documentation, XSD and website generators can rely only on the API file without the need to duplicate (relatively complex) reflection code.

The API dump includes information about each target in a format that’s friendly for XSLT processing and includes pre-calculated pieces of information such as URI slugs, inline XML documentation, etc.

image

Step 3: Conceptual documentation generation

Before NLog.chm can be generated we need to generate conceptual documentation which explains configuration file format for each target, layout renderer, etc. This is all done by MakeApiDoc.exe and uses NLog.api for each platform, generated in the previous step. The result is a bunch of *.aml files which will be used by SHFB later. (Note that MakeApiDoc.exe is not checked in into public NLog repository yet).

Step 4: XSD schema generation

Similar to conceptual documentation, NLog.xsd is also generated from NLog.api – the result is a document with documented XSD types and elements which can be used in Visual Studio to provide Intellisense(tm) for authoring and validating NLog.config. (Note that MakeNLogXsd.exe in the public repository is not up-to-date yet and does not use NLog.api, instead it’s an older version from NLog 1.0 which uses reflection. It will be updated soon.)

Step 5: Website generation

NLog.api will also be used to generate documentation for the website, which includes:

The website will be generated using simple XSLT stylesheet, which is possible thanks to the relatively simple api file format. Since users will not need to generate the website, the tools used here will not be part of the NLog release.

Step 6: Documentation generation

Having assemblies, code comments (generated in step 1) and conceptual documentation (generated in step 3) we can finally launch SHFB to generate documentation each platform. The documentation is produced in two formats:

  • NLog.chm – compiled help file which will be included in the download
  • website files – which you can browse on the web

Step 7: Installer

The last remaining steps in getting NLog ready for release will be packaging. Most likely this is going to be automated using WIX, but I did not get to that part yet.

First preview build of NLog 2.0 is available

Very experimental first build of NLog from 2.0 branch is available for download. It’s not alpha, beta or even gamma, just a preview of upcoming changes for those adventurous enough to try it out. My primary goal for this preview release is making sure there are no unintended breaking changes.

I would greatly appreciate your help in making sure the code which uses NLog 1.x continues to work with NLog 2.0 (in both source and binary-compatibility situations). Please report any issues as comments under this post.

Summary of changes:

  • Entire code base has been ported to C# 3.0, cleaned up and simplified.
  • This release has been compiled against the following frameworks:
    • Silverlight 2.0 and 3.0
    • .NET Framework 2.0
    • .NET Framework 3.5 (client and extended profile)
    • .NET Framework 4.0 Release Candidate (client and extended profile)
    • Mono 2.x
    • .NET Compact Framework 2.0 and 3.5
  • Support for pre-2.0 versions of the .NET Framework and Mono has been removed
  • Logging pipeline and extensibility interface (targets, layout renderers, layouts, filters, etc.) has been completely redesigned for better maintainability
  • Added support for wrapper layout renderers

Please consult breaking change policy for information about expected behavior. I would like to make sure logging APIs and configuration files continue to work without having to modify your code.

Download sources and binaries from http://nlog.codeplex.com/releases/view/41859
Documentation: http://nlog-project.org/doc/2.0/

Support for .NET Framework 4 Client Profile in NLog 2.0

.NET Framework 4 client profile is a subset of .NET framework used for client programming. It’s a smaller download, because it does not have some server-specific assemblies such as System.Web.dll, compilers and design-time components. When your project targets the client profile, VS and the compiler will validate that it can actually run in that environment. Your application cannot (statically) reference any system DLLs which are not available in the client profile or any other dlls which may depend on such system assemblies.

Why am I writing about this and how is it related to NLog?

Historically NLog has been distributed as a single assembly with static references to all the required system assemblies, including ones which will no longer be available in the client profile:

image

As you can see, we have System.Web.dll (because of ASP.NET-specific layout renderers and modules) and System.Messaging.dll (because of MSMQ target) which are from extended profile. If I were to distribute NLog 2.0 with those dependencies, people would not be able to compile their client applications using it- this is bad.

Difficult choice

Since people use NLog for both client-side and server-side deployments (and removing ASP.NET support is not an option) I need to have builds of NLog which support both environments. There are 3 options I’m considering:

  1. Release two versions of NLog.dll – one for .NET 4.0 Extended profile and one for .NET 4.0 Client profile
  2. Remove static dependencies and release single NLog.dll which detects the profile at runtime and invokes System.Web/System.Messaging through reflection.
  3. Release NLog.dll which would be client-only subset and NLog.Extended.dll which would include features specific to extended profile. This would mean that NLog 2.0 for other versions of the framework would also be split into NLog.dll and NLog.Extended.dll

Each option has different pros and cons, which makes this a hard choice:

Option 1 has the following advantages:

  • Single assembly to deploy in all scenarios (now that NLog.ComInterop.dll has been merged with NLog.dll there is truly a singly dll)
  • Relatively simple code – stuff which does not compile on client profile will be simply #ifdef-ed.
  • No dynamic invocation

But there is possible confusion because of existence of two different builds of the same component (with the same name, version numbers and everything else) with slightly different feature set.

Option 2 means single assembly, but potentially slower and harder-to-maintain reflection code

Option 3 is probably the cleanest (there is no conditional compilation and no dynamic invocation), but it means that there will be sometimes 2 assemblies to deal with – in client apps you would reference NLog.dll and in extended profile apps, you can (optionally) use NLog.Extended.dll.

What do you think? Which of those options should I choose? Would you prefer a single assembly over multiple ones? Do you think reflection is a good practice to avoid having to deal with client profile limitations?

Please post your thought in comments.