Log4Net with WCF hosted in a Windows Service

Let's see how to get logging working with a WCF service hosted in a windows service.

When I tried it at first, It didn't work on the service, but it worked perfectly fine on the web application (MVC4).

Instead of using Log4Net directly, I have used Commons.Logging. I'm just gonna quote how awesome it is here. And I will highlight why I think it's awesome.

When writing a library it is very useful to log information. However there are many logging implementations out there, and a library cannot impose the use of a particular one on the overall application that the library is a part of.


The Logging package is an ultra-thin bridge between different logging implementations. A library that uses the commons-logging API can be used with any logging implementation at runtime. Commons-logging comes with support for a number of popular logging implementations, and writing adapters for others is a reasonably simple task.


Applications (rather than libraries) may also choose to use commons-logging. While logging-implementation independence is not as important for applications as it is for libraries, using commons-logging does allow the application to change to a different logging implementation without recompiling code.

As you can see, there are benefits. Who'd wanna recompile the code when they want to say switch to NLog or something else?

First things first, You might want to keep things in control even if there's a super logging library out there.
So, in my case, I just abstracted out a log manager in my application framework.

using System;
using Common.Logging;

namespace Core.Common.Logging
{ 
    public sealed class SystemLogManager
    {
        public static ILog GetLogger(Type type)
        {
            return global::Common.Logging.LogManager.GetLogger(type);
        }

        public static ILog GetLogger<T>()
        {
            return global::Common.Logging.LogManager.GetLogger<T>();
        }

        public static ILog GetLogger(string name)
        {
            return global::Common.Logging.LogManager.GetLogger(name);
        }

        public static ILog GetLogger()
        {
            return global::Common.Logging.LogManager.GetCurrentClassLogger();
        }
    }
}

Then I can use this class like this.

protected static ILog Log = SystemLogManager.GetLogger();

public void LogTest()
{
   Log.Trace(m => m("Testing 1 2 3")); 
}

Now, we move onto the windows service which hosts our WCF services.

I'm gonna assume you already have a working win service ready but if you don't, just add a new service project to the solution. Just follow this to get an idea on how to create a service. WCF-Hosting-with-Windows-Service

Basically, you need to open your WCF service host in the OnStart() method. and close the host on OnStop(). And configure the App.Config file with your bindings and endpoints and behaviors and extensions and whatnot.

 Now, logging.

You must reference the Common.Logging and log4net and Common.Logging.Log4Net to this service project.

Then you need to modify the App.Config file.

1. Add config sections for Common.Logging and Log4Net.


<configSections>
    <!-- Common.Logging config -->
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
    </sectionGroup>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
2. Define the factory adapter for the common.logging library.


<!-- Common.Logging config -->
  <common>
    <logging>
      <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net">
        <arg key="configType" value="INLINE" />
      </factoryAdapter>
    </logging>
  </common>
3. Define the log4net configuration.
You can do this in a separate file or just do it inline. (like me)


<!-- Log4Net Inline config -->
  <log4net>
    <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender,log4net">
      <param name="File" value="Logs.txt" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock,log4net" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="2" />
      <maximumFileSize value="1MB" />
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="LogFileAppender" />
    </root>
  </log4net>

4. If you want to activate log4net internal debugging, then add this to your <appSettings> section
the next step defines how to name the file for internal debugging.

<add key="log4net.Internal.Debug" value="true" />

5. Active WCF logging/tracing 
Just add these sections to your app.config and you're good to go.


 <!-- Diagnostics config -->
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="xml" type="System.Diagnostics.XmlWriterTraceListener" initializeData="WCFTraces.svclog" />
    </sharedListeners>
    <trace autoflush="true">
      <listeners>
        <add initializeData="log4net.txt" type="System.Diagnostics.TextWriterTraceListener" name="textWriterTraceListener">
          <filter type="" />
        </add>
      </listeners>
    </trace>
  </system.diagnostics>

the System.ServiceModel will log the WCF transport level messages.
the System.ServiceModel.MessageLogging will log the contents of the WCF messages. (actual data)

6. Configure WCF logging.


 <!-- WCF Services config -->
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    <diagnostics>
      <messageLogging logMessagesAtTransportLevel="true" logMessagesAtServiceLevel="true" logMalformedMessages="true" logEntireMessage="true" maxSizeOfMessageToLog="65535000" maxMessagesToLog="500" />
      <endToEndTracing propagateActivity="true" activityTracing="true" messageFlowTracing="true" />
    </diagnostics>

    <!-- Services -->
    <services> </services>
 </system.serviceModel>

Now run the service and you should have a Logs.txt inside the bin/ folder



For my reference:

http://haacked.com/archive/2006/09/27/Log4Net_Troubleshooting.aspx/
http://stackoverflow.com/questions/16398680/unable-to-get-log4net-working-with-net-windows-service
https://github.com/net-commons/common-logging/downloads
http://netcommon.sourceforge.net/downloads.html
http://netcommon.sourceforge.net/docs/2.1.0/reference/html/ch01.html#logging-adapters-log4net
http://msdn.microsoft.com/en-us/library/sd8zc8ha(v=vs.110).aspx
http://geekswithblogs.net/tmurphy/archive/2011/08/15/sending-large-arrays-with-wcf.aspx
http://msdn.microsoft.com/en-us/library/ms733069(v=vs.110).aspx
http://www.codeproject.com/Articles/653493/WCF-Hosting-with-Windows-Service
http://blogs.msdn.com/b/carlosfigueira/archive/2011/08/02/wcf-extensibility-system-diagnostic-tracing.aspx
http://stackoverflow.com/questions/2802590/how-to-intercept-raw-soap-request-response-data-from-wcf-client
http://blogs.msdn.com/b/carlosfigueira/archive/2011/08/02/wcf-extensibility-system-diagnostic-tracing.aspx

Popular posts from this blog

Print a receipt using a Thermal Printer with C#.NET

Automatic redirect upon session timeout using ASP.NET MVC and Javascript

Complex Master-Detail Form using Knockout.js and ASP.NET MVC