С точки зрения дизайна, каковы лучшие практики для ведения журналов? [закрыто]


11

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

Но с точки зрения проектирования на объектно-ориентированном языке, каковы лучшие практики ведения журналов, которые следуют ООП и шаблонам?

Примечание: в настоящее время я делаю это в C #, поэтому примеры в C # явно приветствуются. Я также хотел бы видеть примеры на Java и Ruby.


Изменить: я использую log4net. Я просто не знаю, как лучше подключить его.

Ответы:


6

Лучшая практика, которую я бы порекомендовал, - это использовать log4j, а не катиться самостоятельно. (Который был перенесен с Java на C # и Ruby, поэтому он применим ко всем 3 интересующим вас языкам.)

Если вы прочитаете эту страницу руководства, вы обнаружите несколько других лучших практик. Например, легкий, настраиваемый вне вашего приложения, возможность включать и выключать ведение журналов для разных частей вашего приложения независимо, и так далее.


5

Там, где я работаю, мы пишем много настольных приложений .NET. Обычно мы реализуем 2 события в наших компонентах, одно для регистрации информации, а другое для регистрации исключений (хотя мы часто позволяем исключениям всплывать вместо того, чтобы вызывать отдельное событие. Это зависит от ситуации). Используя эту архитектуру, ни одна из наших библиотек не должна знать, как осуществляется ведение журнала или как информация используется, хранится или обрабатывается. Затем у нас есть приложение для обработки событий журналирования способом, который подходит для этого приложения. Несколько лет назад эта архитектура сделала наш переход от использования ведения журнала MS Enterprise Library к компоненту ведения журнала BitFactory очень простым переходом.


+1 за использование шаблона Event / Observer: измените наблюдателя, вы изменили запись
Matthieu M.

2

Поскольку вы делаете это в C #, я бы порекомендовал вам взглянуть на NLog и ElMAH. Их можно ОЧЕНЬ легко установить с помощью NUGET. Я поместил некоторые ссылки на них ниже, чтобы вы могли получить больше информации.


2

Лично я выбираю каркас ведения журнала (в моем случае Entlib, потому что я работаю с .NET) и пишу аспект AOP для ведения журнала.

Затем вы можете приписать любые методы / свойства / классы / пространства имен и добавить к ним логи, не загромождая источник.


Это звучит очень интересно, но у меня есть сомнения относительно того, что вы могли бы регистрировать и насколько информативным будет журнал (то есть больше, чем просто «инструментарий» методов). Хотелось бы увидеть рабочий пример такого подхода, чтобы увидеть, что можно, а что нельзя. Тем более, что я только начинаю над новым приложением и хотел бы посмотреть, где / как далеко я мог бы это нести.
Марьян Венема

@marjan Venema: В документации Post Sharp есть пример аспекта, который регистрирует вход / выход из метода. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/… В случае Post sharp он вплетает код из атрибута в исходный код во время сборки, поэтому он не влияет на производительность, как это делают некоторые другие.
Стивен Эверс

1

Система, над которой я сейчас работаю, использует управляемую событиями архитектуру и обмен сообщениями, так что большинство действий в нашей системе являются результатом команды и приводят к событиям (как отправляемые классы DTO, а не как стандартное событие делегата). Мы прикрепляем обработчики событий, единственной целью которых является обработка журналов. Этот дизайн помогает нам не повторяться, а также не нужно изменять существующий код для добавления / изменения функциональности.

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

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

Я скажу, однако, что запись соответствующей информации (особенно в режиме поиска Ctrl-F / find) является наиболее важной частью.

Вторая наиболее важная часть - это удаление кода регистрации из вашей основной логики - он может сделать метод уродливым, длинным и запутанным очень быстро.

public class MctLogger :
    IEventHandler<StoryImported>,
    IEventHandler<StoryScanned>,
    IEventHandler<SourceDirectoryMissing>,
    IEventHandler<SourceDirectoryAccessError>,
    IEventHandler<CannotCreateScannedStoryDirectory>,
    IEventHandler<CannotReadStoryDocument>,
    IEventHandler<StorySkippedPastCutoff>,
    IEventHandler<StorySkippedDuplicateUniqueId>,
    IEventHandler<StorySkippedByFilter>
{

    public void Observe(StoryImported e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryImported");
        log.Info("Story Unique ID: {Story.UniqueId}, Content ID: {ContentId}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StoryScanned e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StoryScanned");
        log.Info("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(SourceDirectoryMissing e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryMissing");
        log.Error("Directory: " + e.Directory);
    }

    public void Observe(SourceDirectoryAccessError e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.SourceDirectoryAccessError");
        log.Error(e.Exception, "Exception: " + e.Exception.Message);
    }

    public void Observe(CannotCreateScannedStoryDirectory e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotCreateScannedStoryDirectory");
        log.Error(e.Exception, "Directory: {Directory}, Exception: {Exception.Message}".SmartFormat(e));
    }

    public void Observe(CannotReadStoryDocument e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.CannotReadStoryDocument");
        if (e.Exception == null) {
            log.Warn("File: {FilePath}".SmartFormat(e));
        }
        else {
            log.Warn(e.Exception, "File: {FilePath}, Exception: {Exception.Message}".SmartFormat(e));
        }
    }

    public void Observe(StorySkippedPastCutoff e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedPastCutoff");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedDuplicateUniqueId e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedDuplicateUniqueId");
        log.Warn("Story Unique ID: {Story.UniqueId}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }

    public void Observe(StorySkippedByFilter e)
    {
        var log = Slf.LoggerService.GetLogger("RoboChef.Content.Mct.StorySkippedByFilter");
        log.Warn("Story Unique ID: {Story.UniqueId}, Reason: {Reason}, File: {FilePath}, Title: {Story.Headline}".SmartFormat(e));
    }
}

1

Как уже говорили другие, используйте log4jили log4netили какую-то другую хорошо построенную структуру ведения журналов.

Мне действительно не нравится, когда логирование кода мешает бизнес-логике. Вот почему я использую Log4PostSharp. Это означает, что я могу использовать Aspect Oriented Programming для аннотирования таких методов:

[Log(LogLevel.Info, "Counting characters.")]
int CountCharacters(string arg) 
{
    return arg.Length;
}

Или каждый метод в сборке, как это:

[assembly: Log(AttributeTargetTypes = "*", 
 EntryLevel = LogLevel.Debug, ExitLevel = LogLevel.Debug, 
 ExceptionLevel = LogLevel.Error)]

0

Я не уверен, что какая-либо структура делает это, но с точки зрения дизайна, я бы смоделировал информацию, которую нужно зарегистрировать в основном в трех категориях:

  1. трассировка уровня метода
  2. регистрация исключений
  3. Разработчики дополнительной информации во время выполнения считают, что это жизненно важно для расследования в случае сбоя во время выполнения (или любого поведения, связанного с ситуацией только во время выполнения).

Для первых двух категорий моя идеальная среда ведения журналов должна обрабатывать их как процесс пост-сборки и быть прозрачной для разработчиков. Было бы неплохо декларативно добавить запись в сборку, примерно так:

Trace YourNamespace.* [public methods|constructors]
{  # options
   ignore trivial methods,
   format: "{time stamp}: {method name}({parameter list})",
   condition: "{Context.Instance.UserID in (12432,23432)}",
}

Exception YourNamespace.Program.Main [unhandled exceptions]
{
  format: "{time stamp}: {Context.Instance.UserId} {exception}",
  action: die,  # options are throw, swallow,
}

Для 3-й категории программисты могут просто создать один или несколько специальных методов «регистрации» и использовать трассировку для первой категории. Методы ведения журнала не более чем служат тупиком, к которому можно применить правила трассировки.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.