Support for .NET Framework 4 Client Profile in NLog 2.0

March 4th, 2010

.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.

Important NLog 2.0 milestone reached

February 20th, 2010

Today marks the very important milestone of NLog 2.0 development – I now have fully automated set of NLog unit tests running on all supported platforms.

Why is this such a big deal? In NLog 1.0 timeframe I was not able to run automated unit tests in some environments (such as .NET Compact Framework which did not support NUnit). Because of that I had to resort to ad-hoc testing which unfortunately missed some pretty big bugs.

The .NET ecosystem has grown significantly in the last couple years, and because NLog 2.0 is going to support at least 5 versions of .NET Framework, 3 versions of Silverlight, 2 versions of .NET Compact Framework and Mono, the ability to run the entire test suite on all frameworks became even more important.

Common test library

Since I wanted to use the same set of unit tests in all supported environments, I’ve decided to switch from NUnit-based tests to MSTest. Main reason was the fact that NUnit is not available for Silverlight and .NET Compact Framework and MSTest is available inside Visual Studio. Switching was relatively painless and in most cases was just a simple search/replace of using statements.

MSTest has the advantage of supporting all versions of .NET and .NET Compact Framework inside Visual Studio and is nicely integrated with the IDE. There are some annoyances, such as pretty complicated debugging experience with mobile devices, but I did not have to write almost any custom code to use it and there is no need for 3rd-party download, which is another plus.

Supporting Silverlight was a bit more tricky, because MSTest does not support it directly, but thankfully there is Silverlight Toolkit on CodePlex which provides required libraries and test runner, which requires just a little bit of custom coding.

I still don’t have automated test coverage in Mono, but I think it should be doable – if you know of some ready-to-use MSTest replacement for Mono (ideally one which works on Unix), please let me know.

Build System

In NLog I’ve switched from NAnt to MSBuild as the build automation platform. MSBuild scripts have the huge advantage of being able to use them in Visual Studio directly while enabling deep customizations. MSBuild is installed as part of .NET Framework so almost everyone should already have it on their machines.

In order to make building and running tests in NLog a bit easier, I’ve created a batch file (build.cmd) which is a wrapper for MSBuild. It lets me select which platforms I want to build and test against and do everything in one shot.

In order to build NLog 2.0 for all supported platforms run:

build.cmd

To build and run tests for all platforms:

build.cmd buildtests runtests

If you want to only target specific frameworks, just put their mnemonics on the command line:

build.cmd netfx20 netfx35client doc

To generate documentation for Silverlight 2.0 and Silverlight 3 use:

build.cmd sl2 sl3 doc

You can produce full release of NLog (binaries and documentation for all platforms):

build.cmd all

There are many more options available, to find out what they are:

build.cmd /?

The working NLog 2.0 is available in Subversion http://svn.nlog-project.org/repos/nlog/branches/NLog2/, but I will be moving it soon to the trunk.

Documentation for NLog 1.0 and 2.0 available online

February 16th, 2010

Documentation for NLog 1.0 is available online at http://nlog-project.org/doc/1.0/

Starting with NLog 2.0 there will be a separate documentation build for each supported framework. They will be all accessible from the following base url:

http://nlog-project.org/doc/2.0/

or you can jump directly to the documentation for your framework of choice:

Note that conceptual documentation (tutorials, configuration information, etc.) is not included yet in NLog 2.0 builds and it will be added later.

Producing such a complex set of documentation was a real challenge. I used excellent Sandcastle and Sandcastle Help File Builder from CodePlex which helped a lot. Getting those tools to produce customized documentation for all NLog 2.x frameworks was a complex task with lots of custom scripting to support the build process.

As always – feedback is more than welcome.

Creating multiple log files from a File target

November 7th, 2009

One of the most of commonly used targets in NLog is the File target. In the simplest case it is used to write entries to a single log file, but it supports many more advanced scenarios.  In this post I’m going to explain how to get NLog to write to multiple log files based on contextual information and how to make it work fast even in complex scenarios.

Read the rest of this entry »

Gibraltar adapter for NLog released

November 1st, 2009

The Gibraltar Adapter for NLog provides log management and analysis for NLog data. With Gibraltar you can easily send logs from your applications to a central location where they can be analyzed in Gibraltar Analyst.

If you are need a powerful log analyzer for NLog that goes beyond simple filtering and grouping and can scale as your application grows, you should check out Gibraltar.

I found this tool to be a really awesome complement to NLog. The guys behind Gibraltar were generous enough to offer me a commission if any NLog users buy their tool. So, check it out, and if you like it and decide to buy it, you’ll be supporting NLog development too, which I’d be grateful for.

Configuring the adapter

Integrating the adapter into your application is trivial and can be done without even having to recompile it. If you want to route all your logs through Gibraltar all you have to do is put the following XML in your NLog.config:

<nlog>
  <extensions>
    <add assembly="Gibraltar.Agent.NLog" />
  </extensions>
  <targets>
    <target name="Gibraltar" xsi:type="Gibraltar" />
  </targets>
  <rules>
    <logger name="*" minlevel="Trace" writeTo="Gibraltar" />
  </rules>
</nlog>

If you prefer not to ship the configuration file, just add this code to your Main() method instead:

NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(NLog.LogLevel.Trace,
    new Gibraltar.Agent.NLog.GibraltarTarget());

NLog 2 backwards compatibility and breaking change policy

October 19th, 2009

I have spent last couple months doing significant refactoring of NLog v2 code base with the goal to improve long-term maintainability, extensibility, usability and testability. During that process I discovered (with great help from FxCop) and decided to fix some design issues and align the codebase with .NET Design Guidelines. Fixing some issues required me to introduce some breaking changes which may impact applications using NLog v1 who want to migrate to NLog v2. This post explains the scope of the breaking changes to date and explains general principles I’m following during NLog 2 development. Read the rest of this entry »

NLog Blog has been launched

October 14th, 2009

NLog now has a blog section where you can track development progress and subscribe to RSS feed with news and comments from users.

Some pre-launch news have been retroactively added to mark some important milestones of NLog development.

NLog 1.0 Refresh has been released

September 7th, 2009
This is a re-release of NLog 1.0 extended to support Visual Studio 2008 and 2010 Beta using updated installer.
No source code changes have been made since 1.0 and the version number matches 1.0 to ensure binary compatiblity. Project structure has been cleaned up to make it easier to build without 3rd party tools.
Get the bits from the Download section.

NLog has moved from SourceForge.net to CodePlex.com

September 6th, 2009

NLog has moved from SourceForge.net to CodePlex. Source and binary releases as well as the issue tracker will be hosted on CodePlex. NLog documentation and Subversion repository will continue to be hosted on nlog-project.org. Existing issues reported on sourceforge.net have been migrated to the “Issue Tracker“.

Wrapper Layout Renderers are coming to NLog 2.0

November 22nd, 2008

NLog v2 branch has a new cool feature called Wrapper Layout Renderers which would help me restructure code to make it more maintainable and testable. I’d like to hear your opinion about it.

What are Wrapper Layout Renderers (WLRs)?

Similar to wrapper targets, WLRs can modify output of other layout renderers, by doing textual transformations like:

  • uppercase/lowercase conversion
  • padding, trimming, alignment
  • search/replace
  • elision of excessive text
  • encryption / masking of sensitive information
  • cryptographic transformations (calculating MD5, SHA1 hashes, HMAC checksums)
  • encoding: base64/uuencode, etc.

Note that in NLog v1 many of those concerns are handled by LayoutRenderer itself. Being able to separate them into standalone, extensible classes is very convenient as you can add them separately and all existing layout renderers will be able to benefit from them.
 
Here’s how you use them:

${uppercase:${level}} – will print log level in uppercase
${rot13:${message}} -  will print ROT13 “encrypted” message
 
For configuration file compatibility, you will still be able to use uppercase, lowercase, padding attributes that you know from NLog v1 directly on layout renderers etc.
 
To implement a wrapper, all you have to do is to create a class derived from WrapperLayoutRendererBase that overrides the Transform() method and apply the usual LayoutRenderer attribute.
Here’s the ROT13 wrapper, which implements Caesar’s cipher.
 
[LayoutRenderer("rot13")]
public sealed class Rot13LayoutRendererWrapper: WrapperLayoutRendererBase
{
    protected override string Transform(string text)
    {
        return DecodeRot13(text);
    }
}
 
You can of course define public properties as be able to set their values as with regular layout renderers.
I’m planning to add an additional concept called ambient properties which will help maintain NLog v1 compatibility while being very extensible. Ambient properties are properties that appear as if they were present on all layout renderers and when you actually use them, they add an implicit wrapper. This allows you to write: ${level:uppercase=true:padding=-10} instead of ${padding:padding=-10:${uppercase:${level}}}

Whenever layout parser encounters an unknown property (such as “uppercase” which doesn’t exist on LevelLayoutRenderer anymore) it will look for layout renderers which define [AmbientProperty("UpperCase")] and will instantiate them and use to wrap the original layout renderer:

 [LayoutRenderer("uppercase")]
[AmbientProperty("UpperCase")]
public sealed class UpperCaseLayoutRendererWrapper : WrapperLayoutRendererBase
{
    private bool _upperCase = false;
    public bool UpperCase
    {
        get { return _upperCase; }
        set { _upperCase = value; }
    }
    protected override string Transform(string text)
    {
        return UpperCase ? text.ToUpperInvariant() : text;
    }
}