Система логирования - нужный и полезный инструмент для протоколирования работы любой программы. Она значительно экономит время, проведенное под отладкой и вылавливанием различных багов.
Для вывода форматированного текста мы воспользуемся функционалом std::ostringstream, в нашем классе лога это переменная “m_oss”, которая будет накапливать в себе все сообщения. Функция член класса GetMsg() будет обеспечивать доступ к этой переменной.
Допустим мы хотим, чтобы сообщения могли выводиться как в консоль, так и в файл лога. Для этого заведем два класса: один будет отвечать за сбор сообщений, а второй, куда и как именно они будут выводиться.
Листинг шаблонного класса лога:
Класс, отвечающий за реализацию вывода сообщений и их параметры:
Для установки уровня детализации достаточно написать:
Если систему логирования предполагается использовать одновременно из нескольких потоков, то можно добавить в класс, реализующий вывод сообщений "C_OutSream", какой-нибудь объект синхронизации, например мьютекс.
Существует много готовых библиотек (log4cplus, Apache log4cxx и тд.), однако в ряде случаев гораздо удобней и полезней иметь свою реализацию, которую можно в дальнейшем модифицировать под конкретную задачу.
И так, сначала определимся с требованиями к ней. Это удобство использования, эффективность применения, возможность расширения и дальнейших модификаций, типобезопасность и, по возможности, кросс платформеннсть. Также она будет поддерживать несколько уровней детализации сообщений (info, error, warning, debug ...). Для вывода форматированного текста мы воспользуемся функционалом std::ostringstream, в нашем классе лога это переменная “m_oss”, которая будет накапливать в себе все сообщения. Функция член класса GetMsg() будет обеспечивать доступ к этой переменной.
Допустим мы хотим, чтобы сообщения могли выводиться как в консоль, так и в файл лога. Для этого заведем два класса: один будет отвечать за сбор сообщений, а второй, куда и как именно они будут выводиться.
Листинг шаблонного класса лога:
// уровни протоколирования
enum LogLevel_t { logError, logWarning, logInfo, logDebug,
logDebug1, logDebug2, logDebug3 };
template < typename T >
class Logger {
public:
Logger( void );
virtual ~Logger( void );
std::ostringstream& GetMsg(LogLevel_t level = logInfo);
// устанавливает уровень детализации
static LogLevel_t& ReportingLevel( void );
static const char* LevelToString(LogLevel_t level);
static LogLevel_t LevelFromString(const char* ch_level);
protected:
std::ostringstream m_oss;
private:
Logger(const Logger&);
Logger& operator = (const Logger&);
};Класс, отвечающий за реализацию вывода сообщений и их параметры:
class OutStream {
public:
// инициализирует(пересоздавая) файл лога
static bool InitLogFile(const char* nameFileLog);
// перенаправление вывода сообщений в файл
static bool UseFile( void );
// перенаправление вывода сообщений в консоль
static bool UseStderr( void );
// надо ли выводить время события в лог
static bool& IsOutputTime( void );
};Объединим функционал двух классов:class Log : public Logger < OutStream > { };
Теперь можем использовать класс лога, как показано ниже:static const int lost[] = {4, 8, 15, 16, 23, 42};
Log().GetMsg(logWarning)<<"\n\t >: "<< lost[0] <<" "<< lost[1] <<" "<<
lost[2] <<" "<< lost[3] <<" "<< lost[4] <<" "<< lost[5] << " end.";
Результат: > WARNING(03:27:54.000) : >: 4 8 15 16 23 42 end.Выполнение этого кода будет создавать экземпляр лога с logWarning уровнем детализации, далее будет получен объект std::sstringstream с накопленными данными, а затем собрана готовая строка, которую можно вывести куда угодно одним лишь вызовом fprintf().
Для установки уровня детализации достаточно написать:
Log::ReportingLevel() = logDebug2;Для удобства завернем все это в макросы:
#define TO_LOG(level) \
if (level > logDebug3); \
else if(level > Log::ReportingLevel()); \
else Log().GetMsg(level)
#define LOG_FILE_INIT(x) OutStream::InitLogFile(x)
#define LOG_IS_OUTPUT_TIME(x) OutStream::IsOutputTime() = x;
#define LOG_USE_FILE OutStream::UseFile();
#define LOG_USE_STDERR OutStream::UseStderr();
Теперь мы можем писать так:// устанавливаем уровень выводимых сообщений
Log::ReportingLevel() = Log::LevelFromString("DEBUG2");
LOG_FILE_INIT("MyLog.log");
// разрешаем выводить время сообщения
LOG_IS_OUTPUT_TIME(true)
LOG_DEBUG_3 << "Initialize the log file!";
LOG_IS_OUTPUT_TIME(false)
// выводим на консоль через stderr
LOG_USE_STDERR
LOG_WARNING << "Some message";
// снова выводим сообщения в файл лога
LOG_USE_FILE
LOG_INFO << "Message out in the file";
И так, у нас получилось что-то вроде ран-тайм полиморфизма или стейт машины для системы логирования. При желании можно сделать выводимые в консоль сообщения различных цветов в зависимости от их уровней. Если систему логирования предполагается использовать одновременно из нескольких потоков, то можно добавить в класс, реализующий вывод сообщений "C_OutSream", какой-нибудь объект синхронизации, например мьютекс.
static std::mutex g_mutex;
class C_OutStream : public OutStream {
public:
static void OutputFile(const char* msg);
static void OutputStdErr(const char* msg);
};
void C_OutStream::OutputFile(const char* msg)
{
g_mutex.lock();
FILE* pStream = fopen(s_logFileName.c_str(), "a");
if (!pStream)
return;
fprintf(pStream, "%s", msg);
fflush(pStream);
fclose(pStream);
g_mutex.unlock();
}
Source code: Logger_VS10.rar
Комментариев нет:
Отправить комментарий