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).