Крутая система логгирования

Эта заметка была написана в моем втором по счету блоге на сайте blogspot два года назад. Сейчас я уже пришел к пониманию, что такая система логов мне не нравится. Она довольно навороченная, но все эти шаблоны меня теперь почему-то пугают. Дабы это добро не сгинуло вместе с тем дневником, публикую здесь. Прочитав данную статью, возможно вы сможете блеснуть умом в узких кругах. :)

Полгода назад мы создавали крутой логгер, использование которого выглядело так:

logger::info() << "Тестовое сообщение номер " << 1;

Самая главная проблема была в том, чтобы воткнуть ‘\n’ в конце сообщения. Для этого в info() был примерно следующий код:

InfoStream info()
{
    static InfoStream stream;
    // std::endl пишет в ostream '\n' и делает flush()
    stream << std::endl;
    return stream;
}

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

Когда лог стал в формате html, начались пляски с бубном.

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

Совсем недавно пришлось копаться во внутренностях. Задача стояла — вынести этот логгер в отдельный набор заголовков а-ля boost. Посмотрев на этот код, я плюнул и решил написать логгер заново. :)

Вот как это было реализовано:

class logger
{
public:
    class internal_stream
    {
    public:
        template <class T>
        internal_stream & operator <<(const T & value)
        {
            logger << value;
            return *this;
        }
 
        ~internal_stream() {}
 
    private:
        // Может использоваться только классом logger
        internal_stream(logger & stream) 
            : mStream(stream)
        {};
 
        logger & mStream;
    };
 
    internal_stream info()
    {
        return internal_stream(*this);
    }
};

Как это все работает:

  • в конструкторе logger — подготавливаем файл для записи, в текстовом ничего не делаем, в html — записываем заголовок
  • в деструкторе logger — закрываем файл, в html формате закрываем теги body, html и т.д.
  • в конструкторе internal_stream — начало строки, в html формате пишем tr, td и все что нравится
  • в деструкторе internal_stream — окончание строки, в текстовом это просто ‘\n’, в html закрываем теги tr и т.д.

Если сделать logger шаблонным классом, то можно реализовать разные буферы вывода. Начиная от текстового, html и xml и заканчивая сервером для приема и клиента для отправки сообщений по сети.

Добавить комментарий

Ваш e-mail не будет опубликован.