2016-08-10 6 views
1

Итак, только что начал использовать NLog. Я выполняю программную реализацию, где я пытаюсь настроить класс, который я могу импортировать в любой проект. Класс имеет два метода: CreateLogger() и GenerateLog(). Вот класс во всей своей полноте:Как заменить NLog Logger новым экземпляром Logger?

using System; 
using NLog; 
using NLog.Config; 
using NLog.Targets; 

namespace LogEngine 
{ 
    /// <summary> 
    ///  Create an instance of NLog for use on a per class level. 
    /// </summary> 
    internal sealed class EventLog 
    { 
     #region Internal Methods 

     /// <summary> 
     ///  Generates the NLog.Logger object that will control logging facilities in this program. 
     /// </summary> 
     /// <returns> 
     ///  static reference to a <see cref="NLog.Logger" /> object. 
     /// </returns> 
     internal static Logger CreateLogger(string baseDir = @"${basedir}\") 
     { 
      // Setup log configuration object and new file and screen output targets. 
      var config = new LoggingConfiguration(); 

      var screenTarget = new ConsoleTarget(); 
      config.AddTarget("screen", screenTarget); 

      var fileTarget = new FileTarget(); 
      config.AddTarget("file", fileTarget); 

      screenTarget.Layout = @"${newline}${message}"; 

      var MinScreenOutput = new LoggingRule("*", LogLevel.Fatal, screenTarget); 
      config.LoggingRules.Add(MinScreenOutput); 

      // Set the properties for the file output target. 
      fileTarget.FileName = baseDir + @"${appdomain:format={1\}} logs\${shortdate}.log"; 
      fileTarget.Layout = @"${longdate} ${pad:padcharacter=~:padding=29:inner= ${level:uppercase=true}}" 
           + @" ${pad:padcharacter=~:padding=30:inner= Event ID\: ${event-properties:item=EventCode}}" 
           + @"${newline}${message} ${when:when=level == 'Error':inner=${newline}Class/Method\:" 
           + @"${pad:padding=9:inner=}${callsite:fileName=true:includeSourcePath=false:skipFrames=1}" 
           + @"${newline}Exception\:${pad:padding=14:inner=}${exception}}${newline}"; 

      // Define what sort of events to send to the file output target. 
      var MinOutputDebug = new LoggingRule("*", LogLevel.Debug, fileTarget); 
      config.LoggingRules.Add(MinOutputDebug); 

      // Set the configuration for the LogManager 
      LogManager.Configuration = config; 

      // Get the working instance of the logger. 
      return LogManager.GetLogger("LogEngine"); 
     } 

     /// <summary> 
     ///  Passes one log entry to the destination logger. 
     /// </summary> 
     /// <param name="log"> 
     ///  The <see cref="NLog.Logger" /> object to write the log entry to. 
     /// </param> 
     /// <param name="eventId"> 
     ///  Four character unique event ID as <see cref="System.String" />. 
     /// </param> 
     /// <param name="level"> 
     ///  The <see cref="NLog.LogLevel" /> value. 
     /// </param> 
     /// <param name="message"> 
     ///  The message to save to the log file as <see cref="System.String" />. 
     /// </param> 
     /// <param name="ex"> 
     ///  If this is an error log event, pass it as an <see cref="System.Exception" /> object. 
     /// </param> 
     internal static void GenerateLog(Logger log, string eventId, LogLevel level, string message, Exception ex = null) 
     { 
      // Values used for all events. 
      LogEventInfo logEvent = new LogEventInfo(); 
      logEvent.Properties["EventCode"] = eventId; 
      logEvent.Level = level; 
      logEvent.Message = message; 

      // If we have an error log, make sure the exception is passed. 
      if (level.Equals(LogLevel.Error)) 
       logEvent.Exception = ex; 

      // Actually write the log entry. 
      log.Log(logEvent); 

      if (level.Equals(LogLevel.Error) || level.Equals(LogLevel.Fatal)) 
       System.Environment.Exit(Convert.ToInt32(eventId)); 
     } 

     #endregion Internal Methods 
    } 
} 

В методе CreateLogger(), вы увидите, есть параметр по умолчанию. Итак, как это работает, когда я называю CreateLogger() в моей программе в начале моего класса, я не передавать никаких параметров и значение $ {имя_базовой_папки} используется для генерации начального протоколирования:

internal class Program 
{ 
    #region Private Fields 

    private static Logger log = EventLog.CreateLogger(); 

    #endregion Private Fields 
... 

Однако, во время Мне нужно изменить местоположение регистрации из $ {basedir} в значение, которое я извлекаю из базы данных SQL. Вот как я это сделать:

if (!path.Equals(null)) 
{ 
    sArgs.path = path.ToString().Trim(); 
    //NLog.Config.SimpleConfigurator.ConfigureForFileLogging(sArgs.path + @"Logs\log1.txt", LogLevel.Debug); 
    //LogManager.Shutdown(); 
    //LogManager.ReconfigExistingLoggers(); 
    log = EventLog.CreateLogger(sArgs.path); 
    LogManager.ReconfigExistingLoggers(); 
} 

«путь» является объект, возвращаемый вызовом SQLCommand.ExecuteScalar(). Это замена $ {basedir}, что мне нужно подключить мой Logger. Если путь не равен NULL, тогда я преобразовываю его в строку и сохраняю его в одноэлементный класс, созданный как «sArgs». Здесь есть какой-то прокомментированный код, чтобы показать, как я пытался решить эту проблему.

Хорошо, поэтому то, что я вижу, находится в последнем блоке кода (когда я установил «журнал» в новый экземпляр, сгенерированный CreateLogger (sArgs.path)). Я вижу, что мой путь ведения журнала в объекте журнала фактически обновление. Но, когда я получаю первую возможность регистрировать событие, он все еще использует старый экземпляр Logger (так что $ {basedir} все еще используется, а не sArgs.path).

Мой вопрос в том, что мне не хватает, что сохраняет изменения в «log», которые я могу явно видеть, когда мой код в отладчике меняет код, фактически становясь местом для объекта Logger? Или это то, что я делаю класс EventLog совершенно неправильным?

Благодарим за понимание, которое вы можете предоставить этой проблеме.

ответ

0

Для каждого класса, в котором вы используете private static Logger log = EventLog.CreateLogger();, вам необходимо изменить baseDir, если вы не хотите, чтобы базовыйDir по умолчанию, определенный в вашем классе EventLog, который будет использоваться.

Вы не указали код для вашего одиночного класса sArgs или любые другие образцы классов, в которых вы хотите использовать свой EventLog.

Используя код, я только изменил ваш EventLog к EventLogger, а также по умолчанию CreateLogger к internal static Logger CreateLogger(string baseDir = @"C:\Temp\NLog\Default\"):

using System; 
using NLog; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     private static Logger log = EventLogger.CreateLogger(); 

     static void Main(string[] args) 
     { 

      EventLogger.GenerateLog(log, "1", LogLevel.Debug, "Default", null); 

      log = EventLogger.CreateLogger(@"C:\Temp\NLog\New\"); 
      LogManager.ReconfigExistingLoggers(); 

      EventLogger.GenerateLog(log, "2", LogLevel.Debug, "New", null); 

      Class1.DoSomething(); 

      Console.WriteLine("Press ENTER to exit"); 
      Console.ReadLine(); 
     } 
    } 
} 

И Class1:

using NLog; 

namespace ConsoleApplication1 
{ 
    public static class Class1 
    { 
     private static Logger log = EventLogger.CreateLogger(); 

     public static void DoSomething() 
     { 
      EventLogger.GenerateLog(log, "3", LogLevel.Debug, "Class1.DoSomething", null); 
     } 
    } 
} 

Запуск результат кода в следующий вывод:

Журнал EventId 1 будет записан в C:\Temp\NLog\Default\ConsoleApplication1.vshost.exe\logs Вход EventId 2 будет записан в C:\Temp\NLog\New\ConsoleApplication1.vshost.exe\logs

и LogEventId 3 в Class1.cs будет записаны C:\Temp\NLog\Default\ConsoleApplication1.vshost.exe\logs, потому что в Class1.cs, когда журнал был инициированным, был использован путь по умолчанию. Если вы хотите изменить baseDir журнала в Class1.cs (и последующих классах), вам нужно будет обновить пути по отдельности.

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

+0

Спасибо @Riaan. Не знаю, почему я этого не видел, но это именно то, что происходит с моей следующей возможностью генерировать журнал после обновления пути в другом классе. Итак, означает ли это, что я должен скрывать класс EventLogger (как вы его назвали) в одноэлементный класс? Это означало бы, когда я введу метод, который я хочу выполнить, мне нужно будет связать его с экземпляром singleton (например, я делаю с sArgs для хранения аргументов моей программы). – breusshe

+0

Я думаю, мне также нужно будет обновить свой макет, поэтому вызов $ {callsite: fileName = true: includeSourcePath = false: skipFrames = 1} будет обновлять skipFrames до равного 2, чтобы фактический метод, в котором происходит ошибка, сохраняется. – breusshe

+0

На самом деле, теперь, когда я думаю об этом, возможно, один-одинетовый класс не подходит для этого. Что мне действительно нужно сделать, так это выяснить, как Class1 проверяет, нужно ли обновлять его конфигурацию. – breusshe

0

Спасибо, @Riann за вашу помощь и комментарии. Я действительно получил эту работу как одноэлементный класс, не влияя на способность $ {callsite} знать фактический метод и строку, где ошибка поймана. Вот как я сделал это, начиная с обновленным классом EventLog:

using System; 
using NLog; 
using NLog.Config; 
using NLog.Targets; 

namespace LogEngine 
{ 
    /// <summary> 
    ///  Create an instance of NLog for use on a per class level. 
    /// </summary> 
    internal sealed class EventLog 
    { 
     #region Constructors 

     static EventLog() 
     { 
     } 

     private EventLog() 
     { 
      this.CreateLogger(); 
     } 

     #endregion Constructors 

     #region Singleton Objects 

     internal static EventLog logger { get { return _logger; } } 

     private static readonly EventLog _logger = new EventLog(); 

     #endregion Singleton Objects 

     #region Private Fields 

     private static Logger _log; 

     #endregion Private Fields 

     #region Internal Methods 

     /// <summary> 
     ///  Generates the NLog.Logger object that will control logging facilities in this program. 
     /// </summary> 
     internal void CreateLogger(string baseDir = @"${basedir}\") 
     { 
      // Setup log configuration object and new file and screen output targets. 
      var config = new LoggingConfiguration(); 

      var screenTarget = new ConsoleTarget(); 
      config.AddTarget("screen", screenTarget); 

      var fileTarget = new FileTarget(); 
      config.AddTarget("file", fileTarget); 

      screenTarget.Layout = @"${newline}${message}"; 

      var MinScreenOutput = new LoggingRule("*", LogLevel.Fatal, screenTarget); 
      config.LoggingRules.Add(MinScreenOutput); 

      // Set the properties for the file output target. 
      fileTarget.FileName = baseDir + @"${appdomain:format={1\}} logs\${shortdate}.log"; 
      fileTarget.Layout = @"${longdate} ${pad:padcharacter=~:padding=29:inner= ${level:uppercase=true}}" 
           + @" ${pad:padcharacter=~:padding=30:inner= Event ID\: ${event-properties:item=EventCode}}" 
           + @"${newline}${message} ${when:when=level == 'Error':inner=${newline}Class/Method\:" 
           + @"${pad:padding=9:inner=}${callsite:fileName=true:includeSourcePath=false:skipFrames=1}" 
           + @"${newline}Exception\:${pad:padding=14:inner=}${exception}}" 
           + @"${when:when=level == 'Fatal':inner=${newline}Class/Method\:" 
           + @"${pad:padding=9:inner=}${callsite:fileName=true:includeSourcePath=false:skipFrames=1}" 
           + @"${newline}Exception\:${pad:padding=14:inner=}${exception}}${newline}"; 

      // Define what sort of events to send to the file output target. 
      var MinOutputDebug = new LoggingRule("*", LogLevel.Debug, fileTarget); 
      config.LoggingRules.Add(MinOutputDebug); 

      // Set the configuration for the LogManager 
      LogManager.Configuration = config; 

      // Get the working instance of the logger. 
      _log = LogManager.GetLogger("LogEngine"); 
     } 

     /// <summary> 
     ///  Passes one log entry to the destination logger and associated exception information. 
     /// </summary> 
     /// <remarks> 
     ///  Use this form of the method when <see cref="NLog.LogLevel.Error" /> or 
     ///  <see cref="NLog.LogLevel.Fatal" /> is used. 
     /// </remarks> 
     /// <param name="caller"> 
     ///  <see cref="System.String" /> holding information about the calling method. 
     /// </param> 
     /// <param name="eventId"> 
     ///  Four character unique event ID as <see cref="System.String" />. 
     /// </param> 
     /// <param name="level"> 
     ///  The <see cref="NLog.LogLevel" /> value. 
     /// </param> 
     /// <param name="message"> 
     ///  The message to save to the log file as <see cref="System.String" />. 
     /// </param> 
     /// <param name="ex"> 
     ///  If this is an error log event, pass it as an <see cref="System.Exception" /> object. 
     /// </param> 
     internal void GenerateLog(string eventId, LogLevel level, string message, Exception ex) 
     { 
      // Values used for all events. 
      LogEventInfo logEvent = new LogEventInfo(); 
      logEvent.Properties["EventCode"] = eventId; 
      logEvent.Level = level; 
      logEvent.Message = message; 

      logEvent.Exception = ex; 

      // Actually write the log entry. 
      _log.Log(logEvent); 

      if (level.Equals(LogLevel.Error) || level.Equals(LogLevel.Fatal)) 
       Environment.Exit(Convert.ToInt32(eventId)); 
     } 

     /// <summary> 
     ///  Passes one log entry to the destination logger. 
     /// </summary> 
     /// <remarks> 
     ///  Use this form of the method when <see cref="NLog.LogLevel.Warn" /> or 
     ///  <see cref="NLog.LogLevel.Info" /> is used. 
     /// </remarks> 
     /// <param name="caller"> 
     ///  <see cref="System.String" /> holding information about the calling method. 
     /// </param> 
     /// <param name="eventId"> 
     ///  Four character unique event ID as <see cref="System.String" />. 
     /// </param> 
     /// <param name="level"> 
     ///  The <see cref="NLog.LogLevel" /> value. 
     /// </param> 
     /// <param name="message"> 
     ///  The message to save to the log file as <see cref="System.String" />. 
     /// </param> 
     internal void GenerateLog(string eventId, LogLevel level, string message) 
     { 
      // Values used for all events. 
      LogEventInfo logEvent = new LogEventInfo(); 
      logEvent.Properties["EventCode"] = eventId; 
      logEvent.Level = level; 
      logEvent.Message = message; 

      // Actually write the log entry. 
      _log.Log(logEvent); 
     } 

     #endregion Internal Methods 
    } 
} 

В основном, в стороне от застройщик, Singleton объектов и закрытых поля регионов, каждый из которых является новым, мне пришлось изменить мои звонки в CreateLogger () и GenerateLog(), чтобы повлиять на личное поле _log. Я также сделал GenerateLogger() перегруженным методом, но это не влияет на общее использование класса EventLog.

В каждом из моих других классов мне нужно было только изменить способ открытия моего регистратора. Я собирался сделать это на уровне методы, но решил сделать это на уровне класса:

internal class Program 
{ 
    #region Private Fields 

    private static readonly EventLog log = EventLog.logger; 

    #endregion Private Fields 
... 

Таким образом, для любого метода я нахожусь, если я хочу изменить протоколирование пути, я просто спросить мой протоколировать экземпляр для вызова CreateLogger (пути):

if (!path.Equals(null)) 
{ 
    sArgs.path = path.ToString().Trim(); 
    log.CreateLogger(sArgs.path); 
} 

Любых будущих вызовов, независимо от того, какого класса я нахожусь, мне просто нужно позвонить GenerateLog(), и я буду иметь правильный путь журнала.

Смежные вопросы