Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

Logging display and WPF

, 12 Oct 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Logging display and WPF

A question appeared over on the CodeProject forums today about binding the output from log4net into WPF. The question asked was:

“I'm trying to use Log4net to log messages within my application. I'm adding a WPF window and want to stream the messages to the window. Log4net provides a TextWriterAppender that takes a StringWriter and writes logged events to the StringWriter, flushing it after each event. I want to simply connect the output of the StringWriter as the Text property on a TextBox. When I started this, it seemed simple and obvious – now I'm less sure. Ideally, I would simply like to bind the StringWriter to the TextBox, but haven't found the incantation.

The basic problem is that the StringWriter doesn't provide something like the INotifyPropertyChanged event to trigger code output a new log message (unless there is something behind the scenes I haven't found).

I've see many examples of binding, all of which seem to presume that I have control over the writer itself. Am I missing something simple (I hope), or is this really not that straightforward.”

This is a very good question, so I thought I'd knock together a quick sample application to demonstrate how to do this. The first thing to remember is that log4net allows you to create your own appenders and use them in your application. The second thing to remember is that you need to hook INotifyPropertyChanged into the mechanism. To that end, I created the following appender:

namespace log4netSample.Logging
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using log4net.Appender;
  using System.ComponentModel;
  using System.IO;
  using System.Globalization;
  using log4net;
  using log4net.Core;

  /// <span class="code-SummaryComment"><summary>
</span>  /// The appender we are going to bind to for our logging.
  /// <span class="code-SummaryComment"></summary>
</span>  public class NotifyAppender : AppenderSkeleton, INotifyPropertyChanged
  {
    #region Members and events
    private static string _notification;
    private event PropertyChangedEventHandler _propertyChanged;

    public event PropertyChangedEventHandler PropertyChanged
    {
      add { _propertyChanged += value; }
      remove { _propertyChanged -= value; }
    }
    #endregion

    /// <span class="code-SummaryComment"><summary>
</span>    /// Get or set the notification message.
    /// <span class="code-SummaryComment"></summary>
</span>    public string Notification
    {
      get
      {
        return _notification; ;
      }
      set
      {
        if (_notification != value)
        {
          _notification = value;
          OnChange();
        }
      }
    }

    /// <span class="code-SummaryComment"><summary>
</span>    /// Raise the change notification.
    /// <span class="code-SummaryComment"></summary>
</span>    private void OnChange()
    {
      PropertyChangedEventHandler handler = _propertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(string.Empty));
      }
    }

    /// <span class="code-SummaryComment"><summary>
</span>    /// Get a reference to the log instance.
    /// <span class="code-SummaryComment"></summary>
</span>    public NotifyAppender Appender
    {
      get
      {
        return Log.Appender;
      }

    }

    /// <span class="code-SummaryComment"><summary>
</span>    /// Append the log information to the notification.
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="loggingEvent">The log event.</param>
</span>    protected override void Append(LoggingEvent loggingEvent)
    {
      StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
      Layout.Format(writer, loggingEvent);
      Notification += writer.ToString();
    }
  }
}

Whenever a new message is appended, the Notification is updated and the PropertyChangedEventHandler is called to notify the calling application that the binding has been updated. In order to use this appender, you need to hook it into your configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net"
      type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <appSettings>
    <add key="log4net.Internal.Debug" value="false"/>
  </appSettings>
  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <add name="textWriterTraceListener"
             type="System.Diagnostics.TextWriterTraceListener"
             initializeData="C:\log4net_internal.log"/>
      </listeners>
    </trace>
  </system.diagnostics>
  <log4net>
    <appender name="NotifyAppender" type="log4netSample.Logging.NotifyAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <param name="Header" value="[Header]\r\n" />
        <param name="Footer" value="[Footer]\r\n" />
        <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n" />
      </layout>
    </appender>

    <root>
      <level value="ALL" />
      <appender-ref ref="NotifyAppender" />
    </root>
  </log4net>
</configuration>

Note that you might want to add the following line into your AssemblyInfo.cs file:

[assembly: log4net.Config.XmlConfigurator(Watch=true)]

I find the following class really helpful when logging:

namespace log4netSample.Logging
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using log4net;
  using log4net.Config;
  using log4net.Appender;
  using log4net.Repository.Hierarchy;

  public enum LogLevel
  {
    Debug = 0,
    Error = 1,
    Fatal = 2,
    Info = 3,
    Warning = 4
  }
  /// <span class="code-SummaryComment"><summary>
</span>  /// Write out messages using the logging provider.
  /// <span class="code-SummaryComment"></summary>
</span>  public static class Log
  {
    #region Members
    private static readonly ILog _logger = LogManager.GetLogger(typeof(Log));
    private static Dictionary<LogLevel, Action<string>>

It’s a simple matter then to do something like:

Log.Write(LogLevel.Info, “This is my message”);

If you download the attached sample, you'll get to see the whole application running in all its glory, and you can see how updating the log results in the output being updated. Don't forget to rename the .doc file to .zip when you save it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Pete O'Hanlon
CEO
United Kingdom United Kingdom
A developer for over 30 years, I've been lucky enough to write articles and applications for Code Project as well as the Intel Ultimate Coder - Going Perceptual challenge. I live in the North East of England with 2 wonderful daughters and a wonderful wife.
 
I am not the Stig, but I do wish I had Lotus Tuned Suspension.
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralThank you PinmemberOceanator Framewurk16-Dec-14 11:20 
BugDownload not working PinprofessionalMarco Bertschi14-Jan-14 2:11 
GeneralRe: Download not working PinprotectorPete O'Hanlon14-Jan-14 2:41 
GeneralRe: Download not working PinprofessionalMarco Bertschi14-Jan-14 3:09 
QuestionExactly what I need PinprofessionalMarco Bertschi14-Jan-14 2:09 
GeneralMy vote of 5 PinprofessionalEdo Tzumer20-May-13 4:12 
QuestionLogging from a different thread PinmemberAdrian Arsene28-Oct-12 3:31 
AnswerRe: Logging from a different thread PinprotectorPete O'Hanlon29-Oct-12 8:51 
GeneralRe: Logging from a different thread PinmemberAdrian Arsene30-Oct-12 22:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150123.1 | Last Updated 13 Oct 2009
Article Copyright 2009 by Pete O'Hanlon
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid