I have a web api 2 application, as of now configured with Windows Authentication. The application has a dedicated app pool called FlowManager running using ApplicationPoolIdentity.
here is the log4net configuration from web.config file, where I am using %username in PatternLayout to log the user name.
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="C:\Logs\FlowManager\FlowManager.log" />
<param name="AppendToFile" value="true" />
<datePattern value="yyyyMMdd" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="3" />
<maximumFileSize value="2MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d{MM-dd-yyyy HH:mm:ss:fff} %-5level [%property{log4net:HostName}, %username] %m%n" />
</layout>
</appender>
<root>
<level value="TRACE" />
<appender-ref ref="LogFileAppender" />
</root>
</log4net>
So in the application code where I want to log, I am using
logger.Debug("Request entered"); //in a http module
logger.Debug("Test log entry"); //in the api controller
It is generating log as expected.
06-08-2016 12:06:07:287 DEBUG [1178959001DLP, IIS APPPOOL\FlowManager] Request entered
06-08-2016 12:06:09:257 DEBUG [1178959001DLP, IIS APPPOOL\FlowManager] Test log entry
Now I was asked to log the authenticated user name. I found this nice question Capture username with log4net and followed the instructions and was able to achieve it. Here is how the new log4net configuration looks like. For simplicity, I am just showing PatternLayout section of log4net.
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d{MM-dd-yyyy HH:mm:ss:fff} %-5level [%property{log4net:HostName}, %HTTPUser] %m%n" />
<converter>
<name value="HTTPUser" />
<type value="LoggerExtentions.ContextUserPatternConverter" />
</converter>
</layout>
As you see I created a custom pattern converter to capture user name.
using System.Threading;
using System.Web;
using log4net.Core;
using log4net.Layout.Pattern;
namespace LoggerExtentions
{
/// <summary>
/// Log4net custom pattern converter to log context user instaed of app pool user
/// </summary>
public class ContextUserPatternConverter : PatternLayoutConverter
{
protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
{
var userName = string.Empty;
var context = HttpContext.Current;
if (context != null && context.User != null && context.User.Identity.IsAuthenticated)
{
userName = context.User.Identity.Name;
}
else
{
var threadPincipal = Thread.CurrentPrincipal;
if (threadPincipal != null && threadPincipal.Identity.IsAuthenticated)
{
userName = threadPincipal.Identity.Name;
}
}
writer.Write(userName);
}
}
}
This gives me the following log messages
06-08-2016 13:06:09:123 DEBUG [1178959001DLP, ] Request entered
06-08-2016 13:06:09:257 DEBUG [1178959001DLP, DOMAIN\johnkl] Test log entry
If you notice the log entry that is created from http module has no user name, which I get it because Context is not yet set with authenticated user. I have been asked to log the default username captured by log4net in such situations. My question is how can I achieve it? Whenever I don't have a HttpContext user, how can I supply the default username captured by log4net? Is there a way to provide a conditional statement in the log4net Pattern layout? Or any other alternate option?