I have an application, with multiple parts.
I'm using log4net throughout to log events, and I'd like those events to have a SOURCE that matches WHERE in the application they came from (you know like "SOURCE" implies). So I have a log under "Applications and Services Logs" and have set up log4net as you'd expect.
In the log4net configuration has an element "applicationName" that according to the documentation specifies the "The name of the log where messages will be stored."
The example from the docs shows
<applicationName value="MyApp" />
Log4Net uses the value from "applicationName" to log events. According to the docs in log4net source code the "ApplicationName property should be used to distinguish events".
So assuming LogName is set properly, I'd expect to see my "LogName" under "Applications and Services Logs" in the Event Viewer, and then I just need to set the applicationName to get the source correct. However, the applicationName appears only to be set in the configuration... But this means all events are logged under the same source:
How should the source name be set...? Is there an alternative way to do this?
I didn't find an answer so I downloaded the source for log4net and decided to figure it out.
The following code snippet sets the ApplicationName in log4net
private static void SetEventSource(string sourceName)
{
var repository = LogManager.GetRepository();
if (repository != null)
{
var appender = repository.GetAppenders().Where(x => x.Name == "eventLogAppender").FirstOrDefault();
if (appender is log4net.Appender.EventLogAppender)
{
var eventAppender = (log4net.Appender.EventLogAppender)appender;
eventAppender.ApplicationName = sourceName;
eventAppender.ActivateOptions();
}
}
}
I have an enumeration of "LoggingSources", so I just loop through this when the application starts to ensure that the log source exists.
public static void ConfigureWindowsEvents()
{
// Create event log sources for each of our Logging types
var loggingEvents = Enum.GetValues(typeof(LoggingSources));
foreach (var item in loggingSources)
{
string source = item.ToString();
if (!EventLog.SourceExists(source))
{
EventLog.CreateEventSource(source, EVENT_LOG_NAME);
}
}
}
And then when I log an event I set the source, and event id
public static void SetThreadContextAndLog(LoggingSources eventId, Action logAction)
{
if (logAction != null)
{
log4net.ThreadContext.Properties[EVENT_ID_KEY] = (int)eventId;
try
{
SetEventSource(eventId.ToString());
logAction();
}
finally
{
log4net.ThreadContext.Properties[EVENT_ID_KEY] = DEFAULT_EVENT_ID;
}
}
}
Calling the method as follows...
public static void LogEvent(LoggingSources pEvent, string pMessage, EventLogEntryType pEventType)
{
SetThreadContextAndLog(pEvent, () =>
{
if (pEventType == EventLogEntryType.Warning)
{
Log.Warn(pMessage);
}
});
}
This results in the source and event id being correctly set...
It's a bit of a faff, but I can't see a neater way of setting the log source without creating a custom Appender...