3

I chose log4Net after much considerations to be my Logging application so please no arguments on that

OK, hear is my problem

  • I got a single process connected to multiple Clients
  • Each Client has a unique ID stored as a String in its own separate Thread
  • Each Client with the same unique ID can connect multiple times

  • I want to create a log file for every Client in a different .txt file.

  • At every new connection, i want to create a log file with client ID appended by Date Time and seconds

This scenario has got me confused since i also don't have any previous logging experience of any application at all.

I hope i have made my scenario clear enough :)

Basit Anwer
  • 6,742
  • 7
  • 45
  • 88

2 Answers2

3

This does not answer you question for how to write the requests to their own files, but the log4net Help specifically recommends a simpler approach. Use the ThreadContext.Properties object and other properties to decorate the log messages so that it is possible to distinguish messages from each request.

http://logging.apache.org/log4net/release/faq.html

See "Can the outputs of multiple client request go to different log files?"

If you have a Client ID, you could certainly do something like this:

log4net.ThreadContext.Properties["ClientID"] = GetTheClientId();

In your pattern layout, you can do something like this:

<conversionPattern value="%date [%thread] %-5level %logger [%property{ClientID}] - %message%newline" />

Where %property{ClientID} will pull your ClientID from the ThreadContext.

This will cause each logged message to be tagged with the relevant ClientID.

See this link for a good tutorial on using the log4net context objects:

http://www.beefycode.com/post/Log4Net-Tutorial-pt-6-Log-Event-Context.aspx

The entire log4net tutorial is very good, especially if you are just starting out with log4net.

Here is Part 1:

http://www.beefycode.com/post/Log4Net-Tutorial-pt-1-Getting-Started.aspx

Having said all of that, people do often use the GlobalContext object to name their output file, such as is described in this link:

http://geekswithblogs.net/rgupta/archive/2009/03/03/dynamic-log-filenames-with-log4net.aspx

Whenever I have seen the process of naming the output file via the GlobalContext, the suggested solution ALWAYS says to be sure to set the GlobalContext.Properties["whatever"] value BEFORE log4net actually starts. This leads me to believe that it will be difficult, if not impossible, to create individual log files based on information that is dynamically stored in the ThreadContext, since that information probably won't be known until log4net is already running.

[UPDATE]

Here is another link from here at SO that illustrates how to name the output file for a value in the GlobalContext. Note again that the value that filename is to be based on must be set into the GlobalContext before log4net is configured and before a logger is retrieved.

How do I use a GlobalContext property in a log4net appender name?

As I say above and in my comments, I am not sure that it is possible for log4net to create multiple output files (for the same FileAppender configuration) with the output filename(s) dictated by a value in the ThreadContext or by the thread id property. Maybe someone else who is more familiar with log4net can weigh in on that.

Having said that, it is possible to do exactly this in NLog. Here is an NLog configuration that defines a File Target whose name is derived, in part, from the thread id (note ${threadid} in the fileName portion of the configuration):

<targets>
    <target name="file" xsi:type="File" layout="${longdate} | ${processid} | ${threadid} | ${logger} | ${level} | ${message}" fileName="${basedir}/log_${threadid}.txt" />
</targets>

With the following code:

  class Program
  {
    public static Logger logger = LogManager.GetCurrentClassLogger();

    static void Main(string[] args)
    {

      int totalThreads = 20;
      TaskCreationOptions tco = TaskCreationOptions.None;
      Task task = null;

      logger.Info("Enter Main");

      Task[] allTasks = new Task[totalThreads];
      for (int i = 0; i < totalThreads; i++)
      {
        int ii = i;
        task = Task.Factory.StartNew(() =>
        {
          logger.Info("Inside delegate.  i = {0}", ii);
        });

        allTasks[i] = task;
      }

      logger.Info("Wait on tasks");

      Task.WaitAll(allTasks);

      logger.Info("Tasks finished");

      logger.Info("Exit Main");
    }
  }

I got 4 log files, each of whose name contains the thread id and each of which contained messages only from a single thread.

Similarly, with this configuration (note ${mdc:item=id}):

<targets>
    <target name="file" xsi:type="File" layout="${longdate} | ${processid} | ${threadid} | ${logger} | ${level} | ${message}" fileName="${basedir}/log_${mdc:item=id}.txt" />
</targets>

And this code:

  class Program
  {
    public static Logger logger = LogManager.GetCurrentClassLogger();

    static void Main(string[] args)
    {

      int totalThreads = 20;
      TaskCreationOptions tco = TaskCreationOptions.None;
      Task task = null;

      logger.Info("Enter Main");

      Task[] allTasks = new Task[totalThreads];
      for (int i = 0; i < totalThreads; i++)
      {
        int ii = i;
        task = Task.Factory.StartNew(() =>
        {
          MDC.Set("id",Thread.CurrentThread.ManagedThreadId.ToString());
          logger.Info("Inside delegate.  i = {0}", ii);
        });

        allTasks[i] = task;
      }

      logger.Info("Wait on tasks");

      Task.WaitAll(allTasks);

      logger.Info("Tasks finished");

      logger.Info("Exit Main");
    }
  }

I am able to get multiple output files based on the value stored in the MDC (the NLog equivalent to log4net's ThreadContext.Properties object).

Community
  • 1
  • 1
wageoghe
  • 27,390
  • 13
  • 88
  • 116
  • Thx for the info but there must be someother way to create multiple log files depending on a certain context oterwise i would be flooding one log file with all of the excess information of other clients – Basit Anwer Feb 01 '11 at 06:19
  • @KiNGPin - Well, I am certainly not a log4net expert, but I have looked around and have not found a way to make separate log files based on information that is known only at runtime. It is possible to cause some loggers to write to one file and others to write to other files. It is also possible to create multiple file appenders and route logging messages based on filtering. So, for example, if you always had clients called "A", "B", and "C", you could configure 3 file appenders and apply a filter to each one that only let in the appropriate logging messages. – wageoghe Feb 01 '11 at 16:05
  • It seems more likely that you don't know ahead of time what the specific client ids are or how many clients there will be. From what I understand about log4net, that will make it difficult (maybe impossible?) to do exactly what you want to do. Also, from all examples that I have seen of using the GDC (or GlobalContext.Properties) to name the output file (see the geekswithblogs link above), the example ALWAYS says that the GDC value must be set BEFORE log4net is configured. That tells me that log4net determines the filename at startup. If your ClientID values aren't known ... – wageoghe Feb 01 '11 at 16:09
  • until after the process is started (i.e. when the clients are accessing the process), then it seems likely that log4net cannot be configured to do exactly what you want. Maybe a better log4net expert will weigh in here. FWIW, you can do this with NLog via configuration. I know that you have settled on log4net, but I am going to post an example of how to do this with NLog anyway. You might find it useful. – wageoghe Feb 01 '11 at 16:11
2

Thank-you guys for all your answers and help but after lots and lots and lots of searching i finally found the answer .... not only i can create multiple Files but with dynamic file names.Hope this helps you guys as well :)

The idea is not to be based on a config file because as everybody said, one Appender is associated with one file name so one might be able to give one appender a dynamic file name but still not N number of File names

so my configuration is as follows

<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>

  <log4net>
  </log4net>

</configuration>

[UPDATE]: Actually you dont even need any config Yes, my configuration is empty, since i plan on to create dynamic configurations

Here is the code:

Main:

SetLevel(clientID, "ALL");
AddAppender(clientID, CreateFileAppender(clientID, fileName));

ILog log = LogManager.GetLogger(clientID);
log.Any("whatever you want");

Functions:

public static log4net.Appender.IAppender CreateFileAppender(string name, string fileName)
{
      log4net.Appender.FileAppender appender = new
      log4net.Appender.FileAppender();
      appender.Name = name;
      appender.File = fileName;
      appender.AppendToFile = false;

      log4net.Layout.PatternLayout layout = new
      log4net.Layout.PatternLayout();
      layout.ConversionPattern = "%d [%thread] %-5p %c [%a] - %m [%line] [%M]%n";
      layout.ActivateOptions();

      appender.Layout = layout;
      appender.ActivateOptions();

      return appender;
}

public static void SetLevel(string loggerName, string levelName)
{
      log4net.ILog log = log4net.LogManager.GetLogger(loggerName);
      log4net.Repository.Hierarchy.Logger l = (log4net.Repository.Hierarchy.Logger)log.Logger;

      l.Level = l.Hierarchy.LevelMap[levelName];
}

// Add an appender to a logger
public static void AddAppender(string loggerName, log4net.Appender.IAppender appender)
{
      log4net.ILog log = log4net.LogManager.GetLogger(loggerName);
      log4net.Repository.Hierarchy.Logger l=(log4net.Repository.Hierarchy.Logger)log.Logger;
      l.AddAppender(appender);
}

Now, what we do is create a logger with a specified name, and fetch it whenever and wherever we want in any thread or any class, one can even fetch a created appender and modify it if required by using the command

     log4net.LogManager.GetRepository().GetAppenders()

and iterating through it. So actually everything is Dynamic :)

Woops forgot to add in the orignal source: Log4Net Mail archive

Basit Anwer
  • 6,742
  • 7
  • 45
  • 88
  • Glad you found an answer. Just for completeness, I should mention that you can achieve in NLog exactly what you have shown above, but with just a single Target (equivalent of log4net Appender configuration). In your code, you would use NLog as usual - simple retrieve the logger based on name. All logged messages will go to the one Target which will send the message to a file based on the logger name. In other words, one NLog File Target can cause output to go to more than one file. If you are interested (maybe not since you are settled on log4net) I can ... – wageoghe Feb 02 '11 at 14:46
  • ... post a simple NLog configuration and a simple sample program that demonstrates it. – wageoghe Feb 02 '11 at 14:47
  • Thankyou for the help but the company i am working on has a Java counterpart also so they are pretty much settled on log4Net as its compatible with log4j – Basit Anwer Feb 10 '11 at 07:49