Сборник кусков кода
Различные примеры кода.
[C++] Простая библиотека логирования
05.10.2012
|
Abyx |
Библиотека состоит из пары классов:
класс BasicLog — абстрактный класс, обеспечивает необходимую инфраструктуру для логирования;
класс StreamLog — конкретный класс логирования, пишет текст в переданный ему поток (std::ostream). Использует мьютекс для синхронизации доступа к этому потоку.
Код библиотеки:
(Один файл Log.hpp)
Пример использования:
Пример своего класса логирования:
Известные проблемы:
Метод endLog может бросить исключение, например bad_alloc.
По этому код между "MY_LOG_*()" и ";" не должен бросать исключения. Если при записи во временный поток оттуда будет брошено исключение bad_alloc, то скорее всего в методе endLog тоже возникнет bad_alloc, и программа завершится.
Впрочем, в любом случае, программы обычно умирают от неожиданного bad_alloc, так что это незначительный недостаток библиотеки.
О производительности:
Для повышения производительности, было бы лучше делать асинхронную запись в лог.
Вместо m_stream << tempStream.rdbuf() << endl; в методе endLog должно быть что-то вроде
m_logQueue.emplace_back(std::move(tempStream)); m_condVar.notify_all();, а сама запись в поток лога должна быть в отдельном фоновом потоке.
класс BasicLog — абстрактный класс, обеспечивает необходимую инфраструктуру для логирования;
класс StreamLog — конкретный класс логирования, пишет текст в переданный ему поток (std::ostream). Использует мьютекс для синхронизации доступа к этому потоку.
Код библиотеки:
(Один файл Log.hpp)
#pragma once
/* Simple Log library
*/
#include <sstream>
#include <mutex>
namespace lib
{
class BasicLog
{
public:
enum Level { Debug, Info, Warning, Error, Fatal };
BasicLog(Level level = Info) : m_level(level) {}
virtual ~BasicLog() {}
Level getLevel() const { return m_level; }
void setLevel(Level level) { m_level = level; }
bool matchLevel(Level level) const { return level >= m_level; }
struct Stream
{
BasicLog& log;
std::stringstream stream;
explicit Stream(BasicLog& log) : log(log)
{
log.beginLog(stream);
}
~Stream()
{
log.endLog(stream);
}
};
protected:
virtual void beginLog(std::stringstream& tempStream) { (void)tempStream; }
virtual void endLog(std::stringstream& tempStream) = 0;
private:
BasicLog(const BasicLog&); // = delete;
void operator=(const BasicLog&); // = delete;
Level m_level;
};
class StreamLog : public BasicLog
{
public:
StreamLog(std::ostream& stream, Level level = Info) : BasicLog(level), m_stream(stream) {}
private:
StreamLog(const StreamLog&); // = delete;
void operator=(const StreamLog&); // = delete;
virtual void endLog(std::stringstream& tempStream) override
{
std::unique_lock<std::mutex> lock(m_mutex);
m_stream << tempStream.rdbuf() << std::endl;
}
std::mutex m_mutex;
std::ostream& m_stream;
};
#define LIB_LOG_HELPER_(log, level) if (log.matchLevel(level)) lib::BasicLog::Stream(log).stream
}
Пример использования:
#include "Log.hpp"
#include <iostream>
#define MY_LOG_DEBUG(log) LIB_LOG_HELPER_(log, log.Debug) << "DEBUG: "
#define MY_LOG_INFO(log) LIB_LOG_HELPER_(log, log.Info) << "INFO: "
#define MY_LOG_WARN(log) LIB_LOG_HELPER_(log, log.Warning) << "WARNING: "
#define MY_LOG_ERROR(log) LIB_LOG_HELPER_(log, log.Error) << "ERROR: "
#define MY_LOG_FATAL(log) LIB_LOG_HELPER_(log, log.Fatal) << "FATAL: "
int main()
{
lib::StreamLog log(std::cout);
MY_LOG_INFO(log) << "some" << " text";
}
Пример своего класса логирования:
#include <Windows.h>
struct DebugLog : lib::BasicLog
{
virtual void endLog(std::stringstream& tempStream) override
{
tempStream << '\n';
auto&& str = tempStream.str();
::OutputDebugStringA(str.c_str()); // в отличие от StreamLog, тут мьютекс не нужен
}
};
...
DebugLog dbgLog;
MY_LOG_INFO(dbgLog) << "text";
Известные проблемы:
Метод endLog может бросить исключение, например bad_alloc.
По этому код между "MY_LOG_*()" и ";" не должен бросать исключения. Если при записи во временный поток оттуда будет брошено исключение bad_alloc, то скорее всего в методе endLog тоже возникнет bad_alloc, и программа завершится.
Впрочем, в любом случае, программы обычно умирают от неожиданного bad_alloc, так что это незначительный недостаток библиотеки.
О производительности:
Для повышения производительности, было бы лучше делать асинхронную запись в лог.
Вместо m_stream << tempStream.rdbuf() << endl; в методе endLog должно быть что-то вроде
m_logQueue.emplace_back(std::move(tempStream)); m_condVar.notify_all();, а сама запись в поток лога должна быть в отдельном фоновом потоке.
05.10.2012 0 комментариев |