Пост

Простой и достаточно мощный логгер

Я не являюсь сторонником изобретателей велосипедов. Всегда считал, что если кто-то сделал за тебя какую-то работу и поделился ей, то результатами этой работы можно смело пользоваться. Это экономит кучу времени и сил. К тому же, уверен ли ты, что твой велосипед будет работать лучше ранее изобретенных велосипедов?

PLOG - Почему я использую этот логгер?

Критерии выбора

  • Простота подключения и использования
  • Возможность писать в файл (или несколько файлов одновременно)
  • Фильтрация по степени важности
  • Кроссплатформенность
  • Возможность добавления дополнительного функционала

PLOG отвечает этим требованиям и дает дополнительные плюшки.

Подключение к проекту и использование

Все максимально просто! Указать путь к склонированному проекту.

1
INCLUDEPATH += ../plog/include

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "plog/Log.h"

int main()
{
    plog::init(plog::verbose, "plogger.log");

    PLOG_INFO << "Hello my little pony";
    PLOG_ERROR << "Hello my little pony";
    PLOG_DEBUG << "Hello my little pony";
    PLOG_VERBOSE << "Hello my little pony";
    PLOG_WARNING << "Hello my little pony";

    return 0;
}

После запуска будет создан файл, куда будут записаны логи.

1
2
3
4
5
6
$ tail -f plogger.log
2019-08-29 00:25:27.060 INFO  [13388] [main@6] Hello my little pony
2019-08-29 00:25:27.060 ERROR [13388] [main@7] Hello my little pony
2019-08-29 00:25:27.060 DEBUG [13388] [main@8] Hello my little pony
2019-08-29 00:25:27.060 VERB  [13388] [main@9] Hello my little pony
2019-08-29 00:25:27.060 WARN  [13388] [main@10] Hello my little pony

Так же вывод логов по степени подробности можно легко параметризовать используя getopt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
while ((opt = getopt(argc, argv, "dv")) != -1) {
    switch(opt) {
    case 'd':
        ++debug;
        break;
    case 'v':
        ++verbose;
        break;
    default:
        break;
    }
}

if (debug) {
    plog::init(plog::debug, "plogger.log");
} else if (verbose) {
    plog::init(plog::verbose, "plogger.log");
} else {
    plog::init(plog::info, "plogger.log");
}

Обзор функционала

  • Вывод в консоль и параллельная запись логов в файл. Для использования необходимо добавить ConsoleAppender.
1
2
static plog::ConsoleAppender<plog::TxtFormatter> consoleAppender;
plog::init(plog::verbose, "plogger.log").addAppender(&consoleAppender);
  • Вывод в консоль информации разными цветами. Для использования необходимо добавить ColorConsoleAppender. Полезно для ускорения визуальной локализации ошибок и для более читабельного вида.
1
2
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
plog::init(plog::verbose, "plogger.log").addAppender(&consoleAppender);

Примерно так это будет выглядеть на Ubuntu.

  • Шифрование логов. Здесь необходимо создать класс LogEncrypt со статическим методм header, который будет вызывать метод encrypt и передать этот класс в RollingFileAppender. Непосредственно в encrypt будет реализовано простое XOR шифрование.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
namespace plog
{
static std::string key;

class LogEncrypt
{
public:
    static std::string header(const util::nstring& str) {
        return encrypt(str);
    }

    static std::string encrypt(const util::nstring& str) {
        const std::string& in = str;
        std::string out;
        out.resize(in.size());

        for (size_t i = 0; i < out.size(); ++i) { // Простое XOR шифрование.
            out[i] = in[i] ^ key[i % (key.size() - 1)];
            if (i == out.size() - 1) {
                ++i;
                out.push_back('\n' ^ key[i % (key.size() - 1)]);
            }
        }
        return out;
    }
};
}

При инициализации логгера необходимо передать наш “шифратор” LogEncrypt классу RollingFileAppender. Я дополнительно использую класс LoggerEncryptKey, который взял на себя обязательства получения ключа шифрования из файла.

1
2
3
4
5
6
7
8
LoggerEncryptKey loggerEncryptKey("/path/to/key");
if (loggerEncryptKey.good()) {
    plog::key = loggerEncryptKey.getKey();
}

static plog::RollingFileAppender<plog::TxtFormatter, plog::LogEncrypt> encAppender("encPlogger.log");
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
plog::init(plog::verbose, "plogger.log").addAppender(&consoleAppender).addAppender(&encAppender);

Соответственно, при чтении файла encPlogger.log, видно, что он содержит зашифрованные данные. Дополнительно отмечу, что класс LogEncrypt должен быть реализован в заголовочном файле, иначе вы не найдете способ передать методу encrypt ключ шифрования. И вообще, пока разбирался со всем этим, мне пришла мысль, что шифрование данных можно реализовать другим способом, а именно в качестве некоторой утилиты, на вход которому будет направлен вывод, и эта утилита, в свою очередь, сохраняла бы зашифрованный лог в файл. Но это уже тема для другого поста.

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

И собственно код дешифратора логов

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int main(int argc, char** argv)
{
    if (argc != 3) {
        usage(argv[0]);
    } else {
        const std::string logPath = argv[1];
        const std::string keyPath = argv[2];

        std::ifstream keyFile(keyPath);
        std::ifstream log(logPath);

        std::string key;
        keyFile >> key;

        std::string out;
        char c;
        while (log.get(c)) {
            out.push_back(c);
        }

        size_t ind = 0;
        for (size_t i = 0; i < out.size(); ++i) {
            char cc = char(out[i] ^ key[ind % (key.size() - 1)]);
            ++ind;
            cout << cc;
            if (cc == '\n') {
                ++i;
                ind = 0;
            }
        }
    }
    return 0;
}

Вместо заключения

В будущем при более детальном изучении PLOG и выявлении новых интересных фич буду дополнять статью новой информацией.

Ссылки

Авторский пост защищен лицензией CC BY 4.0 .