Гнучка розробка інтерфейсу журналу в C #

Я хочу написати власні класи Logging (у C #), які реалізують стандартний інтерфейс, який я можу викликати з будь-якої частини коду.

Моя ідея полягає в тому, щоб багато класів журналу реалізовували інтерфейс Logger, кожен для свого конкретного призначення журналу, наприклад, FileLogger буде реалізовувати реєстрацію у файлі, реєстратор TextBox буде реалізовувати реєстрацію в Multi Line TextBox у формі, DBLogger реалізувати вхід до таблиці бази даних тощо

Крім того, кожен клас логгера може мати вкладені журнали або ланцюгові класи реєстратора, так що один метод виклику Log() з коду програми може реєструвати повідомлення в декількох місцях призначення; наприклад, журнал у файл і текстове поле у ​​формі в одному дзвінку.

Проблема, з якою я стикаюся, це:

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

Коли я викликаю logger.Log (повідомлення), деякі повідомлення можуть не застосовуватися до певного призначення журналу. Наприклад, деяке повідомлення може бути призначене для реєстрації тільки в запущеному файлі журналу або текстовому полі прогресу, але не в текстовому полі користувача, і навпаки.

Оскільки реєстратори будуть приковані так, що один виклик функції може входити в усі необхідні пункти призначення, як може конкретний реєстратор визначити, що повідомлення журналу не призначене для нього, і, отже, ігнорувати повідомлення журналу?

Мій інтерфейс журналу зразків:

public interface Logger
{
    public void Log(string msg);
    public void Log(string msgType, string msg);
    public void InitLogSession();
    public void EndLogSession();
    public void AddLogger(Logger chainedLogger);
    public void RemoveLogger(Logger chainedLogger);
}

public class FileLogger : Logger
{
      //implement methods
}

public class TextBoxLogger : Logger
{
      //implement methods
}

public class DBLogger : Logger
{
      //implement methods
}

РЕДАГУВАТИ 1:

Якщо бути точнішим, то можуть бути 4 реєстратора: 2 файлових реєстратора і 2 текстових реєстратора. Допускається, зокрема, повідомлення для 1 з текстових логгерів і 1 з файлових реєстраторів; як слід розробити мою конструкцію?

EDIT 2: Будь ласка, не пропонуйте існуючі структури журналювання. Я просто хочу написати її самостійно!

EDIT 3: В порядку. У мене є дизайн. Надайте свій відгук і, можливо, заповніть прогалини.

Переглянутий інтерфейс:

public interface Logger
{
    public void Log(string msg);
    public void Log(string msgType, string msg);
    public void Log(int loggerIds, string msg);
    public void Log(int loggerIds, string msgType, string msg);
    public void InitLogSession();
    public void EndLogSession();
    public int getLoggerId();
}

public enum LoggerType
{
    File,
    TextBox
};

public class LoggerFactory
{
    public Logger getLogger(LoggerType loggerType)
    {

    }
}

Клас LoggerFactory буде єдиним способом створити примірник реєстратора. Цей клас призначить унікальний ідентифікатор кожному екземпляру реєстратора. Цей унікальний ідентифікатор буде силою 2. Приклад: 1-й реєстратор отримає ідентифікатор 1, другий отримає ідентифікатор 2, третій - 4, а 4-й - 8 і так далі.

Повернутий об'єкт реєстратора може бути типувати до певного класу, а інші значення, такі як filePath, textbox і т.д., можуть бути встановлені абонентом, або ж я можу мати кілька методів у LoggerFactory: один для кожного типу реєстратора, який буде приймати конкретні параметри .

Отже, припустимо, що ми маємо 4 реєстратора з ідентифікаторами 1,2,4,8. Певне повідомлення, яке має бути оброблено першим і третім реєстратором (тобто ідентифікатори реєстратора 1 і 4), має бути зареєстровано за допомогою функції:

    public void Log(int loggerIds, string msg);

Значення, яке буде передано до loggerIds, має бути "0101". Кожен реєстратор перевірить, чи є його біт ідентифікатора реєстратора ON. Якщо так, тільки тоді він запише повідомлення.

Тепер у функції підписів, я згадав int тип, але який є специфічним оптимізований тип для виконання біт маніпуляцій і порівнянь?

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

Примітка: В даний час я все ще на .NET 2.0. Якщо можливо, запропонуйте рішення в межах .NET 2.0, інакше добре, я можу перейти до більш високих версій.

ПРОТИ цієї конструкції: Кожен клас, який потрібно зареєструвати, повинен знати про всі наявні реєстратори, створені за допомогою програми, і відповідно налаштувати бітовий шаблон. Будь-які ідеї, як мати вільно пов'язаний дизайн?

3
З точки зору роботи, я впевнений, що вона працюватиме. Але є деякі прогалини, наприклад, int не є правильним типом даних. Я просто хочу трохи розбити. Тому кожному реєстратору буде виділено конкретний біт у бітовому шаблоні. Який тип даних слід використовувати? По-друге, з точки зору проектування, чи є це хорошим дизайном?
додано Автор AllSolutions, джерело
Мені подобається робити це самостійно. Я не хочу вивчати бібліотеку третьої сторони. Але як унікальні імена допоможуть реєстратору вирішити, чи слід реєструвати певне повідомлення?
додано Автор AllSolutions, джерело
До речі, я переглядав інший потік, запропонований вами, де ви задали питання. Дизайн, який я запропонував - чи не буде він працювати навіть на запитання, яке ви запитали? Якщо ви хочете накопичувати повідомлення про стан і реєструвати їх одночасно пізніше, ви можете ввести метод Flush (). На зворотному боці, кожен клас, який потрібно зареєструвати, повинен знати про всі доступні реєстратори, створені за допомогою програми, і відповідно налаштувати бітовий шаблон.
додано Автор AllSolutions, джерело
Але навіть у вашому іншому рішенні кожен клас повинен знати про GUIMessageQueue, тому, напевно, дуже важко мати вільно пов'язаний код.
додано Автор AllSolutions, джерело
Замість приєднання інших екземплярів ILogger , перевірте Ланцюг шаблону відповідальності . Кожен реєстратор міг би додатково споживати інший логгер, передаючи повідомлення вниз. Нещодавно я зробив подібне і маю клас NullLogger , який емулює /dev/null . У мене є деякі реєстратори декоратора, такі як TimestampLogger і DebugLogger , які дозволяють додавати їх за бажанням (споживаючи інший ILogger ).
додано Автор Erik, джерело
Думаю, вам потрібно спробувати переглянуту ідею. Якщо ви використовуєте модульне тестування, ви повинні мати змогу макетів заводу та інших класів і перевірити, як вони будуть працювати
додано Автор ScruffyDuck, джерело
Ви хочете, щоб це як вправа? Тому що існують рамки, які вже роблять це досить добре (наприклад, log4net ). Ці фреймворки використовують конфігураційні файли, щоб визначити, як будуть діяти індивідуальні реєстратори (якщо і де вони надсилатимуть свій вивід). Кожен реєстратор у вашому коді повинен мати унікальне ім'я, на основі якого визначається вихід (або кілька виходів).
додано Автор Groo, джерело
ну ... яка логіка, яку ви, використовуєте, щоб вирішити, який реєстратор реєструє, яке повідомлення?
додано Автор Paolo Falabella, джерело
Думаю, це правильно.
додано Автор Andreas H., джерело

5 Відповіді

Чому ви не дивитесь (або навіть не використовуєте) існуючу структуру реєстрації, наприклад log4net або NLog .

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

8
додано
Правила +1 log4net!
додано Автор Luca, джерело
Категорії повідомлень/Імена лічильників/Рівні реєстратора - як будь-який з них може обробляти декілька конкретних напрямків на рівні повідомлення? Чи можете ви сказати мені, чи є 4 реєстратора: 2 реєстратора файлів і 2 текстові реєстратори, і конкретне повідомлення призначене для 1 з текстових логгерів і 1 з файлових реєстраторів; як ви зможете вирішити це рішення?
додано Автор AllSolutions, джерело
@devdigital, я опублікував наступне запитання про налаштування TextBoxAppender через файл XML: xml файл "> stackoverflow.com/questions/14114614/… . Ви можете поглянути на це?
додано Автор AllSolutions, джерело
@devdigital, "Потім ви можете зіставити їх з одним або декількома" цілями "". Чи можу я представити власні рівні журналу за допомогою фреймворка log4net, і я можу зіставити даний рівень з кількома цілями. У документі з функціями log4net я не бачив жодного апендера для TextBox.
додано Автор AllSolutions, джерело
Якщо бути точнішим, то можуть бути 4 реєстратора: 2 файлових реєстратора і 2 текстових реєстратора. Допускається, зокрема, повідомлення для 1 з текстових логгерів і 1 з файлових реєстраторів; як з цим впоратися?
додано Автор AllSolutions, джерело
Але коли повідомлення призначене для 2 з 4 реєстраторів, яким чином одне поле "Категорія" допоможе 4 реєстраторам вирішити, чи призначено це повідомлення для них чи ні? Чи можете ви навести приклад псевдокоду?
додано Автор AllSolutions, джерело
Мені подобається робити це самостійно. Я не хочу вивчати бібліотеку третьої сторони. Рівні журналу не допоможуть, оскільки різні повідомлення одного і того ж рівня журналу можуть потребувати реєстрації лише у вибраних напрямках.
додано Автор AllSolutions, джерело
Ви можете ( l4ndash.com/& hellip; ) але вам краще використовувати ім'я реєстратора для диференціювання. Щоб вивести текст у текстовий ящик, потрібно написати спеціальний додаток, наприклад, triagile.blogspot.co.uk/2010/12/ & hellip;
додано Автор devdigital, джерело
Напр. Ви можете надіслати повідомлення категорії або спеціальному реєстратору, а потім у вашій конфігурації зіставити будь-яке повідомлення з назви/категорії "спеціального" реєстратора до цільового вікна, яке називається "textbox1", і цільового файлу, який називається "file1".
додано Автор devdigital, джерело
Якщо ви пишете власну структуру реєстрації (яка, схоже, багато роботи, коли вже доступні популярні фреймворки), ви маєте повний контроль. Ви можете використовувати файл конфігурації (або код за допомогою API) для відображення категорій повідомлень/імен/реєстраторів на окремі цілі. Цього можна досягти за допомогою log4net/NLog, використовуючи різні реалізації ILogger, які є оболонками для викликів log4net/NLog, але встановлює відповідне ім'я реєстратора.
додано Автор devdigital, джерело
Вам доведеться диференціювати кожне повідомлення, яке вимагає різних цілей, якщо вони мають один і той же рівень реєстрації та ім'я реєстратора. Наприклад, до кожного повідомлення можна додати тип категорії. Якщо ви хочете використовувати існуючу структуру, ви можете побудувати власні імена реєстраторів, а не використовувати ім'я типу. Напр. у вас може бути декілька протоколів ILogger, які використовує клас, причому кожна реалізація використовує інше ім'я реєстратора.
додано Автор devdigital, джерело
Не повторюйте колеса. Перейти на NLog або log4net (обидва фрейми досить зрілі, в корпоративних ми використовуємо log4net) і AOP :)
додано Автор Simon, джерело

Я написав свій власний реєстратор деякий час назад. Щоб бути чесним це було не таке гарне як ці наявні для вільного та я здійснив що я намагався re-винайти колесо що вже навколо!

Я бачу, що ви хочете написати власний код, але все одно може бути ідея подивитися на рішення з відкритим кодом і, можливо, використовувати їх або змінити їх для ваших власних потреб

Зараз я використовую TracerX: http: //www.codeproject .com/Статті/23424/TracerX-Logger-і-Viewer-for-NET Це проект з відкритим вихідним кодом, завдяки якому його легко змінити. Інші згадані реєстратори також добре.

EDIT

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

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

Тому

  1. create an abstract message class or interface that has a process method.
  2. create a number of message types inheriting from the abstract class or interface that represent the different types of logging you want to carry out. The process method could determine where to send them.
  3. Consider using a factory to create the message type you need during runtime Томуyou don't need to decide what types you will need in advance
  4. When you generate a log message use the process message to route the message to the loggers you want it to go to
1
додано
Просто вийшло, щоб побачити ваш відредагований відповідь зараз ... Це корисно і я все ще думаю про це .. тим, ви можете побачити запропонував дизайн я розмістив в моєму редагується питання і заповнити відсутні прогалини?
додано Автор AllSolutions, джерело
Справа в тому, що я не знаю "логгер груп" заздалегідь, інакше було б дуже легко. Під час роботи кожен функціональний блок вирішить, яке повідомлення має перейти до всіх напрямків!
додано Автор AllSolutions, джерело
Навіть клас обгортки - як він повинен вирішити, що повідомлення має перейти на Logger 1, Logger 2, а не на Logger 3 і Logger 4 ?? "Іншим можливим варіантом є відкриття коду TracerX і додавання вашої вимоги" - Якщо я знаю, що я повинен зробити, я міг би зробити це в моєму власному коді, оскільки мені не потрібно використовувати розширені функції TracerX
додано Автор AllSolutions, джерело
Я просто пройшов через TracerX. Здається, це чудовий продукт, але просто не вистачає 1 функції, яку я хочу. керувати реєстраційними напрямками індивідуально на кожному рівні повідомлень! Будь-які ідеї?
додано Автор AllSolutions, джерело
Кожен тут радить використовувати існуючу систему реєстрації. Але чи може існуюча система реєстрації виконувати мою заявлену вимогу? Припустимо, що 1 журнал повідомлення призначений для 2 з 4 реєстраторів (і не заснований на рівні Logging) - чи може існуюча структура обробляти такі сценарії?
додано Автор AllSolutions, джерело
msgstr "створити ряд типів повідомлень, що успадковуються від абстрактного класу або інтерфейсу, що представляють різні типи реєстрації, які потрібно виконати. Метод процесу може визначити, куди їх надсилати." - Але коли одне повідомлення має перейти до декількох реєстраторів, як можна визначити пункт призначення на основі класу типу повідомлення?
додано Автор AllSolutions, джерело
Дякуємо, надіслали вам листа, і ми можемо мати додаткову дискусію про пошту.
додано Автор AllSolutions, джерело
Ну, не міг отримати ваш ідентифікатор пошти з профілю .. він згадує тільки адресу веб-сайту.
додано Автор AllSolutions, джерело
jon AT scruffyduck DOT org DOT uk
додано Автор ScruffyDuck, джерело
Я бачу, що у вас є певна проблема. У мене є подібна проблема, оскільки я хочу надсилати повідомлення про стан користувачеві окремо від журналу програм. Я можу піти на окремий клас для обробки даних користувача. Чи можу я просто запропонувати вам поглянути на статтю для TracerX, яку я розмістив вище? Він використовує рівень Logging, щоб розділити те, куди йде, але код для переглядача дозволяє здійснювати ряд пошуків. Я перехоплюю запити журналу і використовую невеликий клас, щоб відправити їх у потрібне місце. Знову ж таки, це може бути не те, що ви хочете, оскільки воно досить тісно пов'язане з кодом.
додано Автор ScruffyDuck, джерело
У мене вистачило простору. Я думаю, що ми всі говоримо, щоб поглянути на існуючі рамки для основної нудної роботи сантехніка і розширити це для ваших потреб, а не починати з нуля. Однак я бачу, що це може не спрацювати.
додано Автор ScruffyDuck, джерело
Я поняття не маю, якщо це допоможе, але те, що я роблю, посилає мою реєстрацію інформації на невеликий статичний клас обгортки. Цей клас потім виконує роботу над відправкою інформації різним реєстраторам. Одним з них є TracerX, а інший - моє повідомлення про статус користувача, яке повідомляє графічний інтерфейс користувача. Вона змінює рівні журналу та фільтрує повідомлення в залежності від їхнього стану. У мене є маленький клас, який містить інформацію про реєстрацію - рівень журналу, повідомлення тощо і т.д. Іншим можливим варіантом є відкриття коду TracerX і додавання ваших вимог. Я змінив його для свого використання.
додано Автор ScruffyDuck, джерело
Я не впевнений, що коментарі є найкращим способом для запитань. Якщо ви хочете зв'язатися зі мною, тоді моя електронна пошта повинна бути в моєму профілі.
додано Автор ScruffyDuck, джерело
ОК - ще одна ідея. TracerX дозволяє визначати імена реєстраторів. Ви можете мати стільки, скільки хочете. Таким чином, ви, можливо, можете налаштувати ім'я реєстратора для кожної групи, яку ви хочете відправити журналу і використовувати клас фільтра я запропонував вище, щоб відправити повідомлення, де ви хочете, щоб вони пішли в залежності від деяких критеріїв в класі повідомлень ....
додано Автор ScruffyDuck, джерело
Добре, тоді я думаю, що я застряг - вибачте.
додано Автор ScruffyDuck, джерело

Як написав devdigital, ці фреймворки зазвичай роблять це, надаючи призначені для журналювання методи: Warn ("..."), Fail ("...") ...

Ви також можете знайти інтерфейс ILogger засобу ведення журналу проекту замку. (можливо, спробуйте скористатися вихідним кодом ILogger.cs)

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

Подобається це:

    public interface Logger
    {
        public void Log(LogLevel level, string msg);
        public void Log(LogLevel level, string msgType, string msg);
        public void InitLogSession();
        public void EndLogSession();
        public void AddLogger(Logger chainedLogger);
        public void RemoveLogger(Logger chainedLogger);
    }

With an logging level enum Подобається це:

public enum LogLevel
{
    Info,
    Warn,
    Debug,
    Error,
    Fail
}

Реєстратори, які використовуватимуться, будуть потім обрані в межах ланцюга відповідальності.

1
додано
Рівні реєстрації або тип повідомлення фіксуються в параметрі msgType в моїй конструкції. Але повідомлення певного типу також може бути призначене для реєстрації лише в декількох напрямках. Як же це зробити? Наприклад, деякі повідомлення типу INFO, можливо, повинні бути зареєстровані в текстовому вікні Progress, а також текстовому вікні перегляду, тоді як інше повідомлення типу INFO може потребувати реєстрації тільки в текстовому вікні Progress.
додано Автор AllSolutions, джерело
Це стосується рівнів каротажу. Але питання ОП, схоже, більше пов'язане з налаштуванням реєстраторів, щоб мати різні цілі (або "appenders" у log4net, наприклад).
додано Автор Groo, джерело

Це здається гарним місцем для використання методів розширення .

Створіть базовий клас , потім створіть методи розширення для нього.

BaseLogger(LogMessage).toTextBoxLog().toFileLog().toDatabaseLog().

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

0
додано

"Будь ласка, не пропонуйте існуючі структури журналювання. Я просто хочу написати їх самостійно!"

Прийнята відповідь: Не винаходьте колесо! Використовуйте цю існуючу систему реєстрації!

facepalm

Найкраща відповідь, що є моєю відповіддю, - це щось подібне. Використовуйте інтерфейси, якщо ви хочете використовувати функцію plug and play. Ви можете зробити його легко налаштовуваним. Тут високий рівень.

  1. Use a config file to indicate what type of logger you want which implements your logging interface

  2. Use reflection to instantiate the type you pull from your config file AT RUNTIME.

  3. Pass in your logger interface you just made via constructor injection inside your classes.

Ви не винаходите колесо, створюючи інтерфейс. Якщо ви зробите свій загальний інтерфейс достатньо, то це реалізація не специфічна (в ідеалі). Це означає, що якщо log4net переходить до crap, або більше не підтримується, вам не доведеться RIP OUT AND MODIFY ВСІХ ВАШИХ КОДІВ ЗВІНКИ . Це схоже на жорстке підключення лампи безпосередньо до вашого будинку, щоб увімкнути його. Для любові до Бога, будь ласка, не робіть цього. Інтерфейс визначає контракт, за яким компоненти взаємодіють, а не реалізація.

Єдине, про що я можу подумати, - подивитися на існуючі рамки журналювання, знайти загальні елементи і написати інтерфейс як перетин загальних функцій. Очевидно, що з'являться функції, які ви пропустите. Це залежить від того, скільки вам потрібно. Ви можете використовувати Log4net або реєстратор Microsoft Event Viewer або обидва! Не впроваджуються деталі впровадження. І це MUCH менш зв'язана система, ніж мати все в коді, прив'язане до однієї технології/фреймворку.

0
додано
var chat = new Chat();
var chat = new Chat();
642 учасників

Обсуждение вопросов по C# / .NET / .NET Core / .NET Standard / Azure Сообщества-организаторы: — @itkpi — @dncuug