Гибкий дизайн интерфейса ведения журнала в 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 этого проекта: каждому классу, который должен регистрироваться, необходимо знать обо всех доступных журналах, созданных приложением, и соответственно настроить битовый шаблон. Любые идеи, как иметь слабо связанную конструкцию?

Почему бы вам не взглянуть на (или действительно использовать) существующую структуру ведения журнала, такую как  [log4net](https://logging.apache.org/log4net/)  или  [NLog](http://nlog-project.org/) .

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



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

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

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

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

Это основано на принятом ответе на мой вопрос здесь:  [Как передать информацию о статусе в графический интерфейс в слабо связанном приложении](https://stackoverflow.com/questions/14058373/how-to-pass-status-information-to-the-gui-in-a-loosely -coupled-application )  Поэтому я не претендую на оригинальность в этом. Ваши сообщения в журнале просто в тот момент, когда я думаю

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

Так

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



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

Вы также можете найти интерфейс  `ILogger`   [средство ведения журнала](http://docs.castleproject.org/Windsor.Logging-Facility.ashx)  проекта замка. (возможно, попробуйте [Google](https://google.com) исходный код 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
}

```

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



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

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

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

```

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



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

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

 *Facepalm* 

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

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

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

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