Как сохранить информацию о месте вызова при упаковке NLog - PullRequest
31 голосов
/ 14 сентября 2011

У меня есть класс, который упаковывает NLog (называется NLogger). Мои журналы сохраняются в моей базе данных. У меня проблема с тем, как показать, где произошла регистрация. У меня есть это

<parameter name="@Logger" layout="${callsite}"/>  

но это просто показывает 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);
        }

Ответы [ 8 ]

41 голосов
/ 20 сентября 2011

Проблема в том, что ваша оболочка упакована неправильно.Вот пример того, как правильно обернуть NLog, взятый непосредственно из исходного дерева NLog :

using System;
using System.Text;
using NLog;

namespace LoggerWrapper
{    
  /// <summary>    
  /// Provides methods to write messages with event IDs - useful for the Event Log target.    
  /// Wraps a Logger instance.    
  /// </summary>    
  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);
    }
9 голосов
/ 27 апреля 2016

Чтобы пропустить несколько кадров и погрузиться в контекст вызывающих оболочек, установите в App.config или в программе известный модификатор:

skipFrames = 1

Примеры: см. эту страницу для ${callsite:skipFrames=Integer} и эту страницу для ${callsite-linenumber:skipFrames=Integer}

Я рекомендую использовать этот формат в своей оболочке:

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

Вывод этого параметра будет следующим:

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

2 голосов
/ 22 июля 2013

Я уже некоторое время борюсь с этой проблемой.

Действительно неподходящим был 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);
    }

Но, к сожалению, Stacktrace с MEF очень длинный, и я не могу однозначно определить правильный вызывающий объект для реквестера ILogger.

Итак, вместо инъекции ILoggerИнтерфейс с помощью Constructor Injection, я создал интерфейс ILogFactory, который может вводиться с помощью Constructor Injection и вызывать затем метод Create на фабрике

    public interface ILogFactory
    {
        #region Public Methods and Operators

        /// <summary>
        ///     Creates a logger with the Callsite of the given Type
        /// </summary>
        /// <example>
        ///     factory.Create(GetType());
        /// </example>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        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;

      /// <summary>
      ///     The logging service.
      /// </summary>
      public class Logger : NLog.Logger, ILogger
      {
          #region Fields

          private string _loggerName;

          #endregion

          #region Public Methods and Operators

          /// <summary>
          ///     The get logging service.
          /// </summary>
          /// <returns>
          ///     The <see cref="ILogger" />.
          /// </returns>
          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 и вызовите метод Create в конструкторе импорта Mefed:

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

надеюсь, это поможет

2 голосов
/ 21 ноября 2011
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» в макет. Это позволяет мне записать в файл журнала исходное имя метода, для которого была вызвана оболочка журнала, а не имя класса оболочки журнала.

0 голосов
/ 24 июня 2019

В настоящее время более простым подходом для исправления места вызова является использование LogManager.AddHiddenAssembly(Assembly)

например,

LogManager.AddHiddenAssembly(yourAssembly);

Это исправит место вызова и не требует ручного обхода стека и т. Д.

0 голосов
/ 24 января 2019

Существует простой способ добиться этого.Просто добавьте эти атрибуты в свои сигнатуры метода оболочки журнала:

void Log(LogSeverity severity, string message, [CallerFilePath] string fileName = null, [CallerMemberName] string member = null, [CallerLineNumber] int? lineNumber = null);

и передайте их обернутым методам NLog.

См. https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callerfilepathattribute?view=netframework-4.7.2 для получения дополнительной информации о System.Runtime.Сервисы компилятора в .NET.

0 голосов
/ 21 сентября 2016

Ребята После нескольких дней напряженной работы и поиска. Наконец, я просто использую один простой класс, построенный Nlog Wrapper, который может сохранить $ {CallSite} и получить правильное имя регистратора при создании экземпляра Nlog Wrapper.Я поставлю код, как следует с простым комментарием.Как видите, я использую Stacktrace, чтобы получить правильное имя логгера.Используйте write и writewithex, чтобы зарегистрировать logevnet, чтобы он мог сохранить сайт вызовов.

  public  class NlogWrapper 
    {  
        private  readonly NLog.Logger _logger; //NLog logger

    /// <summary>
    /// This is the construtor, which get the correct logger name when instance created  
    /// </summary>

    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();
        }
    }
    /// <summary>
    /// These two method are used to retain the ${callsite} for all the Nlog method  
    /// </summary>
    /// <param name="level">LogLevel.</param>
    ///  <param name="format">Passed message.</param>
    ///  <param name="ex">Exception.</param>
    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
    /// <summary>
    /// This method writes the Debug information to trace file
    /// </summary>
    /// <param name="message">The message.</param>
    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);
    }

    /// <summary>
    /// This method writes the Information to trace file
    /// </summary>
    /// <param name="message">The message.</param>
    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);
    }
    /// <summary>
    /// This method writes the Warning information to trace file
    /// </summary>
    /// <param name="message">The message.</param>
    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);
    }

    /// <summary>
    /// This method writes the Error Information to trace file
    /// </summary>
    /// <param name="error">The error.</param>
    /// <param name="exception">The exception.</param>
    //   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);
    }  


    /// <summary>
    /// This method writes the Fatal exception information to trace target
    /// </summary>
    /// <param name="message">The message.</param>
    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);
    }

    /// <summary>
    /// This method writes the trace information to trace target
    /// </summary>
    /// <param name="message">The message.</param>
    /// 
    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

    }
0 голосов
/ 27 апреля 2016

В качестве альтернативы, вы можете избежать нативного решения из 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() + "} ";
}

Затем вы просто вызываете свои статические методы и добавляете сайт вызова перед сообщением:

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