Как выглядит класс Wrapper Log4Net?
Я искал фреймворк для .net(С#) и решил дать log4net перейти после прочтения нескольких вопросов/ответов в потоке stackoverflow. Я вижу, что люди снова и снова упоминают, что используют класс оболочки для log4net, и мне интересно, как это будет выглядеть.
У меня есть мой код, разделенный на разные проекты (доступ к данным/бизнес/webservice/..).
Как выглядит класс оболочки log4net? Будет ли класс-обертка включен во все проекты? Должен ли я объединять его как отдельный проект?
Должна ли оболочка быть одноэлементным классом?
Ответы
Ответ 1
По существу вы создаете интерфейс, а затем конкретную реализацию этого интерфейса, который напрямую переносит классы и методы Log4net. Дополнительные системы регистрации можно обернуть, создав более конкретные классы, которые объединяют другие классы и методы этих систем. Наконец, используйте factory для создания экземпляров ваших оберток на основе настроек конфигурации или строки изменения кода. (Примечание: вы можете стать более гибким и сложным - используя контейнер Inversion of Control, например StructureMap.)
public interface ILogger
{
void Debug(object message);
bool IsDebugEnabled { get; }
// continue for all methods like Error, Fatal ...
}
public class Log4NetWrapper : ILogger
{
private readonly log4net.ILog _logger;
public Log4NetWrapper(Type type)
{
_logger = log4net.LogManager.GetLogger(type);
}
public void Debug(object message)
{
_logger.Debug(message);
}
public bool IsDebugEnabled
{
get { return _logger.IsDebugEnabled; }
}
// complete ILogger interface implementation
}
public static class LogManager
{
public static ILogger GetLogger(Type type)
{
// if configuration file says log4net...
return new Log4NetWrapper(type);
// if it says Joe Logger...
// return new JoesLoggerWrapper(type);
}
}
И пример использования этого кода в ваших классах (объявлен как статическое поле readonly):
private static readonly ILogger _logger =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
Вы можете получить тот же эффект, который отличается большей производительностью:
private static readonly ILogger _logger =
LogManager.GetLogger(typeof(YourTypeName));
Первый пример считается более ремонтопригодным.
Вы не хотите создавать Singleton для обработки всех протоколов, поскольку журналы Log4Net регистрируются для вызывающего типа; его намного более чистый и полезный, чтобы каждый тип использовал свой собственный регистратор, а не просто видел один тип в файле журнала, сообщающий обо всех сообщениях.
Поскольку ваша реализация должна быть достаточно многоразовой (другие проекты в вашей организации), вы можете сделать ее собственной сборкой или в идеале включить ее в свою собственную сборку/сборку/сборку. Не переопределяйте классы отдельно в каждой вашей сборке бизнес/данных/пользовательского интерфейса, которые не поддерживаются.
Ответ 2
Предполагая, что вы столкнулись с чем-то вроде ответа cfeduke выше, вы также можете добавить перегрузку к LogManager
следующим образом:
public static ILogger GetLogger()
{
var stack = new StackTrace();
var frame = stack.GetFrame(1);
return new Log4NetWrapper(frame.GetMethod().DeclaringType);
}
Таким образом, в вашем коде вы можете просто использовать:
private static readonly ILogger _logger = LogManager.GetLogger();
вместо любого из них:
private static readonly ILogger _logger =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger _logger =
LogManager.GetLogger(typeof(YourTypeName));
Это фактически эквивалентно первой альтернативе (т.е. той, которая использует MethodBase.GetCurrentMethod().DeclaringType
), только немного проще.
Ответ 3
Какие преимущества вы планируете получить от написания обертки для log4net. Я бы рекомендовал сначала освоить классы log4net, прежде чем писать обертку вокруг них. cfeduke прав в своем ответе о том, как писать указанную оболочку, но если вам не нужно добавлять фактическую функциональность к своему примеру, оболочке удалось бы только замедлить процесс регистрации и добавить сложность для будущих сопровождающих. Это особенно актуально, когда инструменты рефакторинга, доступные в .Net, делают такие изменения очень легкими.
Ответ 4
Я успешно изолировал зависимость log4net в одном проекте. Если вы намереваетесь сделать то же самое, вот как выглядит мой класс-оболочка:
using System;
namespace Framework.Logging
{
public class Logger
{
private readonly log4net.ILog _log;
public Logger()
{
_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}
public Logger(string name)
{
_log = log4net.LogManager.GetLogger(name);
}
public Logger(Type type)
{
_log = log4net.LogManager.GetLogger(type);
}
public void Debug(object message, Exception ex = null)
{
if (_log.IsDebugEnabled)
{
if (ex == null)
{
_log.Debug(message);
}
else
{
_log.Debug(message, ex);
}
}
}
public void Info(object message, Exception ex = null)
{
if (_log.IsInfoEnabled)
{
if (ex == null)
{
_log.Info(message);
}
else
{
_log.Info(message, ex);
}
}
}
public void Warn(object message, Exception ex = null)
{
if (_log.IsWarnEnabled)
{
if (ex == null)
{
_log.Warn(message);
}
else
{
_log.Warn(message, ex);
}
}
}
public void Error(object message, Exception ex = null)
{
if (_log.IsErrorEnabled)
{
if (ex == null)
{
_log.Error(message);
}
else
{
_log.Error(message, ex);
}
}
}
public void Fatal(object message, Exception ex = null)
{
if (_log.IsFatalEnabled)
{
if (ex == null)
{
_log.Fatal(message);
}
else
{
_log.Fatal(message, ex);
}
}
}
}
}
И не забудьте добавить это в AssemblyInfo.cs
проекта сопряжения (мне понадобилось несколько часов, чтобы найти это)
[assembly: log4net.Config.XmlConfigurator(Watch = true, ConfigFile = "log4net.config")]
И поместите свой конфигурационный файл log4net в файл log4net.config
, установите его как Content
, Copy Always
Ответ 5
Я понимаю, что класс-оболочка для log4net будет статическим классом, который позаботится об инициализации объекта ведения журнала из app.config/web.config или по коду (например, интеграция с NUnit).
Ответ 6
возможное использование для обертки log4net может быть классом, который получает вызывающий класс и метод через отражение, чтобы получить представление о том, где произошла запись в журнале. по крайней мере, я часто использую это.
Ответ 7
Alconja, мне нравится ваша идея использования stacktrace для возврата к вызывающему методу. Я думал о дальнейшем инкапсуляции вызовов, чтобы не просто получить объект журнала, но и выполнить фактическое выполнение журнала. Я хочу статический класс, который обрабатывает ведение журнала, путем абстрагирования от конкретной используемой реализации. То есть
LoggingService.LogError("my error message");
Таким образом, мне нужно изменить внутренности статического класса, если позже я решу использовать другую систему регистрации.
Итак, я использовал вашу идею, чтобы получить вызывающий объект, используя трассировку стека:
public static class LoggingService
{
private static ILog GetLogger()
{
var stack = new StackTrace();
var frame = stack.GetFrame(2);
return log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType);
}
public static void LogError(string message)
{
ILog logger = GetLogger();
if (logger.IsErrorEnabled)
logger.Error(message);
}
...
}
Кто-нибудь видит проблему с этим подходом?
Ответ 8
Существуют фреймворки, такие как Prism Library для WPF, которые способствуют использованию facade для структуры ведения журнала по вашему выбору.
Это пример, который использует log4net:
using System;
using log4net;
using log4net.Core;
using Prism.Logging;
public class Log4NetLoggerFacade : ILoggerFacade
{
private static readonly ILog Log4NetLog = LogManager.GetLogger(typeof (Log4NetLoggerFacade));
public void Log(string message, Category category, Priority priority)
{
switch (category)
{
case Category.Debug:
Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Debug, message, null);
break;
case Category.Exception:
Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Error, message, null);
break;
case Category.Info:
Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Info, message, null);
break;
case Category.Warn:
Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Warn, message, null);
break;
default:
throw new ArgumentOutOfRangeException(nameof(category), category, null);
}
}
}
Обратите внимание, что, указав callerStackBoundaryDeclaringType
, вы все равно можете получить имя класса вызывающего, выдающего запрос регистрации. Все, что вам нужно сделать, это включить %C %M
в шаблон преобразования:
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %C.%M - %message%newline" />
</layout>
Однако, как предупреждает документация, генерация информации класса вызывающего абонента выполняется медленно, поэтому ее необходимо использовать с умом.
Ответ 9
Я знаю, что этот ответ запоздал, но он может помочь кому-то в будущем.
Похоже, вам нужен программный API, который дает вам XQuiSoft Logging. Вам не нужно указывать, какой регистратор вы хотите использовать с XQuiSoft. это так просто:
Log.Write(Level.Verbose, "источник", "категория", "ваше сообщение здесь" );
Затем через конфигурацию вы направляете сообщения по источнику, категории, уровню или любому другому настраиваемому фильтру в разные местоположения (файлы, электронные письма и т.д.).
См. эту статью для введения.