【问题标题】:How to replace NLog Logger with a new instance of Logger?如何用新的 Logger 实例替换 NLog Logger?
【发布时间】:2016-08-10 13:17:35
【问题描述】:

所以,刚刚开始使用 NLog。我正在做一个编程实现,我试图设置一个可以导入任何项目的类。该类有两个方法:CreateLogger() 和 GenerateLog()。这是整个课程:

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() 时,事情是如何工作的,我不传递任何参数,并且 ${basedir} 值用于生成初始日志记录:

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() 返回的对象。它是我需要将 Logger 连接到的 ${basedir} 的替代品。如果路径不为空,则将其转换为字符串并将其存储到实例化为“sArgs”的单例类中。这里有一些注释掉的代码来显示我是如何尝试解决这个问题的。

好的,所以我看到的是在最后一个代码块中(当我将“log”设置为由 CreateLogger(sArgs.path) 生成的新实例时)我可以看到我在日志对象中的日志记录路径实际上是更新。但是,当我第一次有机会记录事件时,它仍在使用旧的 Logger 实例(因此 ${basedir} 仍在使用,而不是 sArgs.path)。

我的问题是,我缺少什么来保持对“日志”的更改,而在调试器中步进我的代码时,我可以清楚地看到它实际上成为 Logger 对象的位置?还是我做的 EventLog 类完全错了?

感谢您对此问题提供的任何见解。

【问题讨论】:

    标签: c# logging nlog


    【解决方案1】:

    对于使用private static Logger log = EventLog.CreateLogger(); 的每个类,只要不希望使用EventLogclass 中定义的默认baseDir,就需要更改baseDir。

    您没有提供单例类 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);
            }
        }
    }
    

    运行代码会产生以下输出:

    Log EventId 1 将被写入C:\Temp\NLog\Default\ConsoleApplication1.vshost.exe\logs 日志 EventId 2 将被写入C:\Temp\NLog\New\ConsoleApplication1.vshost.exe\logs

    Class1.cs 中的 LogEventId 3 将被写入C:\Temp\NLog\Default\ConsoleApplication1.vshost.exe\logs,因为在 Class1.cs 中初始化日志时,使用了默认路径。如果要更改 Class1.cs(以及后续类)中日志的 baseDir,则需要单独更新路径。

    希望这会有所帮助。

    【讨论】:

    • 谢谢@Riaan。不知道为什么我没有看到,但这正是我在更新路径后生成日志的下一次机会确实发生在另一个类中发生的情况。那么,这是否意味着我应该将我的 EventLogger 类(如您所称)转换为单例类?这意味着当我输入一个我希望登录的方法时,我需要链接到单例实例(就像我正在使用 sArgs 来保存我的程序参数一样)。
    • 我想我还需要更新我的布局,因此对 ${callsite:fileName=true:includeSourcePath=false:skipFrames=1} 的调用会将 skipFrames 更新为等于 2 所以实际方法正在发生的错误被保留。
    • 实际上,现在我想起来了,也许单例类不是这样做的正确方法。我真正需要做的是想办法让 Class1 检查它是否需要更新其配置。
    • @breusshe,通常您为每个类配置一个私有静态 Logger 日志。你有你的实现很好,在这种情况下我会做的是,如果你对于一个特定的类需要改变路径,在构造函数中添加方法调用来改变那里的路径,或者使用一些依赖注入以实例化您的 logger/Args 类并在您的类中使用它。
    【解决方案2】:

    感谢@Riann 的帮助并添加了 cmets。实际上,我将它作为一个单例类工作,而不会影响 ${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
        }
    }
    

    基本上,除了Contructor、Singleton Objects 和Private Fields 区域之外,所有这些都是新的,我不得不更改我在CreateLogger() 和GenerateLog() 中的调用以影响私有字段_log。我也确实让 GenerateLogger() 成为一个重载方法,但这不会影响 EventLog 类的整体使用。

    在我的其他每个课程中,我只需要更改打开记录器的方式。我打算在方法级别执行此操作,但决定在类级别执行:

    internal class Program
    {
        #region Private Fields
    
        private static readonly EventLog log = EventLog.logger;
    
        #endregion Private Fields
    ...
    

    所以,对于我使用的任何方法,如果我想更改我的日志记录路径,我只需让我的日志实例调用 CreateLogger(path):

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

    以后的任何调用,无论我在哪个班级,我只需要调用 GenerateLog(),我就会有正确的日志路径。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-05
      • 1970-01-01
      • 2013-04-06
      • 1970-01-01
      • 2019-01-16
      相关资源
      最近更新 更多