如何在包装NLog时保留呼叫站点信息

我有一个包装NLog的类(称为NLogger)。 我的日志保存到我的数据库中。 我遇到问题的是如何显示日志记录的位置。 我有这个

 

但这只是显示Core.Logging.Loggers.NLogLogger.Log这是我的NlogWrapper而不是调用我的包装器的类。

这是我的包装方法

  public void Log(LogType messageType, Type context, string message, Exception exception) { NLog.Logger logger = NLog.LogManager.GetLogger(context.Name); LogLevel logLevel = LogLevel.Info; // Default level to info switch (messageType) { case LogType.Debug: logLevel = LogLevel.Debug; break; case LogType.Info: logLevel = LogLevel.Info; break; case LogType.Warning: logLevel = LogLevel.Warn; break; case LogType.Error: logLevel = LogLevel.Error; break; case LogType.Fatal: logLevel = LogLevel.Fatal; break; default: throw new ArgumentException("Log message type is not supported"); } logger.Log(logLevel, message, exception); } 

问题是你的包装器没有正确包装。 下面是一个如何正确包装NLog的示例,直接从NLog的源代码树中获取 :

 using System; using System.Text; using NLog; namespace LoggerWrapper { ///  /// Provides methods to write messages with event IDs - useful for the Event Log target. /// Wraps a Logger instance. ///  class MyLogger { private Logger _logger; public MyLogger(string name) { _logger = LogManager.GetLogger(name); } public void WriteMessage(string eventID, string message) { /// /// create log event from the passed message /// LogEventInfo logEvent = new LogEventInfo(LogLevel.Info, _logger.Name, message); // // set event-specific context parameter // this context parameter can be retrieved using ${event-context:EventID} // logEvent.Context["EventID"] = eventID; // // Call the Log() method. It is important to pass typeof(MyLogger) as the // first parameter. If you don't, ${callsite} and other callstack-related // layout renderers will not work properly. // _logger.Log(typeof(MyLogger), logEvent); } } } 

关键是将记录器包装器的类型传递给对Log的调用。 当NLog试图找到呼叫站点时,它会上升到堆栈,直到第一个调用方法的声明类型不是传递给Log调用的类型。 这将是实际调用您的包装器的代码。

在您的情况下,您的记录器看起来像这样:

  public void Log(LogType messageType, Type context, string message, Exception exception) { NLog.Logger logger = NLog.LogManager.GetLogger(context.Name); LogLevel logLevel = LogLevel.Info; // Default level to info switch (messageType) { case LogType.Debug: logLevel = LogLevel.Debug; break; case LogType.Info: logLevel = LogLevel.Info; break; case LogType.Warning: logLevel = LogLevel.Warn; break; case LogType.Error: logLevel = LogLevel.Error; break; case LogType.Fatal: logLevel = LogLevel.Fatal; break; default: throw new ArgumentException("Log message type is not supported"); } // // Build LogEvent here... // LogEventInfo logEvent = new LogEventInfo(logLevel, context.Name, message); logEvent.Exception = exception; // // Pass the type of your wrapper class here... // logger.Log(typeof(YourWrapperClass), logEvent); } 

要跳过几帧并深入了解包装调用者上下文,请在App.config中设置,或者在程序中设置着名的修饰符:

skipFrames = 1

示例:请参阅此页面以获取${callsite:skipFrames=Integer} 此页面的${callsite-linenumber:skipFrames=Integer}

我建议你在你的包装器中使用这种格式:

 ${callsite:fileName=true:includeSourcePath=false:skipFrames=1} 

此设置的输出如下:

… {LicenseServer.LSCore.MainThreadFunction(LSCore.cs:220)} …

 internal string GetCallingMethodName() { string result = "unknown"; StackTrace trace = new StackTrace(false); for (int i = 0; i < trace.FrameCount; i++) { StackFrame frame = trace.GetFrame(i); MethodBase method = frame.GetMethod(); Type dt = method.DeclaringType; if (!typeof(ILogger).IsAssignableFrom(dt) && method.DeclaringType.Namespace != "DiagnosticsLibrary") { result = string.Concat(method.DeclaringType.FullName, ".", method.Name); break; } } return result; } 

资料来源: http : //slf.codeplex.com/discussions/210075

我使用上面发布的代码简单地提取调用方法名称,并将其作为“message”参数的一部分传递给布局。 这使我可以将调用日志包装器的原始方法名称写入日志文件(而不是日志包装器的类名)。

我一直在与这个问题作斗争。

真正重要的是日志文件中的Callsite(FullyQualified Namespace)。

首先,我试图从Stacktrace中获取正确的记录器:

  [MethodImpl(MethodImplOptions.NoInlining)] private static NLog.Logger GetLogger() { var stackTrace = new StackTrace(false); StackFrame[] frames = stackTrace.GetFrames(); if (null == frames) throw new ArgumentException("Stack frame array is null."); StackFrame stackFrame; switch (frames.Length) { case 0: throw new ArgumentException("Length of stack frames is 0."); case 1: case 2: stackFrame = frames[frames.Length - 1]; break; default: stackFrame = stackTrace.GetFrame(2); break; } Type declaringType = stackFrame.GetMethod() .DeclaringType; return declaringType == null ? LogManager.GetCurrentClassLogger() : LogManager.GetLogger(declaringType.FullName); } 

但遗憾的是,带有MEF的Stacktrace非常长,我无法清楚地识别ILogger请求者的正确调用者。

因此,我没有通过构造函数注入注入ILogger接口,而是创建了一个ILogFactory接口,可以通过构造函数注入注入,然后调用工厂上的Create方法

  public interface ILogFactory { #region Public Methods and Operators ///  /// Creates a logger with the Callsite of the given Type ///  ///  /// factory.Create(GetType()); ///  /// The type. ///  ILogger Create(Type type); #endregion } 

并实施了它:

  using System; using System.ComponentModel.Composition; [Export(typeof(ILogFactory))] [PartCreationPolicy(CreationPolicy.Shared)] public class LogFactory : ILogFactory { #region Public Methods and Operators public ILogger Create(Type type) { var logger = new Logger().CreateLogger(type); return logger; } #endregion } 

使用ILogger:

  public interface ILogger { #region Public Properties bool IsDebugEnabled { get; } bool IsErrorEnabled { get; } bool IsFatalEnabled { get; } bool IsInfoEnabled { get; } bool IsTraceEnabled { get; } bool IsWarnEnabled { get; } #endregion #region Public Methods and Operators void Debug(Exception exception); void Debug(string format, params object[] args); void Debug(Exception exception, string format, params object[] args); void Error(Exception exception); void Error(string format, params object[] args); void Error(Exception exception, string format, params object[] args); void Fatal(Exception exception); void Fatal(string format, params object[] args); void Fatal(Exception exception, string format, params object[] args); void Info(Exception exception); void Info(string format, params object[] args); void Info(Exception exception, string format, params object[] args); void Trace(Exception exception); void Trace(string format, params object[] args); void Trace(Exception exception, string format, params object[] args); void Warn(Exception exception); void Warn(string format, params object[] args); void Warn(Exception exception, string format, params object[] args); #endregion } 

和实施:

  using System; using NLog; using NLog.Config; ///  /// The logging service. ///  public class Logger : NLog.Logger, ILogger { #region Fields private string _loggerName; #endregion #region Public Methods and Operators ///  /// The get logging service. ///  ///  /// The . ///  public ILogger CreateLogger(Type type) { if (type == null) throw new ArgumentNullException("type"); _loggerName = type.FullName; var logger = (ILogger)LogManager.GetLogger(_loggerName, typeof(Logger)); return logger; } 

要使用它…只需注入ILogFactory并在Mefed导入构造函数中调用Create方法:

  [ImportingConstructor] public MyConstructor( ILogFactory logFactory) { _logger = logFactory.Create(GetType()); } 

希望这可以帮助

或者,您可以避免NLog设置的本机解决方案,并检索文件 方法| 包装器代码中的行信息:

 using System.Diagnostics; ... static private string GetCallsite() { StackFrame sf = new StackTrace(2/*Skip two frames - dive to the callers context*/, true/*Yes I want the file info !*/).GetFrame(0); return "{" + sf.GetFileName() + " | " + sf.GetMethod().Name + "-" + sf.GetFileLineNumber() + "} "; } 

然后你只需调用静态方法并在消息之前添加callsite:

 LogManager.GetCurrentClassLogger().Trace(GetCallsite() + "My Trace Message."); 

伙计经过几天的艰苦努力和搜索。最后,我只使用一个简单的类构建了Nlog Wrapper,它可以保留$ {callsite}并在创建Nlog Wrapper实例时获取正确的记录器名称。 我将把代码放在简单的注释之后。 如您所见,我使用Stacktrace来获得正确的记录器名称。 使用write和writewithex注册logevnet,以便保留callsite。

  public class NlogWrapper { private readonly NLog.Logger _logger; //NLog logger ///  /// This is the construtor, which get the correct logger name when instance created ///  public NlogWrapper() { StackTrace trace = new StackTrace(); if (trace.FrameCount > 1) { _logger = LogManager.GetLogger(trace.GetFrame(1).GetMethod().ReflectedType.FullName); } else //This would go back to the stated problem { _logger = LogManager.GetCurrentClassLogger(); } } ///  /// These two method are used to retain the ${callsite} for all the Nlog method ///  /// LogLevel. /// Passed message. /// Exception. private void Write(LogLevel level, string format, params object[] args) { LogEventInfo le = new LogEventInfo(level, _logger.Name, null, format, args); _logger.Log(typeof(NlogWrapper), le); } private void WriteWithEx(LogLevel level, string format,Exception ex, params object[] args) { LogEventInfo le = new LogEventInfo(level, _logger.Name, null, format, args); le.Exception = ex; _logger.Log(typeof(NlogWrapper), le); } #region Methods ///  /// This method writes the Debug information to trace file ///  /// The message. public void Debug(String message) { if (!_logger.IsDebugEnabled) return; Write(LogLevel.Debug, message); } public void Debug(string message, Exception exception, params object[] args) { if (!_logger.IsFatalEnabled) return; WriteWithEx(LogLevel.Debug, message, exception); } ///  /// This method writes the Information to trace file ///  /// The message. public void Info(String message) { if (!_logger.IsInfoEnabled) return; Write(LogLevel.Info, message); } public void Info(string message, Exception exception, params object[] args) { if (!_logger.IsFatalEnabled) return; WriteWithEx(LogLevel.Info, message, exception); } ///  /// This method writes the Warning information to trace file ///  /// The message. public void Warn(String message) { if (!_logger.IsWarnEnabled) return; Write(LogLevel.Warn, message); } public void Warn(string message, Exception exception, params object[] args) { if (!_logger.IsFatalEnabled) return; WriteWithEx(LogLevel.Warn, message, exception); } ///  /// This method writes the Error Information to trace file ///  /// The error. /// The exception. // public static void Error( string message) // { // if (!_logger.IsErrorEnabled) return; // _logger.Error(message); //} public void Error(String message) { if (!_logger.IsWarnEnabled) return; //_logger.Warn(message); Write(LogLevel.Error, message); } public void Error(string message, Exception exception, params object[] args) { if (!_logger.IsFatalEnabled) return; WriteWithEx(LogLevel.Error, message, exception); } ///  /// This method writes the Fatal exception information to trace target ///  /// The message. public void Fatal(String message) { if (!_logger.IsFatalEnabled) return; Write(LogLevel.Fatal, message); } public void Fatal(string message, Exception exception, params object[] args) { if (!_logger.IsFatalEnabled) return; WriteWithEx(LogLevel.Fatal, message, exception); } ///  /// This method writes the trace information to trace target ///  /// The message. /// public void Trace(string message, Exception exception, params object[] args) { if (!_logger.IsFatalEnabled) return; WriteWithEx(LogLevel.Trace, message, exception); } public void Trace(String message) { if (!_logger.IsTraceEnabled) return; Write(LogLevel.Trace, message); } #endregion }