Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Logging with Microsoft.NET

4.86/5 (5 votes)
24 Apr 2015CPOL3 min read 51.8K   3  
In this article, we will learn how to integrate logging into our applications to keep track of their health.

Introduction

Logging makes applications easy to maintain by giving developers and users the ability to keep track of
their execution. That's why through this article, I wanted to present two different approaches to integrate Logging mechanism into our .NET solutions:

  1. Implement our own Logging library based on ‘System.Diagnostics’ namespace.
  2. Using a third party library called Log4NET: http://logging.apache.org/log4net/

Background

This article may be useful for intermediate developers who have some basics in C#.

Using the Code

Through this article, we will explain in depth logging in .NET applications. We will explain this through two steps:

  1. Introduction to System.Diagnostics (prebuilt logging namespace in .NET)
  2. Introduction to Log4net

I. System.Diagnostics Namespace

The System.Diagnostics namespace provides classes that allow you to interact with system processes, event logs, and performance counters, Classes like EventLog which provides functionality to write to event logs, read event log entries, and create and delete event logs and event sources on the network. TraceSource which also provides a set of methods and properties that enable applications to trace the execution of code and associate trace messages with their source. In this paragraph, we will implement a tracing/Logging library based on the TraceSource Class. This library enable users to:

  • trace different scenarios inside applications [Start, Stop, Error, Information, Critical, Warning]
  • The Trace destination is configurable, it can be a file, it can be a console application and it supports also sending Log Message by mail.

To do so, this logging utility contains:

  • Interface called Logger.cs
  • Interface called ILogger.cs
  • Enumeration called LogType which presents the destination of trace messages
C#
/// <summary>
/// Trace manager contract for trace instrumentation
/// </summary>
///
public interface ILogger
{
        /// <summary>
        /// Send "start" flag to trace repository
        /// </summary>
        void TraceStart(string message);

        /// <summary>
        /// Send "stop" flag to trace repository
        /// </summary>
        void TraceStop(string message);

        /// <summary>
        /// Trace information message to trace repository
        /// <param name="message">Information message to trace</param>
        /// </summary>
        void TraceInfo(string message);

        /// <summary>
        /// Trace warning message to trace repository
        /// </summary>
        /// <param name="message">Warning message to trace</param>
        void TraceWarning(string message);

        /// <summary>
        /// Trace error message to trace repository
        /// </summary>
        /// <param name="message">Error message to trace</param>
        void TraceError(string message);

        /// <summary>
        /// Trace critical message to trace repository
        /// </summary>
        /// <param name="message">Critical message to trace</param>
        void TraceCritical(string message);
}

/// <summary>
/// The destination of the trace messages [FileSystem or Emails]
/// </summary>
public enum LogType
{
    File,
    Email,
}

The class that implements the interface ILogger.cs:

C#
public class Logger : ILogger
{
    #region Members

    TraceSource source;
    LogType logType;

    #endregion

    #region  Constructor

    /// <summary>
    /// Create a new instance of this trace manager
    /// </summary>
    public Logger(LogType logType)

    {
        // Create default source
        source = new TraceSource("Code Project : ");
        this.logType = logType;
    }

    #endregion

    #region Private Methods
    /// <summary>
    /// Trace internal message in configured listeners
    /// </summary>
    /// <param name="eventType">Event type to trace</param>
    /// <param name="message">Message of event</param>
    void TraceInternal(TraceEventType eventType, string message)
    {
        if (source != null)
        {
            try
            {
                if (this.logType == LogType.File)
                {
                    source.TraceEvent(eventType, (int)eventType, message);
                    foreach (TraceListener item in source.Listeners)
                    {
                        item.IndentSize = 4;
                        item.Flush();
                    }
                }
                else

                {
                    Console.WriteLine("Send Mail");
                  //You just need a function that send Email to someone
                }
            }
            catch (SecurityException)
            {
                //Cannot access to file listener or cannot have
                //privileges to write in event log
                //do not propagate this :-(
            }
        }
    }
    #endregion

    #region Public Methods
    /// <summary>
    /// <see cref=" Logging.ILogger"/>
    /// </summary>
    public void TraceStart(string message)
    {
        TraceInternal(TraceEventType.Start, message);
    }

    /// <summary>
    /// <see cref=" Logging.ILogger"/>
    /// </summary>
    public void TraceStop(string message)
    {
        TraceInternal(TraceEventType.Stop, message);
    }

    /// <summary>
    /// <see cref=" Logging.ILogger"/>
    /// </summary>
    /// <param name="message"><see cref=" Logging.ILogger"/></param>
    public void TraceInfo(string message)
    {
        if (String.IsNullOrEmpty(message))
            throw new ArgumentNullException(message);

        TraceInternal(TraceEventType.Information, message);
    }

    /// <summary>
    /// <see cref=" Logging.ILogger"/>
    /// </summary>
    /// <param name="message"><see cref=" Logging.ILogger"/></param>
    public void TraceWarning(string message)
    {
        if (String.IsNullOrEmpty(message))
            throw new ArgumentNullException(message);
        TraceInternal(TraceEventType.Warning, message);
    }

    /// <summary>
    /// <see cref=" Logging.ILogger"/>
    /// </summary>
    /// <param name="message"><see cref=" Logging.ILogger"/></param>
    public void TraceError(string message)
    {
        if (String.IsNullOrEmpty(message))
            throw new ArgumentNullException(message);

        TraceInternal(TraceEventType.Error, message);
    }

    /// <summary>
    /// <see cref=" Logging.ILogger"/>
    /// </summary>
    /// <param name="message"><see cref=" Logging.ILogger"/></param>
    public void TraceCritical(string message)
    {
        if (String.IsNullOrEmpty(message))
            throw new ArgumentNullException(message);

        TraceInternal(TraceEventType.Critical, message);
    }
    #endregion
}

Now let’s see how to configure the library through the configuration file. The configuration file will determine:

  1. The name of the source
  2. The type of destination message [a text file, a console file, etc...]
  3. The trace option output, in our case, the output of any trace containing this message
    • The type of trace [information, Error, warning, etc…]
    • Date Time of the trace
    • Thread ID
    • Process ID
    • TimeStamp
    XML
    <system.diagnostics>
        <sources> 
          <source name="CodeProject : " switchName="sourceSwitch" 
          switchType="System.Diagnostics.SourceSwitch">
            <listeners>
              <remove name="Default" />
              <add name="console" type="System.Diagnostics.ConsoleTraceListener" 
              traceOutputOptions="DateTime,ThreadId,ProcessId,Timestamp">
                <filter type="System.Diagnostics.EventTypeFilter" 
                initializeData="All"/>
              </add>      
            </listeners>
          </source>
        </sources>
        <switches>
          <add name="sourceSwitch" value="All"/>
        </switches>
      </system.diagnostics>

    Testing the library:

    C#
    Logger Logger = new Logger(LogType.File);//in case when we do not want to send log by Email.
    Logger.TraceStart("start Operation");
    Logger.TraceInfo("Trace Operation");
    Logger.TraceWarning("Warning Operation");
    Logger.TraceError("Error Operation");
    Logger.TraceCritical("Critical Operation");
    Logger.TraceStop("Stop Operation");

    Console Display of Trace Message

    If we want to customize the destination of trace messages, for example, display the trace message in a file system, we just add a 'TextWriterTraceListener' to the configuration file:

    XML
              <add name="myListener" type="System.Diagnostics.TextWriterTraceListener" 
    initializeData="E:\TextWriterOutput.log"
                   traceOutputOptions="DateTime,ThreadId,ProcessId,Timestamp">
                <filter type="System.Diagnostics.EventTypeFilter" initializeData="All"/>
             
     </add>

    Displaying trace messages in Bloc Notes.

    Using Bloc Notes

    You can customize the tracing output's target by adding or removing TraceListener instances to or from the collection stored in the TraceSource.Listeners property. By default, trace output is produced using an instance of the DefaultTraceListener class. The preceding configuration file example demonstrates removing the DefaultTraceListener and adding a TextWriterTraceListener/ConsoleTraceListener to produce the trace output for the trace source.

    As a Microsoft developer, I have always been more comfortable when implementing my own libraries based on .NET prebuilt namespaces because I want to have absolute control on my source code, but I have seen many projects using Logging with third party libraries, for example, Log4Net. In the next paragraph, we will learn how to integrate this library into our applications.

II. Logging with Log4NET

The Apache log4net library is a tool to help the programmer output log statements to a variety of output targets. Log4net is a port of the log4j framework to Microsoft.NET platform. To integrate this library, you must use nuget package manager.

Log4net Nuget package manager

Like the TraceSource class, Log4net library enables the developer to customize message tracing (changing the log destinations and the format of messages). We will write two scenarios:

  1. Default Configuration [Console Configuration]
    C#
    private static readonly ILog log = LogManager.GetLogger(typeof(Program));
    static void Main(string[] args)
    {
    // Default Configuration [Console Configuration]
    BasicConfigurator.Configure();
    log.Info("Hi ");
    log.Warn("Thanks Code Project");
    Console.ReadLine();
    }

    Default Log4net configuration

  2. Using custom configuration [saving messages into a text file].
    C#
    private static readonly ILog log = LogManager.GetLogger(typeof(Program));
    static void Main(string[] args)
           {
               //The Logging configuration
               String configFilePath = AppDomain.CurrentDomain.BaseDirectory + @"\Log4NET.config";
    
               if (File.Exists(configFilePath))
               {
                   //Load the configuration from the XML File
                   XmlConfigurator.Configure(new FileInfo(configFilePath));
                   log.Info("Hi ");
                   log.Warn("Thanks Code Project");
               }
               Console.ReadLine();
           }
    

    XML Configuration file

    XML
    <log4net>
       <!--FileAppender -->
       <appender name="FileAppender" type="log4net.Appender.FileAppender">
         <file value="E:\log-file.txt" />
         <appendToFile value="true" />
         <encoding value="utf-8" />
         <layout type="log4net.Layout.SimpleLayout" />
       </appender>
    
       <!-- On définit le logger root au niveau DEBUG et
       on l'associe à l'appender A1 -->
       <root>
         <level value="DEBUG" />
         <appender-ref ref="FileAppender" />
       </root>
     </log4net>
    

    Displaying trace messages in Bloc Notes.

    Log4net File Appender

Summary

Log4net is a port of Log4j to the .NET universe, Log4j is a popular logging framework and that's why log4net has rapidly grown. The class System.Diagnostics.TraceSource provides high performance logging and tracing for applications but both use nearly the same mechanism.

I hope that you appreciated my effort. Thank you for viewing my blog post, try to download the source code and do not hesitate to leave your questions, comments and thanks if you want to.

License

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