Простой и достаточно мощный логгер
Я не являюсь сторонником изобретателей велосипедов. Всегда считал, что если кто-то сделал за тебя какую-то работу и поделился ей, то результатами этой работы можно смело пользоваться. Это экономит кучу времени и сил. К тому же, уверен ли ты, что твой велосипед будет работать лучше ранее изобретенных велосипедов?
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 и выявлении новых интересных фич буду дополнять статью новой информацией.
Ссылки
- Утилита для дешифровки логов - https://github.com/techlinked/encrypted-log-reader.git
- Рассматриваемые примеры - https://github.com/techlinked/plog-using.git