Using log4net for logging data in an Windows application

Debugging can be a hard and time-consuming process, especially if we are developing in a multithreaded environment. To understand the execution process we need to capture the log, log4net is a convenient way to capture logs. This tutorial will describe how to use it while we are developing TwinCAT HMI Extension.

In this case, the log file is captured for an application such as windows form, console, etc, 

Log4net has used in windows applications to captured log data. This is very important to log data during run time. It is very easy to log data by following the steps:

STEP 1:

Add an App.config file, here is an example:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
    <startup> 
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
  <log4net debug="true">
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
      <file value="logfiletest.txt" />
      <appendToFile value="false" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="50" />
      <maximumFileSize value="1MB" />
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="RollingLogFileAppender" />
    </root>
  </log4net>
</configuration>

STEP 2:

Add the log4net DLL as a reference to the project

STEP 3:

            Call the following method during starting the application, a suitable place could be when we call the main method (for example)

            //Load from App.Config file
            log4net.Config.XmlConfigurator.Configure();

STEP 4:

           In the actual class, we declare a variable 

           private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

STEP 5:

          Call the method to capture log data in the log.txt file as defined in the app.config

          log.InfoFormat("OPC identifier = {0}, initialConnection = {1}", Identifier, initialConnection);

Using log4net for logging data in a DLL

In this case log file is captured for an application such as windows DLL that is loaded by another application, 

It is very easy to log data by following the steps:

STEP 1:

Add an log4net.config file in the DLL project:

<log4net>
  <root>
    <level value="ALL" />
    <!--<appender-ref ref="console" />-->
    <appender-ref ref="file" />
  </root>
  <!--<appender name="console" type="log4net.Appender.ConsoleAppender">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date %level %logger - %message%newline" />
    </layout>
  </appender>-->
  <appender name="file" type="log4net.Appender.RollingFileAppender">
    <file value="logfile.log" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="5" />
    <maximumFileSize value="10MB" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
    </layout>
  </appender>
</log4net>

STEP 2:

Add the log4net dll as reference to the project

STEP 3:

            Configure the log4net in the main class of your DLL project, in my case it is Class1 is the main class and I have initialized the log4net here. If we know the path of the DLL loader application then we can use the path  and copy the config there by visual studio build event for example. Because log4net.config should be located in the same folder where the DLL is located. 

 

namespace TestDll
{
    public class Class1
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public Class1 ()
        {
            if (!log4net.LogManager.GetRepository().Configured)
            {
                string s = AssemblyDirectory;
                //Explicitly set the path though we should use any of the method shown here
                s = "C:\\HomeAutomation\\Log4Net_DLL_Testing\\TestDll\\TestDll\\bin\\Debug\\log4net.config";
                string path1 = System.Reflection.Assembly.GetAssembly(typeof(Class1)).Location;
                string directory = Path.GetDirectoryName(path1);
                var ss = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                var configFile = new FileInfo(s);
                if (!configFile.Exists)
                {
                    throw new FileLoadException(String.Format("The configuration file {0} does not exist", configFile));
                }
                log4net.Config.XmlConfigurator.Configure(configFile); 
            }
            log.Info("Initializing  Class1");
        }

        public void CallMethodforLog(int i)
        {
            log.InfoFormat("Calling CallMethodforLog i =  {0}", i);
        }
        public  string AssemblyDirectory
        {
            get
            {
                string codeBase = Assembly.GetExecutingAssembly().CodeBase;
                UriBuilder uri = new UriBuilder(codeBase);
                string path = Uri.UnescapeDataString(uri.Path);
                return Path.GetDirectoryName(path);
            }
        }
    }
}

STEP 4:

           We call CallMethodforLog  to add log in the file:

          

        public void CallMethodforLog(int i)
        {
            log.InfoFormat("Calling CallMethodforLog i =  {0}", i);
        }

Using log4net for logging data in a DLL loaded by browser framework (TwinCAT HMI Extension)

Precaution:

If we use log4net to capture log data from a DLL which uses a database connection specially Nhibernate then we face a problem to make a connection to the database. The reason is that NHibernate uses a different version of logger DLL  and that does not match the one we use. As a result, Nhibernate throws an exception. In that case, we need to disable the logging.

In this case, the log file is captured by the framework and configured the DLL. We are using the TwinCAT HMI framework as an example.

It is very easy to log data by following the steps:

STEP 1:

Add a log4net.config file in the  extension DLL project:

  
    
    
    
  
  
  
    
    
    
    
    
    
    
      
    
  

STEP 2:

Add the log4net DLL as a reference to the project

STEP 3:

            C# extension project wizard does not generate the constructor, you need to build a constructor where you set the log4net configuration. We need to copy the log4net.config file to the following folder. 

 

C:\TwinCAT\Functions\TE2000-HMI-Engineering\\Bin\\x86\TcHmiEngineeringExtensions.Server\

And actual log file will be generated to C:\TwinCAT\Functions\TE2000-HMI-Engineering\Infrastructure\TcHmiServer\1.10.1336.404\Win32\logfile.log

Configure the constructor.

 

   public ServerExtensionCSharp_Log4net()
   {
      if (!log4net.LogManager.GetRepository().Configured)
      {
        string s = "C:\\TwinCAT\\Functions\\TE2000-HMI-Engineering\\Bin\\x86\\TcHmiEngineeringExtensions.Server\\log4net.config";
        //Actual log is generated in the path C:\TwinCAT\Functions\TE2000-HMI-Engineering\Infrastructure\TcHmiServer\1.10.1336.404\Win32

       var configFile = new FileInfo(s);
       if (!configFile.Exists)
       {
         throw new FileLoadException(System.String.Format("The configuration file {0} does not exist", configFile));
       }
                log4net.Config.XmlConfigurator.Configure(configFile);
     }
  }

STEP 4:

           We use the log method   to log in to the file:

                         switch (mapping)
                          {
                            case "RandomValue":
                                log.Info("Accessing random value");
                                ret = RandomValue(command);
                                break;

                            case "MaxRandom":
                                log.Info("Setting max random value");
                                ret = MaxRandom(command);
                                break;

TIPS 01:  If we don’t add the log4net.config file to the HMI framework folder then HMI will not be loaded.

TIPS 02: Check the log4net.config has the right path. HMI Engineering and HMI Server have different paths. If you are mixing then the extension module will not be loaded

TIPS 03:  We can see sometimes the following message “Index (zero-based) must be greater than or equal to zero” instead of generating logs. The underlying reason for this is 

log.InfoFormat ("I am {1} years old", myAge); //Will cause this error, index start with 1, NOK                                               
log.InfoFormat ("I am {0} years old", myAge); //1 has been changed to 0 OK
log.InfoFormat ("I am {0} years old" + myAge); // there is a +, it should be ,

TIPS 04:  If you are taking logs by using hardware (Embedded PC) then you need to copy log4net.config to this directory, C:\TwinCAT\Functions\TF2000-HMI-Server and the log file will be generated in the same folder.

 

See also:  How to debug with SQLite https://www.hemelix.com/software/sqlite-logging-for-hmi-extension/