Come conservare le informazioni sul callsite quando si esegue il wrapping di NLog

Ho una class che avvolge NLog (chiamato NLogger). I miei registri sono salvati nel mio database. La cosa che sto avendo un problema con è come faccio a mostrare dove si è verificato il logging. ho questo

 

ma questo mostra solo Core.Logging.Loggers.NLogLogger.Log che è il mio NlogWrapper e non la class che chiama il mio wrapper.

Questo è il mio metodo di wrapper

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

    Il problema è che il tuo wrapper non si sta avviando correttamente. Ecco un esempio di come avvolgere correttamente NLog, prelevato direttamente dall’albero di origine di 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); } } } 

    La chiave sta passando il tipo di wrapper del registratore alla chiamata su Log. Quando NLog tenta di trovare il sito della chiamata, sale nello stack fino al primo metodo di chiamata il cui tipo di dichiarazione NON è il tipo passato alla chiamata Log. Questo sarà il codice che sta effettivamente chiamando il tuo wrapper.

    Nel tuo caso, il tuo logger sarebbe simile a questo:

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

    Per saltare alcuni frame e immergersi nel contesto dei chiamanti wrapper, impostarlo in App.config o nel programma il famoso modificatore:

    skipFrames = 1

    Esempi: vedi questa pagina per ${callsite:skipFrames=Integer} e questa pagina per ${callsite-linenumber:skipFrames=Integer}

    Ti consiglio di utilizzare questo formato nel tuo wrapper:

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

    L’output di questa impostazione sarà il seguente:

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

    Fonte: http://slf.codeplex.com/discussions/210075

    Ho usato il codice postato sopra per estrarre semplicemente il nome del metodo chiamante e passarlo come parte del parametro "message" al layout. Ciò mi consente di avere il nome del metodo originale in cui è stato chiamato il wrapper del log nel file di log (anziché il nome della class del wrapper del log).

    Ho combattuto con questo problema da un po ‘ora.

    Davvero improtant era il Callsite (FullyQualified Namespace) all’interno dei file di log.

    Innanzitutto, ho provato a ottenere il logger corretto da 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); } 

    Purtroppo, lo Stacktrace con MEF è molto lungo e non riesco a identificare chiaramente il chiamante corretto per il Richiedente dell’ILogger.

    Quindi, invece di iniettare l’interfaccia di ILogger tramite Constructor Injection, ho creato un’interfaccia ILogFactory, che può essere iniettata tramite Constructor Injection e chiamare quindi Create Method on the Factory

      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 } 

    E lo ha implementato:

      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 } 

    Con l’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 } 

    e implementazione di:

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

    Per usarlo … basta iniettare ILogFactory e richiamare il metodo Create in un Costruttore di importazione di Mefed:

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

    spero che questo ti aiuti

    In alternativa, è ansible evitare la soluzione nativa dall’impostazione di NLog e recuperare il file | metodo | informazioni sulla linea nel codice dei wrapper:

     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() + "} "; } 

    Quindi basta chiamare i metodi statici e aggiungere callsite prima del messaggio:

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

    Ragazzi Dopo diversi giorni di duro lavoro e ricerca. Finalmente, ho appena usato una semplice class creata da Nlog Wrapper che può conservare $ {callsite} e ottenere il nome del logger corretto quando viene creata l’istanza di Nlog Wrapper. Inserirò il codice come segue con un semplice commento. Come puoi vedere, utilizzo Stacktrace per ottenere il nome corretto del logger. Usa write e writewithex per registrare logevnet in modo che possa mantenere 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 }