I have wrapped Log4net in a static wrapper and want to log
loggingEvent.LocationInformation.MethodName
loggingEvent.LocationInformation.ClassName
Well the error was somewhere in my appender but for completeness ill include the answer to the best of my knowledge:
the Facade you need should wrap ILogger and NOT ILog
public static class Logger
{
private readonly static Type ThisDeclaringType = typeof(Logger);
private static readonly ILogger defaultLogger;
static Logger()
{
defaultLogger =
LoggerManager.GetLogger(Assembly.GetCallingAssembly(),"MyDefaultLoggger");
...
public static void Info(string message)
{
if (defaultLogger.IsEnabledFor(infoLevel))
{
defaultLogger.Log(typeof(Logger), infoLevel, message, null);
}
}
I implemented the following solution for this (.Net framework 4.5+) : the log4net wrapper methods (e.g. Info, Warn, Error) could make use of CallerMemberName and CallerFilePath to fetch the class and method name of the code from where the logs are being called. You can then aggregate these into a custom log4net property.
Feel free to use your log4net own wrapper implementation, the only important things here are: the signature of the Info and Error methods, and the implementation of the GetLogger method.
The 'memberName' and 'sourceFilePath' args should never be specified when calling the Logger.Info or Logger.Error methods, they are auto-filled-in by .Net.
public static class Logger
{
private class LogSingletonWrapper
{
public ILog Log { get; set; }
public LogSingletonWrapper()
{
Log = LogManager.GetLogger(GetType());
}
}
private static readonly Lazy<LogSingletonWrapper> _logger = new Lazy<LogSingletonWrapper>();
public static void Info(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "")
=> GetLogger(memberName, sourceFilePath).Info(message);
public static void Error(string message,Exception ex, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "")
=> GetLogger(memberName, sourceFilePath).Error(message, ex);
private static ILog GetLogger(string memberName, string sourceFilePath)
{
var classname = sourceFilePath.Split('\\').Last().Split('.').First();
log4net.ThreadContext.Properties["Source"] = $"{classname}.{memberName.Replace(".", "")}";
return _logger.Value.Log;
}
}
Then you would could use a log conversion pattern like this in your .config file :
<conversionPattern value="[%level][%date][Thd%thread: %property{Source}][Message: %message]%newline" />
This would result in logs looking like this:
[INFO][2019-07-03 16:42:00,936][Thd1: Application.Start][Message: The application is starting up ...]
[ERROR][2019-07-03 16:42:44,145][Thd6: DataProcessor.ProcessDataBatch][Message: Attempted to divide by zero.]
The following methods were called in the above example: the Start method of the Application class, and the ProcessDataBatch method of the DataProcessor class.
The only thing I can think of doing (as I dont currently use log4net) is to request a stacktrace(new StackTrace), and go back a frame to get the info you need. However, I am unsure of the runtime performance impact of this.
Click here to learn how to implement log4net in .NET Core 2.2
The following steps are taken from the above link, and break down how to add log4net to a .NET Core 2.2 project.
First, run the following command in the Package-Manager console:
Install-Package Log4Net_Logging -Version 1.0.0
Then add a log4net.config with the following information (please edit it to match your set up):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="logfile.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d [%t] %-5p - %m%n" />
</layout>
</appender>
<root>
<!--LogLevel: OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL -->
<level value="ALL" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
</configuration>
Then, add the following code into a controller (this is an example, please edit it before adding it to your controller):
public ValuesController()
{
LogFourNet.SetUp(Assembly.GetEntryAssembly(), "log4net.config");
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
LogFourNet.Info(this, "This is Info logging");
LogFourNet.Debug(this, "This is Debug logging");
LogFourNet.Error(this, "This is Error logging");
return new string[] { "value1", "value2" };
}
Then call the relevant controller action (using the above example, call /Values/Get
with an HTTP GET), and you will receive the output matching the following:
2019-06-05 19:58:45,103 [9] INFO-[Log4NetLogging_Project.Controllers.ValuesController.Get:23] - This is Info logging