З точки зору дизайну, які найкращі практики ведення лісозаготівлі? [зачинено]


11

Я хочу додати журнал до програми, над якою зараз працюю. Я додав журнал раніше, це не проблема.

Але, з точки зору дизайну об'єктно-орієнтованою мовою, які найкращі практики ведення журналів, які відповідають OOP та шаблонам?

Примітка. Зараз я це роблю в C #, тому приклади в C # очевидно вітаються. Я також хотів би побачити приклади в Java та Ruby.


Редагувати: я використовую log4net. Я просто не знаю, який найкращий спосіб підключити його.

Відповіді:


6

Найкращою практикою, яку я рекомендував би, є використання log4j, а не прокатка власних. (Який був перенесений з Java на C # і Ruby, тому він стосується всіх трьох мов, які вас цікавлять.)

Якщо ви прочитаєте цю сторінку керівництва, ви знайдете кілька інших найкращих практик. Наприклад, легка, налаштована поза вашою програмою, можливість самостійно здійснювати реєстрацію вгору та вниз для різних частин програми тощо.


5

Де я працюю, ми пишемо багато .NET настільних додатків. Ми зазвичай реалізуємо 2 події в наших компонентах, один для реєстрації інформації, а інший для реєстрації винятків (хоча ми часто дозволяємо виняткам заміряти, а не піднімати окрему подію. Це залежить від ситуації). Використовуючи цю архітектуру, жодна наша бібліотека не повинна знати, як здійснювати ведення журналів чи як використовувати, зберігати чи обробляти інформацію. Потім у нас додаток обробляє події журналу таким чином, який відповідає цьому додатку. Кілька років тому ця архітектура зробила наш перехід від використання журналу MS Enterprise Library на компонент журналу BitFactory дуже простим переходом.


+1 за використання шаблону події / спостерігача: поміняйте спостерігача, ви змінили реєстрацію
Matthieu M.

2

Оскільки ви це робите в C #, я б рекомендував вам переглянути NLog та ElMAH. Їх можна ДУЖЕ легко встановити за допомогою NUGET. Я поклав декілька посилань на ті, що нижче, щоб ви могли отримати більше інформації.


2

Особисто я приймаю рамки журналу вибору (в моєму випадку Entlib, оскільки я працюю з .NET) і пишу AOP-аспект для ведення журналів.

Потім ви можете віднести будь-які методи / властивості / класи / простори імен та додати до них журнал, не захаращуючи джерело.


Це звучить дуже цікаво, але у мене є застереження щодо того, що ви могли б увійти в систему та наскільки інформаційним буде журнал (тобто більше, ніж "просто" інструментація методів). Хочеться побачити робочий приклад такого підходу, щоб побачити, що можна, а що не можна зробити. Тим більше, що я тільки починаю новий додаток і хотів би побачити, куди / як далеко можу це здійснити.
Мар'ян Венема

@marjan Venema: Документація після різкої публікації містить приклад аспекту, який реєструє вхід / вихід методу. doc.sharpcrafters.com/postsharp/2.0/##PostSharp.chm/html/… У випадку пост-гострого вбудованого коду з атрибута в джерело під час збирання, тому це не впливає на продуктивність, як це роблять інші.
Стівен Еверс

1

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

Ось приклад одного такого класу реєстрації, який обробляє всі події, які слід реєструвати, із вузького розділу нашого додатку (тих, що стосуються одного конкретного джерела вмісту, який ми імпортуємо).

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

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

Друга найважливіша частина - це відключення коду реєстрації від вашої основної логіки - це може зробити метод некрасивим і довгим і перекрученим дуже швидко.

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. Це означає, що я можу використовувати програмування, орієнтоване на аспекти, для коментування таких методів:

[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,
}

Для третьої категорії програмісти можуть просто створити один або кілька виділених методів "ведення журналів" та використовувати трасування для першої категорії. Методи ведення журналу не мають нічого іншого, ніж служать точкою заглушки, до якої можна застосувати правила відстеження.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.