How to dynamically set log file using App.config and System.Diagnostics?

21,094

Solution 1

Here is a worked example using Systems.Diagnostics, which has two advantages over non-base class libraries. It is always allowed (in large organizations), always available, and to the extent that developers are familiar with the base class library, the next batch of maintenance developers will have heard of it.

using System.Diagnostics;

namespace DynamicTraceLog
{
    class Program
    {
        static void Main(string[] args)
        {
            //Use TraceSource, not Trace, they are easier to turn off
            TraceSource trace = new TraceSource("app");
            //SourceSwitches allow you to turn the tracing on and off.
            SourceSwitch level =new SourceSwitch("app");
            //I assume you want to be dynamic, so probalby some user input would be here:
            if(args.Length>0 && args[0]=="Off")
                level.Level= SourceLevels.Off;
            else
                level.Level = SourceLevels.Verbose;
            trace.Switch = level;
            //remove default listner to improve performance
            trace.Listeners.Clear();
            //Listeners implement IDisposable
            using (TextWriterTraceListener file = new TextWriterTraceListener("log.txt"))
            using (ConsoleTraceListener console = new ConsoleTraceListener())
            {
                //The file will likely be in /bin/Debug/log.txt
                trace.Listeners.Add(file);
                //So you can see the results in screen
                trace.Listeners.Add(console);
                //Now trace, the console trace appears immediately.
                trace.TraceInformation("Hello world");
                //File buffers, it flushes on Dispose or when you say so.
                file.Flush();
            }
            System.Console.ReadKey();
        }
    }
}

Re: how to format the output Try either of these two for trace listeners that implement templated trace formating/output using Systems.Diagnostics classes: http://essentialdiagnostics.codeplex.com or http://ukadcdiagnostics.codeplex.com/ Systems.Diagnostics doesn't provide for any particular formating or outputing of standard tokens.

Solution 2

I know I may get down voted for this, but I'm going to go off the ranch for a minute and I'm going to suggest you use a different tool from the toolbox. Log4Net is a very powerful and succinct logging framework. For example, below is a console application that uses it and it's fully functional as you see it.

using Com.Foo;

// Import log4net classes.
using log4net;
using log4net.Config;

public class MyApp 
{
    // Define a static logger variable so that it references the
    // Logger instance named "MyApp".
    private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

    static void Main(string[] args) 
    {
        // Set up a simple configuration that logs on the console.
        BasicConfigurator.Configure();

        log.Info("Entering application.");
        Bar bar = new Bar();
        bar.DoIt();
        log.Info("Exiting application.");
    }
}

But let's say we wanted to do that with a configuration file just like you're implying you would like to use. Well, that's pretty straight forward! Below is the configuration we'd place in the App.config to accomplish the same thing:

<log4net>
    <!-- A1 is set to be a ConsoleAppender -->
    <appender name="A1" type="log4net.Appender.ConsoleAppender">

        <!-- A1 uses PatternLayout -->
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message%newline" />
        </layout>
    </appender>

    <!-- Set root logger level to DEBUG and its only appender to A1 -->
    <root>
        <level value="DEBUG" />
        <appender-ref ref="A1" />
    </root>
</log4net>

Then that configuration can be used like this:

using Com.Foo;

// Import log4net classes.
using log4net;
using log4net.Config;

public class MyApp 
{
    private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

    static void Main(string[] args) 
    {
        // BasicConfigurator replaced with XmlConfigurator.
        XmlConfigurator.Configure(new System.IO.FileInfo(args[0]));

        log.Info("Entering application.");
        Bar bar = new Bar();
        bar.DoIt();
        log.Info("Exiting application.");
    }
}

And don't let the pattern stuff catch you off guard, it's just configuring what you're coding above so that you can have some consistency in the messages. It really keeps it easy though because all you ever have to log is the information that needs to be plugged into the pattern and the pattern is then encapsulated away.


This is a crash course in Log4Net, but the reason I'm really recommending it is because in the two examples above you see that they logged to the console, but you have a myriad of possible loggers, just look at this list:

log4net.Appender.AdoNetAppender: Writes logging events to a database using either prepared statements or stored procedures.

log4net.Appender.AnsiColorTerminalAppender: Writes color highlighted logging events to a an ANSI terminal window.

log4net.Appender.AspNetTraceAppender: Writes logging events to the ASP trace context. These can then be rendered at the end of the ASP page or on the ASP trace page.

log4net.Appender.ColoredConsoleAppender: Writes color highlighted logging events to the application's Windows Console.

log4net.Appender.ConsoleAppender: Writes logging events to the application's Console. The events may go to either the standard our stream or the standard error stream.

log4net.Appender.DebugAppender: Writes logging events to the .NET system.

log4net.Appender.EventLogAppender: Writes logging events to the Windows Event Log.

log4net.Appender.FileAppender: Writes logging events to a file in the file system.

log4net.Appender.LocalSyslogAppender: Writes logging events to the local syslog service (UNIX only).

log4net.Appender.MemoryAppender: Stores logging events in an in memory buffer.

log4net.Appender.NetSendAppender: Writes logging events to the Windows Messenger service. These messages are displayed in a dialog on a users terminal.

log4net.Appender.OutputDebugStringAppender: Writes logging events to the debugger. If the application has no debugger, the system debugger displays the string. If the application has no debugger and the system debugger is not active, the message is ignored.

log4net.Appender.RemoteSyslogAppender: Writes logging events to a remote syslog service using UDP networking.

log4net.Appender.RemotingAppender: Writes logging events to a remoting sink using .NET remoting.

log4net.Appender.RollingFileAppender: Writes logging events to a file in the file system. The RollingFileAppender can be configured to log to multiple files based upon date or file size constraints.

log4net.Appender.SmtpAppender: Sends logging events to an email address.

log4net.Appender.SmtpPickupDirAppender: Sends logging events to an email address but writes the emails to a configurable directory rather than sending them directly via SMTP.

log4net.Appender.TelnetAppender: Clients connect via Telnet to receive logging events.

log4net.Appender.TraceAppender: Writes logging events to the .NET trace system.

log4net.Appender.UdpAppender: Sends logging events as connectionless UDP datagrams to a remote host or a multicast group using a UdpClient.


So, as you can see, it's extremely capable OOB. I hope this post has been helpful.

Share:
21,094
Simpleton
Author by

Simpleton

Updated on October 12, 2020

Comments

  • Simpleton
    Simpleton over 3 years

    I was looking for a solution to provide logging to my latest project when I came across an article ( http://www.daveoncsharp.com/2009/09/create-a-logger-using-the-trace-listener-in-csharp/ ) that talked about using System.Diagnostics and App.config to log via the Trace method. I was able to successfully implement both the class and the App.config but I'd really like to be able to dynamically assign the value/location (inside initializeData) of the log file and I don't have a clue how to do it. If you have any suggestions, please feel free to post!

    App.config:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.diagnostics>
        <trace autoflush="true" indentsize="4">
          <listeners>
            <add name="myListener" 
                 type="System.Diagnostics.TextWriterTraceListener" 
                 initializeData="fileSizeThreshold=512, fileSizeUnit=kilobytes, 
                 fileAgeThreshold=1, fileAgeUnit=months, fileNameTemplate='{0}\MyApp-{1:MMM-yy}.log'"/>
            <remove name="Default" />
          </listeners>
        </trace>
      </system.diagnostics>
    </configuration>
    

    Logger Class:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    
    
    namespace MyCurrentProject
    {
        class Logger
        {
            public void Error(string module, string message)
            {
                WriteEntry(message, "ERROR:", module);
            }
    
            public void Error(Exception ex, string module)
            {
                WriteEntry(ex.Message, "ERROR:", module);
            }
    
            public void Warning(string module, string message)
            {
                WriteEntry(message, "WARNING:", module);
            }
    
            public void Info(string module, string message)
            {
                WriteEntry(message, "INFO:", module);
            }
    
            private void WriteEntry(string message, string type, string module)
            {
                Trace.WriteLine(
                        string.Format("{0} {1} [{2}] {3}",
                                      DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                                      type,
                                      module,
                                      message));
            }
        }
    }
    

    RE: Sorry for not being clear... just to be clear, I need the file path that the log file output is save to to be dynamically set. I'd like the path to save to a location in %AppData%. The problem I had with the config was that once I set the value for 'initializeData' I couldn't find a way to change or dynamically set/reset that value. Truthfully... at this point I just want a solution that works and allows me to manage the location of the log file.

  • Simpleton
    Simpleton over 11 years
    Ok, I'll bite.. how do you install this package so I can add the reference to my project/solution? If it matters, I'm using VS 2012.
  • Mike Perrenoud
    Mike Perrenoud over 11 years
    @MeikelDeitrick, log4net has a NuGet package that you can find here (nuget.org/packages/log4net) so you can just pull it down through the NuGet Package Manager. If you don't have that get it here (visualstudiogallery.msdn.microsoft.com/…).
  • Simpleton
    Simpleton over 11 years
    @Mike, I was able to install log4net via NuGet but I couldn't locate the package under the list of available references. How can I add the reference?
  • Simpleton
    Simpleton over 11 years
    Actually, it appears that it automatically attached the reference to my project. Do I have to run this command every time I want to use/add a reference to log4net to my projects?
  • Mike Perrenoud
    Mike Perrenoud over 11 years
    @MeikelDeitrick, that is correct, use NuGet to install log4net for all of your projects ... it adds the reference automatically as you have seen.
  • Mike Perrenoud
    Mike Perrenoud over 11 years
    @MatthewMartin, isn't that cute.
  • Simpleton
    Simpleton over 11 years
    Thanks for the great post... I had hoped to initialize this inside my App.config so that it would be universally available to all the various classes/objects in my project that and I'd like to be able to format the output as its generated. How might I got about this using your example?
  • smith willy
    smith willy over 11 years
    Okay, then I'm not sure I understood in your question what you meant by "dynamically assign the value/location of the log file". App.config is read once when the app domain is loaded-- for winforms just at the start of the app, for asp.net 1st request or app recycles (or when web.config edits cause a recycle). Not very dynamic. To make it universally available, put in in a static class, which are shared across the app domain. In ASP.NET, do the set up work in global.asax. To intercept the output and format it to your liking, you need a custom listener. See post for suggestions.
  • Simpleton
    Simpleton over 11 years
    Sorry for not being clear... just to be clear, I need the file path that the log file output is save to to be dynamically set. I'd like the path to save to a location in %AppData%. The problem I had with the config was that once I set the value for 'initializeData' I couldn't find a way to change or dynamically set/reset that value. Truthfully... at this point I just want a solution that works and allows me to manage the location of the log file.
  • Simpleton
    Simpleton over 11 years
    @Mike, sorry Mike, but I haven't been able to get your solution to work. It seems that no matter what I try nothing happens... meaning nothing is logged.
  • smith willy
    smith willy over 11 years
    The above code should work for that, the file path is in the TextWriterTraceListener's constructor. Remove and re-add the listener when ever you need to write to a different file (say by naming it log + DateTime.Now + .txt