包装Log4net时如何记录MethodName?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/157232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:11:43  来源:igfitidea点击:

How to log MethodName when wrapping Log4net?

.netlogginglog4net

提问by Claus Thomsen

I have wrapped Log4net in a static wrapper and want to log

我已将 Log4net 包装在静态包装器中并想记录

loggingEvent.LocationInformation.MethodName
loggingEvent.LocationInformation.ClassName

However all I get is the name of my wrapper.

然而,我得到的只是我的包装器的名称。

How can I log that info using a forwardingappender and a static wrapper class like

如何使用 forwardingappender 和静态包装器类记录该信息,例如

Logger.Debug("Logging to Debug");
Logger.Info("Logging to Info");
Logger.Warn("Logging to Warn");
Logger.Error(ex);
Logger.Fatal(ex);

采纳答案by Claus Thomsen

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

您需要的 Facade 应该包装 ILogger 而不是 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);
        }
    }

回答by Magnus Johansson

What about the %Mand %Cvariables? http://logging.apache.org/log4net/log4net-1.2.11/release/sdk/log4net.Layout.PatternLayout.html

怎么样%M%C变量? http://logging.apache.org/log4net/log4net-1.2.11/release/sdk/log4net.Layout.PatternLayout.html

Usage, something 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?

这不是你所追求的吗?

回答by Fred

Just declare your log variable like this...

只需像这样声明您的日志变量...

private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

Then you can use it normaly.

然后就可以正常使用了。

回答by nightcoder

I would simply use something like %stacktrace{2}as a conversion pattern.

我会简单地使用类似的东西%stacktrace{2}作为转换模式。

Example of output:

输出示例:

MyNamespace.ClassName.Method > Common.Log.Warning

MyNamespace.ClassName.Method > Common.Log.Warning

where MyNamespace.ClassName.Methodis a method that is calling my wrapper and Common.Log.Warningis a method of the wrapper class.

whereMyNamespace.ClassName.Method是调用我的包装器Common.Log.Warning的方法,并且是包装器类的方法。

Conversion patterns can be found here.

可以在此处找到转换模式。

回答by Stu

This post helped me work out how to write my own wrapper so in return, thought you might like my complete class to wrap the logger which seems to work quite nicely and actually takes just over half as much time as using an ILog directly!

这篇文章帮助我解决了如何编写自己的包装器,因此作为回报,我认为您可能喜欢我的完整类来包装记录器,它似乎工作得很好,实际上只需要直接使用 ILog 一半的时间!

All that's required is the appropriate xml to set up the logging in the config file and

所需要的只是适当的 xml 来在配置文件中设置日志记录和

[assembly: log4net.Config.XmlConfigurator(Watch = true)] 

in your AssemblyInfo.cs and it should work easily.

在您的 AssemblyInfo.cs 中,它应该可以轻松工作。

One note: I'm using Log4NetDash with a seriously simple set-up so have cheated and put some information in the wrong fields (eg stack trace in Domain field), this still works for me as I don't care where the information is shown but you might want to fix this if you're setting stuff up properly if you spare time!

一个注意事项:我使用 Log4NetDash 的设置非常简单,因此作弊并将一些信息放在错误的字段中(例如,域字段中的堆栈跟踪),这对我仍然有效,因为我不在乎信息在哪里显示但如果您在空闲时间正确设置东西,您可能想要解决此问题!

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; }
        }
    }
}

回答by Quantum_Joe

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.

我为此(.Net 框架 4.5+)实现了以下解决方案:log4net 包装器方法(例如 Info、Warn、Error)可以利用 CallerMemberName 和 CallerFilePath 从日志所在的位置获取代码的类和方法名称叫。然后,您可以将这些聚合到自定义 log4net 属性中。

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.

随意使用您的 log4net 自己的包装器实现,这里唯一重要的事情是:Info 和 Error 方法的签名,以及 GetLogger 方法的实现。

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.

在调用 Logger.Info 或 Logger.Error 方法时,永远不应指定 'memberName' 和 'sourceFilePath' 参数,它们由 .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 :

然后你可以在你的 .config 文件中使用这样的日志转换模式:

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

[INFO][2019-07-03 16:42:00,936][Thd1:Application.Start][消息:应用程序正在启动......]

[错误][2019-07-03 16:42:44,145][Thd6:DataProcessor.ProcessDataBatch][消息:试图除以零。]

The following methods were called in the above example: the Start method of the Application class, and the ProcessDataBatch method of the DataProcessor class.

在上面的示例中调用了以下方法:Application 类的 Start 方法和 DataProcessor 类的 ProcessDataBatch 方法。

回答by Dark_Knight

I will just write more code of the correct 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 on for the rest of methods.

其他方法依此类推。

in web.config or app.config log4net.Layout.PatternLayoutyou can use some Conversion Patterns like:

在 web.config 或 app.config log4net.Layout.PatternLayout 中,您可以使用一些转换模式,例如:

%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>

回答by Shani Bhati

Click hereto learn how to implement log4net in .NET Core 2.2

单击此处了解如何在 .NET Core 2.2 中实现 log4net

The following steps are taken from the above link, and break down how to add log4net to a .NET Core 2.2 project.

以下步骤取自上述链接,分解了如何将 log4net 添加到 .NET Core 2.2 项目。

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

然后添加具有以下信息的 log4net.config(请编辑它以匹配您的设置):

<?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/Getwith an HTTP GET), and you will receive the output matching the following:

然后调用相关的控制器操作(使用上面的示例,/Values/Get使用 HTTP GET调用),您将收到匹配以下内容的输出:

2019-06-05 19:58:45,103 [9] INFO-[Log4NetLogging_Project.Controllers.ValuesController.Get:23] - This is Info logging

2019-06-05 19:58:45,103 [9] INFO-[Log4NetLogging_Project.Controllers.ValuesController.Get:23] - 这是信息记录

回答by mattlant

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.

我唯一能想到的(因为我目前不使用 log4net)是请求一个堆栈跟踪(新的 StackTrace),然后返回一个框架以获取您需要的信息。但是,我不确定这对运行时性能的影响。