Система логирования - нужный и полезный инструмент для протоколирования работы любой программы. Она значительно экономит время, проведенное под отладкой и вылавливанием различных багов.
Для вывода форматированного текста мы воспользуемся функционалом 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
Комментариев нет:
Отправить комментарий