【问题标题】:Setting up C# solution with multiple projects using NLog in Visual Studio在 Visual Studio 中使用 NLog 设置具有多个项目的 C# 解决方案
【发布时间】:2013-03-12 08:41:08
【问题描述】:

我在 Visual Studio 2012 中的解决方案目前包含两个项目:

  • DLL
  • WPF 应用程序(需要 DLL 的方法)

DLL 和 WPF 应用程序都使用NLog 进行日志记录。目前每个项目都包含NLog DLL 本身。

这是我不明白的:

  1. 我似乎没有必要在每个项目中包含相同的 NLog DLL。
  2. 但是,DLL 应该可以在其他解决方案中重用,即,NLog DLL 必须包含在 DLL 项目中。

设置 Visual Studio 解决方案和/或项目的适当方法是什么?

【问题讨论】:

  • 保持现状,我想说。每个想要使用它的程序集只有一个 DLL 副本。如果使用 DLL 的可执行程序集引用(例如)两个也使用 DLL 的其他类库程序集,您将仍然在可执行程序集的输出文件夹中仅获得一个 DLL。

标签: c# visual-studio-2012 dll project nlog


【解决方案1】:

您在使用它的所有项目中都需要该 DLL,而且您肯定需要将它与可执行文件(在您的情况下为 WPF 应用程序)的二进制文件一起部署,以便在运行时找到并使用它。

在我的所有项目中,我倾向于在日志引擎周围创建一个包装器,这样我就不需要引用和依赖特定的第三方日志记录 API,例如 Log4Net 或 NLog,所以我在任何地方都使用我的包装器日志记录类然后我只在包装类的项目和可执行项目中引用了日志记录程序集,以将程序集部署到 bin 文件夹。

希望这会有所帮助;-)

【讨论】:

    【解决方案2】:

    如果您的 DLL 只是您计划在各个项目之间共享的核心库,那么明智的做法是向该库添加 NLog 引用和包装代码,然后确保任何消费者应用程序(例如您的 WPF 项目)有一个与之关联的 NLog.config 文件。

    由于您使用的是 VS2012,我假设您也很可能使用 .NET 4.5,它允许您利用新的调用者信息属性。我为基本的 NLog 包装器编写了以下代码,并相信它在效率(不使用 StackTrace)和可用性之间取得了完美的平衡。

    using System;
    using System.Runtime.CompilerServices;
    using NLog;
    
    namespace ProjectName.Core.Utilities
    {
        /// <summary>
        /// Generic NLog wrapper.
        /// </summary>
        public static class Logger
        {
            /// <summary>
            /// Gets or sets the enabled status of the logger.
            /// </summary>
            public static bool Enabled
            {
                get { return LogManager.IsLoggingEnabled(); }
                set
                {
                    if (value)
                    {                    
                        while (!Enabled) LogManager.EnableLogging();
                    }
                    else
                    {
                        while (Enabled) LogManager.DisableLogging();
                    }
                }
            }
    
            /// <summary>
            /// Writes the diagnostic message at the Trace level.
            /// </summary>
            /// <param name="message"></param>
            /// <param name="exception"></param>
            /// <param name="callerPath"></param>
            /// <param name="callerMember"></param>
            /// <param name="callerLine"></param>
            public static void Trace(string message, Exception exception = null,
                [CallerFilePath] string callerPath = "",
                [CallerMemberName] string callerMember = "",
                [CallerLineNumber] int callerLine = 0)
            {
                Log(LogLevel.Trace, message, exception, callerPath, callerMember, callerLine);
            }
    
            /// <summary>
            /// Writes the diagnostic message at the Debug level.
            /// </summary>
            /// <param name="message"></param>
            /// <param name="exception"></param>
            /// <param name="callerPath"></param>
            /// <param name="callerMember"></param>
            /// <param name="callerLine"></param>
            public static void Debug(string message, Exception exception = null,
                [CallerFilePathAttribute] string callerPath = "",
                [CallerMemberName] string callerMember = "",
                [CallerLineNumber] int callerLine = 0)
            {
                Log(LogLevel.Debug, message, exception, callerPath, callerMember, callerLine);
            }
    
            /// <summary>
            /// Writes the diagnostic message at the Info level.
            /// </summary>
            /// <param name="message"></param>
            /// <param name="exception"></param>
            /// <param name="callerPath"></param>
            /// <param name="callerMember"></param>
            /// <param name="callerLine"></param>
            public static void Info(string message, Exception exception = null,
                [CallerFilePathAttribute] string callerPath = "",
                [CallerMemberName] string callerMember = "",
                [CallerLineNumber] int callerLine = 0)
            {
                Log(LogLevel.Info, message, exception, callerPath, callerMember, callerLine);
            }
    
            /// <summary>
            /// Writes the diagnostic message at the Warn level.
            /// </summary>
            /// <param name="message"></param>
            /// <param name="exception"></param>
            /// <param name="callerPath"></param>
            /// <param name="callerMember"></param>
            /// <param name="callerLine"></param>
            public static void Warn(string message, Exception exception = null,
                [CallerFilePathAttribute] string callerPath = "",
                [CallerMemberName] string callerMember = "",
                [CallerLineNumber] int callerLine = 0)
            {
                Log(LogLevel.Warn, message, exception, callerPath, callerMember, callerLine);
            }
    
            /// <summary>
            /// Writes the diagnostic message at the Error level.
            /// </summary>
            /// <param name="message"></param>
            /// <param name="exception"></param>
            /// <param name="callerPath"></param>
            /// <param name="callerMember"></param>
            /// <param name="callerLine"></param>
            public static void Error(string message, Exception exception = null,
                [CallerFilePathAttribute] string callerPath = "",
                [CallerMemberName] string callerMember = "",
                [CallerLineNumber] int callerLine = 0)
            {
                Log(LogLevel.Error, message, exception, callerPath, callerMember, callerLine);
            }
    
            /// <summary>
            /// Writes the diagnostic message at the Fatal level.
            /// </summary>
            /// <param name="message"></param>
            /// <param name="exception"></param>
            /// <param name="callerPath"></param>
            /// <param name="callerMember"></param>
            /// <param name="callerLine"></param>
            public static void Fatal(string message, Exception exception = null,
                [CallerFilePathAttribute] string callerPath = "",
                [CallerMemberName] string callerMember = "",
                [CallerLineNumber] int callerLine = 0)
            {            
                Log(LogLevel.Fatal, message, exception, callerPath, callerMember, callerLine);
            }
    
            /// <summary>
            /// Writes the specified diagnostic message.
            /// </summary>
            /// <param name="level"></param>
            /// <param name="message"></param>
            /// <param name="exception"></param>
            /// <param name="callerPath"></param>
            /// <param name="callerMember"></param>
            /// <param name="callerLine"></param>
            private static void Log(LogLevel level, string message, Exception exception = null, string callerPath = "", string callerMember = "", int callerLine = 0)
            {
                // get the source-file-specific logger
                var logger = LogManager.GetLogger(callerPath);
    
                // quit processing any further if not enabled for the requested logging level
                if (!logger.IsEnabled(level)) return;
    
                // log the event with caller information bound to it
                var logEvent = new LogEventInfo(level, callerPath, message) {Exception = exception};
                logEvent.Properties.Add("callerpath", callerPath);
                logEvent.Properties.Add("callermember", callerMember);
                logEvent.Properties.Add("callerline", callerLine);
                logger.Log(logEvent);
            }
        }
    }
    

    然后尝试将其放入 NLog.config 中目标之一的布局字段中,以获取详细的调用者信息。

    ${event-context:item=callerpath}:${event-context:item=callermember}(${event-context:item=callerline})
    

    【讨论】:

    • 如果您添加对“Microsoft BCL 构建组件”的引用(通过 NuGet),也可以针对 .net 4.0
    • 为什么 Enabled 的 setter 使用 while 循环来检查 Enabled 的当前状态?如果 value 为 true,则仅调用 LogManager.EnableLogging(),否则调用 LogManager.DisableLogging() 是否足够?
    • 刚刚发现有一个日志启用计数器,启用和禁用分别增加或减少该计数器。我觉得命名有点混乱,希望其他人能从中吸取教训。
    【解决方案3】:

    你最好抽象出你的日志机制的使用。我在this blog post 中对此进行了描述,它是关于 log4net 的,但无论您使用什么框架,原理都是相同的。在任何情况下,您在使用它的每个项目中都需要日志程序集,但是通过抽象它很容易用其他东西替换它(例如在测试时)。日志记录是基础设施,因此您可以将接口和具体实现放在一个基础设施项目中,并从您要记录的项目中引用它。

    【讨论】:

    • 这很好,我同意,但如果 DI 无法实现怎么办。我想在多个项目中的整个解决方案中保持单独的日志记录和引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-10
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多