Настройка решения С# с несколькими проектами с использованием NLog в Visual Studio

Мое решение в Visual Studio 2012 в настоящее время содержит два проекта:

  • DLL
  • Приложение WPF (для которого требуются методы DLL)

Оба приложения DLL и WPF используют NLog для ведения журнала. В настоящее время каждый проект содержит самую DLL NLog.

Вот что я не понимаю:

  • Мне кажется ненужным, включая идентичную DLL NLog в каждом проекте.
  • Однако DLL должна быть повторно использована в других решениях, т.е. как-то DLL файл NLog должен содержаться в проекте DLL.

Каким будет подходящий способ настройки решения и/или проектов Visual Studio?

Ответ 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})

Ответ 3

Лучше абстрагироваться от использования вашего механизма ведения журнала. Я описал это в этом сообщении в блоге, это о log4net, но это тот же принцип, какой бы рамки вы ни использовали. В любом случае вам нужна сборка журналов в каждом проекте, где вы его используете, но, абстрагируя его, легко заменить его чем-то другим (например, при тестировании). Ведение журнала - это инфраструктура, поэтому вы должны поместить интерфейсы и конкретную реализацию в проект инфраструктуры, а также ссылаться на один из проектов, в которых вы хотите регистрироваться.