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

Archive for the ‘News’ Category

Intellisense for NLog configuration files

I have recently updated tools to build NLog.xsd which are needed to Intellisense in Visual Studio. Instead of one, in NLog 2.0 there will be multiple schema files – one for each framework plus a unified schema for all frameworks.

File Name XML Namespace Frameworks
NLog.xsd http://www.nlog-project.org/schemas/NLog.xsd (all frameworks)
NLog.Mono2.xsd http://www.nlog-project.org/schemas/NLog.mono2.xsd Mono 2.x
NLog.NetCf20.xsd http://www.nlog-project.org/schemas/NLog.netcf20.xsd .NET Compact Framework 2.0
NLog.NetCf35.xsd http://www.nlog-project.org/schemas/NLog.netcf35.xsd .NET Compact Framework 3.5
NLog.NetFx20.xsd http://www.nlog-project.org/schemas/NLog.netfx20.xsd .NET Framework 2.0
NLog.NetFx35.xsd http://www.nlog-project.org/schemas/NLog.netfx35.xsd .NET Framework 3.5
NLog.NetFx40.xsd http://www.nlog-project.org/schemas/NLog.netfx40.xsd .NET Framework 4.0
NLog.SL2.xsd http://www.nlog-project.org/schemas/NLog.sl2.xsd Silverlight 2.0
NLog.SL3.xsd http://www.nlog-project.org/schemas/NLog.sl3.xsd Silverlight 3.0
NLog.SL4.xsd http://www.nlog-project.org/schemas/NLog.sl4.xsd Silverlight 4.0

The idea is that each XSD file only contains items (targets, layouts, filters, etc.) supported by a particular framework and unified schema supports all the targets supported by at least one framework. Because of that Intellisense will provide smart editing help and validation that’s specific to the target framework.

Intellisense In Action

When you add NLog.config to your project using Add Item, it will be using a unified schema (so you will see both Silverlight-specific and .NET Framework specific targets there)

image

When you change the XML to a particular framework – for example Silverlight 2.0, you will immediately see that File target is not supported on that platform and XML editor will highlight the place where the error occurs.

image

This also works for individual properties. For example, LogReceiverService target does not support certain properties on .NET Compact Framework 3.5 (because of lack of WCF). Sure enough, when you use .NET CF-specific schema those errors will be highlighted.

image

XSD schemas also provide help when editing NLog.config files:

image

Customizing XSD Schemas

Starting with NLog 2.0 it is also easy to customize NLog.xsd, which can be useful if your organization uses private extensions to NLog. Let’s say you have created your NLog extensions and put them in SampleExtensions.dll. In order to generate customized NLog.xsd, you need to follow this simple process:

The first step is to download and unpack NLog sources (from GitHub or zip package) and build everything by running:

build.cmd build xsd

from command line. This will build NLog and the tools necessary to customize XSD files. First tool we’ll be using is called DumpApiXml, which analyzes a DLL and generates API file from it as described here. We must run it on our extensions assembly and pass it directory (or directories) where all reference assemblies are located.

<nlog-dir>\tools\DumpApiXml\bin\Debug\DumpApiXml.exe –assembly  <path>\SampleExtensions.dll  -ref "D:\Work\NLog\build\bin\Debug\.NET Framework 4.0" –output <path>\SampleExtensions.api

Once we have the SampleExtensions.api project, we need to convert it to XSD using MakeNLogXSD. It accepts multiple *.api files and can produce XSD files with custom namespaces:

<nlog-dir>\tools\MakeNLogXSD\bin\Debug\MakeNLogXSD.exe -api "<nlog-dir>\build\bin\Debug\.NET Framework 4.0\API\NLog.api" –api <path>\SampleExtensions.api -xmlns http://mycompany.com/NLog.xsd –out <path>\MyNLog.xsd

The command will produce MyNLog.xsd which will use http://mycompany.com/NLog.xsd schema. You can now install the schema in Visual Studio (by dropping it in "%ProgramFiles%\Microsoft Visual Studio 9.0\Xml\Schemas" directory) and you should be able to enjoy Intellisense and validation against your custom schema:

image

Ads by Lake Quincy Media

NLog 2.0: Small API reorganizations and breaking changes

I just checked a set of API changes, which may break code which uses recent nightly builds from NLog 2.0 branch. If you are upgrading to the latest build (2010.06.11.01 or newer) you may need to update your code:

  • NLog.Targets.Compound namespace was removed and classes were merged into NLog.Targets.Wrappers namespace. In NLog 2.0 there is no distinction between wrappers and compound targets – they will be collectively referred to as wrappers.
  • NLog.Contexts namespace was removed and classes were merged into NLog namespace (this is actually the situation we had in NLog 1.0, so this change is really undoing previous breaking change which was unnecessary)

The changes are consistent with general breaking change policy and should not impact people only using file-based configuration and simple logging APIs.

New, smaller NLog installer is available

I’ve just checked in code changes to produce much smaller installer packages for NLog 2.0. Until now, the combined installer package for all framework was 20.8MB, after the change it is less than 6 MB. What made the installer huge was not the size of NLog.dll (it is are between 200KB and 300KB depending on the platform), but the documentation: 9 chm files, each about 2MB. it was clearly a big waste, given that the documentation for most platforms is almost the same.

The new installer includes single documentation file generated from a special build of NLog with DOCUMENTATION flag turned on. The combined assembly includes superset of all APIs available for all platforms – the produced assembly won’t necessarily run, but is good enough to generate documentation. The same technique was used in NLog 1.0 timeframe – I originally thought it would not work for 2.0 given large differences between .NET and Silverlight build, but now I think it is a reasonable compromise between the size and accuracy of the doc file.

The new suite of installers should show up on CodePlex in the next couple hours.

NLog 2.0 documentation is available on the wiki

Documentation for NLog 2.0 is now available on the Wiki. It includes reference documents generated automatically from code as well as hand-written documents and tutorials, some of which have been migrated from WordPress pages to the Wiki format.

Documentation for targets, layouts, layout renderers and filters has been enhanced to include usage examples which you can paste into your configuration code. Because of the new format, it is possible to enhance reference documents with user-created content, such as code or configuration samples. See documentation for the File target for an example.

Note that it is not possible to edit certain parts of automatically generated pages. Things like parameter list, supported platforms and usage example do not show up in Wiki source. Instead you will see a special tag which looks like: <generatedDoc id=’File_target’ hash=’db5419b5ab45ec8defc51cb5dabeaaf4′ />, and causes generated doc to be inserted. The tags and contents are automatically updated whenever I re-upload documentation.

Now that every piece of documentation is in Wiki format, I’m looking for volunteers to help maintain and enhance it, so if you like technical writing and want to own part of the documentation, please let me know.

Please report any issues you find with the Wiki as comments here, or (even better), register for an account and fix those issues yourself.

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.

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.

NLog project metrics on Ohloh.net

Today I came by Ohloh.net which I found to be very interesting. It provides detailed statistics about open source projects. NLog was already registered by some good soul, but was pointing at the old source repository on sourceforge.net, so I updated it to use Github repo and recomputed the stats.

The statistics are generally pretty interesting, and seem pretty accurate – but there are also some entertaining ones, like the estimation of the cost it would take to recreate such a project from scratch (obviously in corporate environment):

image

There are many more interesting statistics, so go and check them out.

I’ve also put I USE IT button on top of this page. You can add NLog to your tool stack on Ohloh by clicking on it, which will count in project statistics.

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?