Friday, February 28, 2014

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

Thursday, February 20, 2014

Pagination with Knockout & JQuery

Oftern times, when we need to render large data sets on a web page, we must use pagination or something similar (think of twitter or facebook - load when scrolled down). Today, I'm going to show you how to create pagination with JQuery, Knockout.JS and a pretty cool jquery plugin, "Simple Pagination".

download the "Simple Pagination" JQuery plugin here. http://th3silverlining.com/2010/04/15/pajination-a-jquery-pagination-plugin/

Assume you have an API which return data something like this (orders in my case)

{
    "Items": [
      {
          "ID": 12, 
          "Description": "New pair of shoes", 
          "InvoiceDate": "2012-08-16T00:00:00" 
      },
      {
          "ID": 13, 
          "Description": "One bottle of Juise", 
          "InvoiceDate": "2012-09-16T00:00:00" 
      },
      [Upto 10 Items]
    ],
    "TotalCount": 38,
    "CurrentPage": 1,
    "PageSize": 10,
    "PageCount": 4
}

I have a ViewModel and a Model to match these data. Something like this... (javascript)

(function (itb) {
    var OrdersModel = function (id,desc,idate) {

        var self = this;

        self.ID = ko.observable(id);
        self.Description = ko.observable(desc);
        self.InvoiceDate = ko.observable(idate);
    };
    itb.OrdersModel = OrdersModel;
}(window.AwesomeSystem));

and a ViewModel for "business logic". Code is self explanatory, if don't understand, please mention in comments, I will try my best to explain.

(function (itb) {
    var OrdersViewModel = function () {

        var self = this;

        self.currentPage = ko.observable(1);
        self.pageCount = ko.observable(1);
        self.pageSize = ko.observable(10);
        self.totalCount = ko.observable(1);

        self.orders = ko.observableArray();

        self.loadSuccess = function (data) {

            // Clear the orders array
            self.orders.removeAll();

            // Set the pagining related data from JSON
            self.currentPage(data.CurrentPage);
            self.pageCount(data.PageCount);
            self.pageSize(data.pageSize);
            self.totalCount(data.TotalCount);

            // Loop through items in JSON, 
            // Create new order model and push it into the Items array
            // NOTE: you can use array map utilities in knockout too
            for (var i = 0; i < data.Items.length; i++) {
                var cu = data.Items[i];

                var newItem = new AwesomeSystem.OrdersModel(cu.ID, cu.Description, cu.InvoiceDate);

                self.orders.push(newItem);
            }

            // Lets create pagination for this data
            self.createPagination();
        };

        self.loadFailed = function (result) {
            alert("load failed");
        };

        self.initialize = function () {
            self.getOrdersAJAX();
        };

        // my api urs for paged data looks like
        // http://mysite/api/orders/{pageNo}/{pageSize}
        self.getOrdersAJAX = function (pageNo, pageSize) {
            var url = 'api/orders/';

            if (pageNo > 1)
                url += pageNo + "/" + pageSize;

            $.get(url, null, self.loadSuccess, self.loadFailed);
        };
        // load the given page 
        self.loadPage = function (pageNo, event) {
            console.log(pageNo);
            self.getOrdersAJAX(pageNo, self.pageSize());
        };

        self.createPagination = function () {
            // pagination source:  http://th3silverlining.com/2010/04/15/pajination-a-jquery-pagination-plugin/
            $("#paginationHolder").pagination({
                pages: self.pageCount(),
                itemsOnPage: self.pageSize(),
                currentPage: self.currentPage(),
                cssStyle: 'pagination',
                onPageClick: self.loadPage // fires when a page is clicked 
            });

        };

        self.initialize();
    };
    itb.OrdersViewModel = OrdersViewModel;
}(window.AwesomeSystem))

And, we have the Markup like this.

<table class="table table-bordered table-striped table-hover">
        <thead>
            <tr>
                <th>Description</th>
                <th>Invoice Date</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: orders">

            <tr>
                <td>
                    <span data-bind="text: Description"></span>
                </td>
                <td>
                    <span data-bind="text: InvoiceDate"></span>
                </td>  
            </tr>

        </tbody>

    </table>
    <!-- // Table END -->
   <ul id="paginationHolder"></ul>

Lastly apply the bindings.

<script src="~/Scripts/plugins/jquery/jquery.simplePagination.js"></script>
<script>
    var viewModel = new AwesomeSystem.OrdersViewModel();
    ko.applyBindings(viewModel);
</script>

Voila!