Большинство подобных фреймоворков предоставляют еще множество тонких настроек и опций, которые не были включены мной в мой небольшой класс, что и делает его простым в использовании. Весь код вписывается в один единственный .h
файл и легко понятен, поэтому может быть легко изменен под конкретные нужды.
Код использует часть функций С++11, однако, заставить его работать со старой спецификацией не составит труда..
Разработка
CLogger
— главный класс, с которым мы будем работать. Он находится в пространстве имен framework::Diagnostics
. Функции для синхронизации потоков предоставляются другими классами, расположенными в пространстве имен framework::Threading
. Один из них — CIntraProcessLock
, используется, если экземпляр класса CLogger
совместно используется разными потоками в пределах одного процесса. Если многопоточное использование не предвидится — используйте CNoLock
. При создании экземпляра логгера, вы должны, в качестве параметра шаблона, предоставить один из этих классов, либо любой другой, имеющий методы Lock()
и Unlock()
.
Использование
Пример создания логгера:
using namespace framework::Diagnostics; using namespace framework::Threading; CLogger<CNolock> logger(LogLevel::Info, _T("MyApp"));
Конструктор имеет следующие параметры:
LogLevel
— может быть одним из значений перечисленияframework::Diagnostics::LogLevel
. Это значение используется, если вы добавляете поток вывода, оно определяет, что будет записываться в лог.Name
— имя, данное объекту логгера, которое будет записываться в каждую строку журнала.logItems
— это битовая маска списка элементов, которые необходимо выводить в каждой строке журнала. Значения берутся из перечисленияframework::Diagnostics::LogItem
. По умолчанию, это настроено так:
Таким образом, если вы, например, хотите, чтобы в лог выводились только дата и время, создание экземпляра приняло бы следующий вид:
CLogger<CNoLock> logger(LogLevel::Info, _T("MyApp"), static_cast<int>(LogItem::DateTime));
Следующим шагом необходимо добавить поток вывода. Каждому потоку вывода вы можете назначить «уровень», например уровень Info
выводить в cout, а Error
— в файл. Пример:
logger.AddOutputStream(std::wcout, false, LogLevel::Error); logger.AddOutputStream(new std::wofstream("c:\\temp\\myapp.log"), true, framework::Diagnostics::LogLevel::Info);
Метод AddOutputStream
имеет следующие параметры:
os
— принимает любой производный классostream
, напримерcout
илиwcout
.own
— еслиtrue
, экземплярCLogger
будет использовать операторdelete
, для удаления переданного объекта ostream. Это обычно полезно, если вы создаете новый безымянный экземплярfilestream
в качестве параметра (см. код выше).LogLevel
— все записи, равные или выше этого уровня будут записаны в поток вывода. По умолчанию будет использоваться уровеньCLogger
.
Ну и, наконец, чтобы осуществить запись в лог, необходимо использовать макрос WRITELOG
. Пример:
WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program starting")); WRITELOG(logger, framework::Diagnostics::LogLevel::Warn, _T("Something may have gone wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Error, _T("Something did go wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program Ending"));
Исходный код
main.cpp
#include "threading.h" #include "Logger.h" #include <fstream> using namespace framework::Diagnostics; using namespace framework::Threading; int _tmain(int argc, _TCHAR* argv[]) { CLogger<CNolock> logger(LogLevel::Info, _T("MyApp")); logger.AddOutputStream(std::wcout, false, LogLevel::Error); logger.AddOutputStream(new std::wofstream("c:\\temp\\myapp.log"), true, framework::Diagnostics::LogLevel::Info); WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program starting")); WRITELOG(logger, framework::Diagnostics::LogLevel::Warn, _T("Something may have gone wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Error, _T("Something did go wrong")); WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program Ending")); return 0; }
tstring.h
#ifndef TSTRING_H_DEFINED #define TSTRING_H_DEFINED #include <string> #include <tchar.h> #if defined(UNICODE) || defined(_UNICODE) #define tstring std::wstring #define tout std::wcout #define tin std::wcin #define tostream std::wostream #define tofstream std::wofstream #define tifstream std::wifstream #define tfstream std::wfstream #else #define tstring std::string #define tout std::cout #define tin std::cin #define tostream std::ostream #define tofstream std::ofstream #define tifstream std::ifstream #define tfstream std::fstream #endif #endif
threading.h
#pragma once #include <windows.h> namespace framework { namespace Threading { class CIntraProcessLock { public: CIntraProcessLock() { InitializeCriticalSection(&m_cs); } ~CIntraProcessLock() { DeleteCriticalSection(&m_cs); } inline bool Lock() { return TryEnterCriticalSection(&m_cs) > 0 ? true : false; } inline void Unlock() { return LeaveCriticalSection(&m_cs); } private: CRITICAL_SECTION m_cs; }; class CNoLock { public: inline bool Lock() { return true; } inline void Unlock() { return; } }; } }
Logger.h
#pragma once #include <iostream> #include <vector> #include <stdio.h> #include <Windows.h> #include <time.h> #include "tstring.h" #include "threading.h" #ifdef UNICODE #define WRITELOG(logObj, level, text) logObj.Log(level, __FILEW__, __LINE__, __FUNCTIONW__, text); #else #define WRITELOG(logObj, level, text) logObj.Log(level, __FILEW__, __LINE__, __FUNCTION__, text); #endif using namespace std; namespace framework { namespace Diagnostics { enum class LogLevel { Info, Debug, Warn, Error }; enum class LogItem { Filename = 0x1, LineNumber = 0x2, Function = 0x4, DateTime = 0x8, ThreadId = 0x10, LoggerName = 0x20, LogLevel = 0x40 }; template <class ThreadingProtection> class CLogger { private: struct StreamInfo { tostream* pStream; bool owned; LogLevel level; StreamInfo(wostream* pStream, bool owned, LogLevel level) { this->pStream = pStream; this->owned = owned; this->level = level; } }; public: CLogger(framework::Diagnostics::LogLevel level, LPCTSTR name, int loggableItems = static_cast<int>(LogItem::Function) | static_cast<int>(LogItem::LineNumber) | static_cast<int>(LogItem::DateTime) | stattic_cast<int>(LogItem::LoggerName) | static_cast<int>(LogItem::LogLevel)) : m_level(level), m_name(name), m_loggableItem(loggableItems) { } ~CLogger() { } void AddOutputStream(tostream& os, bool own, LogLevel level) { AddOutputStream(&os, own, level); } void AddOutputStream(tostream& os, bool own) { AddOutputStream(os, own, m_level); } void AddOutputStream(tostream* os, bool own) { AddOutputStream(os, own, m_level); } void AddOutputStream(tostream* os, bool own, LogLevel level) { StreamInfo si(os, own, level); m_outputStreams.push_back(si); } void ClearOutputStreams() { for(vector<StreamInfo>::iterator iter = m_outputStreams.begin(); iter < m_outputStreams.end(); iter++) { if(iter->owned) delete iter->pStream; } m_outputStreams.clear(); } void Log(LogLevel level, LPCTSTR file, INT line, LPCTSTR func, LPCTSTR text) { m_threadProtect.Lock(); for(vector<StreamInfo>::iterator iter = m_outputStreams.begin(); iter < m_outputStreams.end(); iter++) { if(level < iter->level) { continue; } bool written = false; tostream * pStream = iter->pStream; if(m_loggableItem & static_cast<int>(LogItem::DateTime)) written = write_datetime(written, pStream); if(m_loggableItem & static_cast<int>(LogItem::ThreadId)) written = write<int>(GetCurrentThreadId(), written, pStream); if(m_loggableItem & static_cast<int>(LogItem::LoggerName)) written = write<LPCTSTR>(m_name.c_str(), written, pStream); if(m_loggableItem & static_cast<int>(LogItem::LogLevel)) { TCHAR strLevel[4]; loglevel_toString(level, strLevel); written = write<LPCTSTR>(strLevel, written, pStream); } if(m_loggableItem & static_cast<int>(LogItem::Function)) written = write<LPCTSTR>(func, written, pStream); if(m_loggableItem & static_cast<int>(LogItem::Filename)) written = write<LPCTSTR>(file, written, pStream); if(m_loggableItem & static_cast<int>(LogItem::LineNumber)) written = write<int>(line, written, pStream); written = write<LPCTSTR>(text, written, pStream); if(written) { (*pStream) << endl; pStream->flush(); } } m_threadProtect.Unlock(); } private: int m_loggableItem; LogLevel m_level; tstring m_name; vector<StreamInfo> m_outputStreams; ThreadingProtection m_threadProtect; template <class T> inline bool write(T data, bool written, wostream* strm) { if(written == true) { (*strm) << _T(" "); } (*strm) << data; return true; } inline bool write_datetime(bool written, wostream* strm) { if(written == true) { (*strm) << _T(" "); } time_t szClock; tm newTime; time( &szClock ); localtime_s(&newTime, &szClock); TCHAR strDate[10] = { _T('\0') }; TCHAR strTime[10] = { _T('\0') }; _tstrdate_s(strDate, 10); _tstrtime_s(strTime, 10); (*strm) << strDate << _T(" ") << strTime; return true; } void loglevel_toString(LogLevel level, LPTSTR strLevel) { switch (level) { case LogLevel::Error: _tcscpy_s(strLevel, 4, _T("ERR")); break; case LogLevel::Warn: _tcscpy_s(strLevel, 4, _T("WRN")); break; case LogLevel::Info: _tcscpy_s(strLevel, 4, _T("INF")); break; case LogLevel::Debug: _tcscpy_s(strLevel, 4, _T("DBG")); break; } } }; } }
Пример работы
ссылка на оригинал статьи http://habrahabr.ru/post/185222/
Добавить комментарий