[ACCEPTED]-How to log MethodName when wrapping Log4net?-log4net
What about the %M
and %C
variables?
http://logging.apache.org/log4net/log4net-1.2.11/release/sdk/log4net.Layout.PatternLayout.html
Usage, something 1 like:
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%M %C] - %message%newline" />
</layout>
Doesn't that do what you are after?
Well the error was somewhere in my appender 3 but for completeness ill include the answer 2 to the best of my knowledge:
the Facade you 1 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 would simply use something like %stacktrace{2}
as a 5 conversion pattern.
Example of output:
MyNamespace.ClassName.Method 4 > Common.Log.Warning
where MyNamespace.ClassName.Method
is a method 3 that is calling my wrapper and Common.Log.Warning
is a method 2 of the wrapper class.
Conversion patterns 1 can be found here.
Just declare your log variable like this...
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
Then 1 you can use it normaly.
This post helped me work out how to write 16 my own wrapper so in return, thought you 15 might like my complete class to wrap the 14 logger which seems to work quite nicely 13 and actually takes just over half as much 12 time as using an ILog directly!
All that's 11 required is the appropriate xml to set up 10 the logging in the config file and
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
in your 9 AssemblyInfo.cs and it should work easily.
One 8 note: I'm using Log4NetDash with a seriously 7 simple set-up so have cheated and put some 6 information in the wrong fields (eg stack 5 trace in Domain field), this still works 4 for me as I don't care where the information 3 is shown but you might want to fix this 2 if you're setting stuff up properly if you 1 spare time!
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using log4net;
using log4net.Core;
namespace Utility
{
public class Logger
{
static Logger()
{
LogManager.GetLogger(typeof(Logger));
}
public static void Debug(string message, params object[] parameters)
{
Log(message, Level.Debug, null, parameters);
}
public static void Info(string message, params object[] parameters)
{
Log(message, Level.Info, null, parameters);
}
public static void Warn(string message, params object[] parameters)
{
Log(message, Level.Warn, null, parameters);
}
public static void Error(string message, params object[] parameters)
{
Error(message, null, parameters);
}
public static void Error(Exception exception)
{
if (exception==null)
return;
Error(exception.Message, exception);
}
public static void Error(string message, Exception exception, params object[] parameters)
{
string exceptionStack = "";
if (exception != null)
{
exceptionStack = exception.GetType().Name + " : " + exception.Message + Environment.NewLine;
Exception loopException = exception;
while (loopException.InnerException != null)
{
loopException = loopException.InnerException;
exceptionStack += loopException.GetType().Name + " : " + loopException.Message + Environment.NewLine;
}
}
Log(message, Level.Error, exceptionStack, parameters);
}
private static void Log(string message, Level logLevel, string exceptionMessage, params object[] parameters)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += LogEvent;
worker.RunWorkerAsync(new LogMessageSpec
{
ExceptionMessage = exceptionMessage,
LogLevel = logLevel,
Message = message,
Parameters = parameters,
Stack = new StackTrace(),
LogTime = DateTime.Now
});
}
private static void LogEvent(object sender, DoWorkEventArgs e)
{
try
{
LogMessageSpec messageSpec = (LogMessageSpec) e.Argument;
StackFrame frame = messageSpec.Stack.GetFrame(2);
MethodBase method = frame.GetMethod();
Type reflectedType = method.ReflectedType;
ILogger log = LoggerManager.GetLogger(reflectedType.Assembly, reflectedType);
Level currenLoggingLevel = ((log4net.Repository.Hierarchy.Logger) log).Parent.Level;
if (messageSpec.LogLevel<currenLoggingLevel)
return;
messageSpec.Message = string.Format(messageSpec.Message, messageSpec.Parameters);
string stackTrace = "";
StackFrame[] frames = messageSpec.Stack.GetFrames();
if (frames != null)
{
foreach (StackFrame tempFrame in frames)
{
MethodBase tempMethod = tempFrame.GetMethod();
stackTrace += tempMethod.Name + Environment.NewLine;
}
}
string userName = Thread.CurrentPrincipal.Identity.Name;
LoggingEventData evdat = new LoggingEventData
{
Domain = stackTrace,
Identity = userName,
Level = messageSpec.LogLevel,
LocationInfo = new LocationInfo(reflectedType.FullName,
method.Name,
frame.GetFileName(),
frame.GetFileLineNumber().ToString()),
LoggerName = reflectedType.Name,
Message = messageSpec.Message,
TimeStamp = messageSpec.LogTime,
UserName = userName,
ExceptionString = messageSpec.ExceptionMessage
};
log.Log(new LoggingEvent(evdat));
}
catch (Exception)
{}//don't throw exceptions on background thread especially about logging!
}
private class LogMessageSpec
{
public StackTrace Stack { get; set; }
public string Message { get; set; }
public Level LogLevel { get; set; }
public string ExceptionMessage { get; set; }
public object[] Parameters { get; set; }
public DateTime LogTime { get; set; }
}
}
}
How about C#4.5 feature callerinfo - http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx
0
I implemented the following solution for 25 this (.Net framework 4.5+) : the log4net 24 wrapper methods (e.g. Info, Warn, Error) could 23 make use of CallerMemberName and CallerFilePath 22 to fetch the class and method name of the 21 code from where the logs are being called. You 20 can then aggregate these into a custom log4net 19 property.
Feel free to use your log4net own 18 wrapper implementation, the only important 17 things here are: the signature of the Info 16 and Error methods, and the implementation 15 of the GetLogger method.
The 'memberName' and 14 'sourceFilePath' args should never be specified 13 when calling the Logger.Info or Logger.Error 12 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 11 you would could use a log conversion pattern 10 like this in your .config file :
<conversionPattern value="[%level][%date][Thd%thread: %property{Source}][Message: %message]%newline" />
This would 9 result in logs looking like this:
[INFO][2019-07-03 8 16:42:00,936][Thd1: Application.Start][Message: The 7 application is starting up ...]
[ERROR][2019-07-03 6 16:42:44,145][Thd6: DataProcessor.ProcessDataBatch][Message: Attempted 5 to divide by zero.]
The following methods 4 were called in the above example: the Start 3 method of the Application class, and the 2 ProcessDataBatch method of the DataProcessor 1 class.
The only thing I can think of doing (as 5 I dont currently use log4net) is to request 4 a stacktrace(new StackTrace), and go back 3 a frame to get the info you need. However, I 2 am unsure of the runtime performance impact 1 of this.
I will just write more code of the correct 4 answer of Claus
In the wrapper class
public static class Logger
{
private static readonly ILogger DefaultLogger;
static Logger()
{
defaultLogger = LoggerManager.GetLogger(Assembly.GetCallingAssembly(), "MyDefaultLoggger"); // MyDefaultLoggger is the name of Logger
}
public static void LogError(object message)
{
Level errorLevel = Level.Error;
if (DefaultLogger.IsEnabledFor(errorLevel))
{
DefaultLogger.Log(typeof(Logger), errorLevel, message, null);
}
}
public static void LogError(object message, Exception exception)
{
Level errorLevel = Level.Error;
if (DefaultLogger.IsEnabledFor(errorLevel))
{
DefaultLogger.Log(typeof(Logger), errorLevel, message, exception);
}
}
and so 3 on for the rest of methods.
in web.config 2 or app.config log4net.Layout.PatternLayout you can use some Conversion 1 Patterns like:
%location %method %line
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date{dd/MM/yyyy hh:mm:ss.fff tt} [%thread] %level %logger [%location %method %line] [%C %M] - %newline%message%newline%exception"/>
</layout>
Click here to learn how to implement log4net 14 in .NET Core 2.2
The following steps are 13 taken from the above link, and break down 12 how to add log4net to a .NET Core 2.2 project.
First, run 11 the following command in the Package-Manager 10 console:
Install-Package Log4Net_Logging -Version 1.0.0
Then add a log4net.config with the 9 following information (please edit it to 8 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 7 code into a controller (this is an example, please 6 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 5 call the relevant controller action (using 4 the above example, call /Values/Get
with an HTTP GET), and 3 you will receive the output matching the 2 following:
2019-06-05 19:58:45,103 [9] INFO-[Log4NetLogging_Project.Controllers.ValuesController.Get:23] - This 1 is Info logging
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.