Гибкий дизайн интерфейса ведения журнала в C #

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

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

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

Трудность, с которой я столкнулся, заключается в следующем:

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

Когда я вызываю 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: Пожалуйста, не предлагайте существующие рамки ведения журнала. Я просто хочу написать это самостоятельно!

ИЗМЕНИТЬ 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-й получит идентификатор 2, третий получит 4, а 4-й получит 8 и т. Д.

Возвращаемый объект-регистратор может быть typecast для определенного класса, а дополнительные значения, такие как filePath, textbox и т. Д., Могут быть заданы вызывающим, или же я могу использовать несколько методов в LoggerFactory: по одному для каждого типа регистратора, который будет принимать конкретные параметры ,

Итак, предположим, что у нас 4 регистратора с идентификаторами 1,2,4,8. Конкретное сообщение, которое должно обрабатываться 1-м и 3-м регистратором (то есть идентификаторы 1 и 4 журнала), должно регистрироваться с использованием функции:

    public void Log(int loggerIds, string msg);

Значение, которое должно быть передано в loggerIds, должно быть «0101». Каждый регистратор проверяет, включен ли бит идентификатора журнала. Если да, только тогда он будет регистрировать сообщение.

Теперь в сигнатурах функций я упомянул тип int, но который является конкретным оптимизированным типом для выполнения бит-манипуляций и сравнений?

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

Примечание. В настоящее время я все еще на .NET 2.0. Если возможно, предложите решение в .NET 2.0, иначе отлично, я могу перейти к более высоким версиям.

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

3
добавлено отредактировано
Просмотры: 1
de
Что касается работы, я уверен, что это сработает. Но есть некоторые пробелы, например, int не является правильным типом данных. Мне просто нужен бит-шаблон. Таким образом, каждому регистратору будет выделен конкретный бит в битовой схеме. Какой тип данных я должен использовать? Во-вторых, с точки зрения дизайна, это хороший дизайн?
добавлено автор AllSolutions, источник
Я хотел бы сделать это самостоятельно. На данный момент я не хочу изучать стороннюю библиотеку. Но как уникальные имена помогут журналу решить, следует ли ему регистрировать конкретное сообщение?
добавлено автор AllSolutions, источник
Кстати, я шел по другой теме, предложенной вами, где вы задали вопрос. Дизайн, который я предложил, не будет ли работать даже на вопрос, который вы задали? Если вы хотите скопировать сообщения о статусе и зарегистрировать их сразу, вы можете ввести метод Flush (). С другой стороны, каждый класс, который должен регистрироваться, должен знать обо всех доступных журналах, созданных приложением, и, соответственно, настроить битовый шаблон.
добавлено автор AllSolutions, источник
Но даже в вашем другом решении каждый класс должен знать о GUIMessageQueue, поэтому я думаю, что очень сложно иметь свободно связанный код.
добавлено автор AllSolutions, источник
Думаю, вам нужно попробовать свою пересмотренную идею. Если вы используете модульное тестирование, вы должны быть в состоянии макет фабрики и других классов и проверить, как они будут работать
добавлено автор ScruffyDuck, источник
Вы хотите, чтобы это было упражнением? Потому что есть рамки, которые уже делают это довольно хорошо (например, log4net ). Эти фреймворки используют конфигурационные файлы для определения того, как будут вести себя отдельные регистраторы (если и где они будут отправлять свой вывод). Каждый регистратор в вашем коде должен иметь уникальное имя, на основе которого определяется выход (или несколько выходов).
добавлено автор Groo, источник
ну ... в чем логика, которую you использует, чтобы решить, какой журнал регистрирует какое сообщение?
добавлено автор 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 регистраторов, как один поле категории поможет четырем регистраторам решить, предназначено ли для них сообщение или нет? Можете ли вы привести пример псевдокода?
добавлено автор AllSolutions, источник
Я хотел бы сделать это самостоятельно. На данный момент я не хочу изучать стороннюю библиотеку. Уровни журнала не помогут, так как разные сообщения одного и того же уровня журнала могут потребоваться только для входа в систему.
добавлено автор AllSolutions, источник
Вы можете ( l4ndash.com/& hellip; ), но вам может быть лучше использовать имя регистратора для дифференциации. Для вывода в TextBox вам нужно будет написать пользовательский appender, например. triagile.blogspot.co.uk/2010/12/ & hellip;
добавлено автор devdigital, источник
Например. вы можете указать в сообщении имя категории или регистратора «special», а затем в своей конфигурации вы сопоставляете любое сообщение имени/категории журнала «special» с целевым объектом текстового поля с именем «textbox1» и файловой целью, называемой «file1».
добавлено автор devdigital, источник
Если вы пишете свою собственную структуру ведения журнала (которая, похоже, очень много работает, когда есть уже доступные фреймворки), то у вас есть полный контроль. Вы можете использовать файл конфигурации (или код через ваш API) для сопоставления категорий сообщений/имен журналов/уровней регистратора с конкретными целями. Вы можете добиться этого с помощью log4net/NLog, используя различные реализации ILogger, которые являются оболочками для вызовов log4net/NLog, но задает соответствующее имя регистратора.
добавлено автор devdigital, источник
Вам нужно будет различать каждое сообщение, для которого требуются разные цели, если они имеют одинаковый уровень ведения журнала и имя регистратора. Например, вы можете добавить тип «категория» к каждому сообщению. Если вы хотите использовать существующую инфраструктуру, вы можете создать собственные имена журналов, а не использовать имя типа. Например. вы можете иметь несколько ILogger, которые использует класс, причем каждая реализация использует другое имя регистратора.
добавлено автор devdigital, источник
Не ремонтируйте колесо. Перейдите для NLog или log4net (обе среды довольно зрелые, в корпоративном режиме мы используем log4net) и AOP :)
добавлено автор Simon, источник

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

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

Теперь я использую TracerX: http: //www.codeproject .com/Статьи/23424/TracerX-Logger-and-Viewer-for-NET Это проект с открытым исходным кодом, поэтому вам легко изменить исходный код, который вам нужен. Другие, упомянутые выше, также хороши.

<�Сильный> ИЗМЕНИТЬ

Это основано на принятом ответе на мой вопрос здесь: Как передать информацию о статусе в графический интерфейс в слабо связанном приложении Поэтому я не претендую на оригинальность в этом. Ваши сообщения в журнале просто в тот момент, когда я думаю

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

Так

  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
добавлено
Просто случилось видеть ваш отредактированный ответ сейчас ... Это полезно, и я все еще думаю об этом. Кстати, вы можете увидеть предлагаемый дизайн, который я опубликовал в моем EDITed вопросе, и заполнить недостающие пробелы?
добавлено автор AllSolutions, источник
Точка в том, что я не знаю «регистрационные группы» заранее, иначе это было бы очень легко. Во время выполнения каждый функциональный блок собирается решить, какое сообщение должно отправляться на все назначения!
добавлено автор AllSolutions, источник
Даже класс обертки - как он должен решить, что сообщение должно перейти в Logger 1, Logger 2, но не в Logger 3 и Logger 4? «Еще один возможный вариант - открыть код TracerX и добавить ваше требование». Если я знаю, что мне нужно делать, я мог бы также сделать это в своем собственном коде, так как мне не нужно использовать расширенные функции TracerX
добавлено автор AllSolutions, источник
Я только что прошел через TracerX. Это замечательный продукт, но мне просто не хватает 1 функции, которую я хочу. то есть управлять каротажными пунктами индивидуально на каждом уровне сообщений! Есть идеи?
добавлено автор AllSolutions, источник
Все здесь советуют использовать существующую структуру ведения журнала. Но может ли существующая структура регистрации соответствовать моим заявленным требованиям? Предположим, что 1 сообщение журнала предназначено для 2 из 4 регистраторов (и не основано на уровне ведения журнала). Может ли существующая инфраструктура обрабатывать такие сценарии?
добавлено автор AllSolutions, источник
«создать несколько типов сообщений, наследующих от абстрактного класса или интерфейса, которые представляют собой различные типы ведения журнала, которые вы хотите выполнить. Метод процесса может определять, куда их отправлять». - Но когда одно сообщение должно перейти к нескольким регистраторам, как можно определить назначение на основе класса типа сообщения?
добавлено автор AllSolutions, источник
Спасибо, отправили вам письмо, и мы можем продолжить обсуждение по почте.
добавлено автор AllSolutions, источник
Ну, не удалось получить идентификатор вашей почты из профиля. В нем упоминается только адрес веб-сайта. Идет ли ваш идентификатор с jon?
добавлено автор AllSolutions, источник
jon AT scruffyduck DOT org DOT uk
добавлено автор ScruffyDuck, источник
Я вижу, что у вас есть конкретная проблема. У меня аналогичная проблема в том, что я хочу отправлять сообщения о состоянии пользователю отдельно от журнала приложений. Я могу пойти для отдельного класса для обработки пользовательских данных. Могу ли я просто предложить вам взглянуть на статью TracerX, которую я опубликовал выше? Он использует уровень ведения журнала, чтобы отделить то, что происходит, но код для просмотра позволяет ряд поисков. Я перехватываю запросы журнала и использую небольшой класс, чтобы отправить их в нужное место. Опять же, это может быть не то, что вы хотите, поскольку оно довольно тесно связано с кодом.
добавлено автор ScruffyDuck, источник
Я выбежал из космоса. Я думаю, что мы все говорим о том, чтобы посмотреть на существующую структуру для базовой скучной работы сантехники и расширить ее для ваших нужд, а не начинать с нуля. Однако я вижу, что это может не сработать.
добавлено автор ScruffyDuck, источник
Я понятия не имею, поможет ли это то, что я делаю, отправляя мою информацию о регистрации в небольшой статический класс-оболочку. Затем этот класс выполняет работу по отправке информации на разные регистраторы. Один из них - TracerX, а другой - это отчет о статусе пользователя, который сообщает об этом графическому интерфейсу. Он изменяет уровни журналов и фильтрует сообщения в зависимости от того, что они собой представляют. У меня есть небольшой класс, который содержит информацию о журнале - уровень журнала, сообщение и т. Д. Другой возможный вариант - открыть код TracerX и добавить ваше требование. Я изменил его для моего использования.
добавлено автор ScruffyDuck, источник
Я не уверен, что комментарии - лучший способ Q & A. Если вы хотите связаться со мной, мое письмо должно быть в моем профиле.
добавлено автор ScruffyDuck, источник
ОК - еще одна идея. TracerX позволяет вам определять именованные логгеры. Вы можете иметь столько, сколько хотите. Таким образом, вы, возможно, можете настроить именованный журнал для каждой группы, в которую хотите отправить журнал, и использовать предложенный выше класс фильтров для отправки сообщений, куда вы хотите, чтобы они переходили в зависимости от некоторых критериев в классе сообщений ....
добавлено автор ScruffyDuck, источник
Хорошо, тогда я думаю, что застрял - извините.
добавлено автор ScruffyDuck, источник

Как писал devdigital, эти Framework обычно делают это, предоставляя назначенные методы для ведения журнала, такие как: Warn ("..."), Fail ("...") ...

Вы также можете найти интерфейс ILogger средство ведения журнала проекта замка. (возможно, попробуйте Google исходный код 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, источник
Это верно, что касается уровней регистрации. Но вопрос OP, похоже, больше связан с настройкой регистраторов на разные цели (или, например, «appenders» в log4net).
добавлено автор Groo, источник

Это кажется хорошим местом для использования методов расширения .

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

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

Таким образом, вы всегда вызываете BaseLogger , а затем только методы расширения , где это необходимо.

0
добавлено

«Пожалуйста, не предлагайте существующие рамки ведения журнала. Я просто хочу написать это самостоятельно!»

Принятый ответ: не изобретайте велосипед! Используйте эту существующую структуру ведения журнала!

Facepalm

Лучший ответ, который является моим ответом, выглядит примерно так. Используйте интерфейсы, если вы хотите использовать функции plug and play. Вы можете легко настроить его. Вот высокий уровень.

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

  2. Используйте отражение, чтобы создать экземпляр типа, который вы извлекаете из ваш файл конфигурации AT RUNTIME. </Р>

  3. Перейдите в свой интерфейс регистратора, который вы только что сделали через инсталляцию конструктора внутри ваших классов. </Р>

Вы не изобретаете колесо, создавая интерфейс. Если вы сделаете свой интерфейс достаточно общим, это реализация неспецифическая (в идеале). Это означает, что если log4net переходит в хрен, или больше не поддерживается, вам не нужно RIP OUT И ИЗМЕНИТЬ ВСЕ ВАШИМ КОДИРОВАНИЕМ . Это похоже на жесткую проводку лампы непосредственно в ваш дом, чтобы включить ее. Для любви к Богу, пожалуйста, не делайте этого. Интерфейс определяет контракт, по которому взаимодействуют компоненты, а не реализация.

Единственное, о чем я могу думать, это посмотреть на существующие рамки ведения журнала, найти общие элементы и написать свой интерфейс как пересечение общих функций. Очевидно, что у вас будут функции, которые вы пропустите. Это зависит от того, какую гибкость вы хотите. Вы можете использовать Log4net, или журнал регистрации событий Microsoft, или и то, и другое! Никакие детали реализации не выполняются. И это намного менее связанная система, чем наличие всего в вашем коде, связанного с одной технологией/каркасом.

0
добавлено