/* * Log4z License * ----------- * * Log4z is licensed under the terms of the MIT license reproduced below. * This means that Log4z is free software and can be used for both academic * and commercial purposes at absolutely no cost. * * * =============================================================================== * * Copyright (C) 2010-2015 YaweiZhang . * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * =============================================================================== * * (end of COPYRIGHT) */ #include "log4z.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined (WIN32) || defined(_WIN64) #include #include #include #pragma comment(lib, "shlwapi") #pragma warning(disable:4996) #else #include #include #include #include #include #include #include #include #include #include #include #endif #ifdef __APPLE__ #include #include #endif #include #include #include #include #if defined(WIN32) || defined(_WIN64) #define WIN32_LEAN_AND_MEAN #include #include #endif #include #include #include #include #include using namespace SOUI; static const char *const LOG_STRING[]= { "TRACE", "DEBUG", "INFO ", "WARN ", "ERROR", "ALARM", "FATAL", }; #if defined (WIN32) || defined(_WIN64) const static WORD LOG_COLOR[ILog4zManager::LOG_LEVEL_FATAL + 1] = { 0, 0, FOREGROUND_BLUE | FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_RED, FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_RED | FOREGROUND_BLUE }; #else const static char LOG_COLOR[LOG_LEVEL_FATAL + 1][50] = { "\e[0m", "\e[0m", "\e[34m\e[1m",//hight blue "\e[33m", //yellow "\e[31m", //red "\e[32m", //green "\e[35m" }; #endif ////////////////////////////////////////////////////////////////////////// //! Log4zFileHandler ////////////////////////////////////////////////////////////////////////// class Log4zFileHandler { public: Log4zFileHandler(){ _file = NULL; } ~Log4zFileHandler(){ close(); } inline bool isOpen(){ return _file != NULL; } inline bool open(const char *path, const char * mod) { if (_file != NULL){fclose(_file);_file = NULL;} _file = fopen(path, mod); return _file != NULL; } inline void close() { if (_file != NULL){fclose(_file);_file = NULL;} } inline void write(const char * data, size_t len) { if (_file && len > 0) { if (fwrite(data, 1, len, _file) != len) { close(); } } } inline void flush(){ if (_file) fflush(_file); } inline std::string readLine() { char buf[500] = { 0 }; if (_file && fgets(buf, 500, _file) != NULL) { return std::string(buf); } return std::string(); } inline const std::string readContent(); inline size_t size() const{ if(!_file) return 0; size_t pos = ftell(_file); fseek(_file,0,SEEK_END); size_t ret = ftell(_file); fseek(_file,pos,SEEK_SET); return ret; } public: FILE *_file; }; ////////////////////////////////////////////////////////////////////////// //! UTILITY ////////////////////////////////////////////////////////////////////////// static void sleepMillisecond(unsigned int ms); static tm timeToTm(time_t t); static bool isSameDay(time_t t1, time_t t2); static void fixPath(std::string &path); static void trimLogConfig(std::string &str, std::string extIgnore = std::string()); static std::pair splitPairString(const std::string & str, const std::string & delimiter); static bool isDirectory(std::string path); static bool createRecursionDir(std::string path); static unsigned int getProcessID(); static std::string getProcessName(); ////////////////////////////////////////////////////////////////////////// //! LockHelper ////////////////////////////////////////////////////////////////////////// class LockHelper { public: LockHelper(); virtual ~LockHelper(); public: void lock(); void unLock(); private: #if defined (WIN32) || defined(_WIN64) CRITICAL_SECTION _crit; #else pthread_mutex_t _crit; #endif }; ////////////////////////////////////////////////////////////////////////// //! AutoLock ////////////////////////////////////////////////////////////////////////// class AutoLock { public: explicit AutoLock(LockHelper & lk):_lock(lk){_lock.lock();} ~AutoLock(){_lock.unLock();} private: LockHelper & _lock; }; ////////////////////////////////////////////////////////////////////////// //! SemHelper ////////////////////////////////////////////////////////////////////////// class SemHelper { public: SemHelper(); virtual ~SemHelper(); public: bool create(int initcount); bool wait(int timeout = 0); bool post(); private: #if defined (WIN32) || defined(_WIN64) HANDLE _hSem; #elif defined(__APPLE__) dispatch_semaphore_t _semid; #else sem_t _semid; bool _isCreate; #endif }; ////////////////////////////////////////////////////////////////////////// //! ThreadHelper ////////////////////////////////////////////////////////////////////////// #if defined (WIN32) || defined(_WIN64) static unsigned int WINAPI threadProc(LPVOID lpParam); #else static void * threadProc(void * pParam); #endif class ThreadHelper { public: ThreadHelper(){_hThreadID = 0;} virtual ~ThreadHelper(){} public: bool start(); bool wait(); virtual void run() = 0; private: unsigned long long _hThreadID; #ifndef WIN32 pthread_t _phtreadID; #endif }; #if defined (WIN32) || defined(_WIN64) unsigned int WINAPI threadProc(LPVOID lpParam) { ThreadHelper * p = (ThreadHelper *) lpParam; p->run(); return 0; } #else void * threadProc(void * pParam) { ThreadHelper * p = (ThreadHelper *) pParam; p->run(); return NULL; } #endif ////////////////////////////////////////////////////////////////////////// //! LogData ////////////////////////////////////////////////////////////////////////// struct LogData { LoggerId _id; //dest logger id int _level; //log level unsigned __int64 _time; //create time DWORD _pid; DWORD _tid; std::string _content; //content std::string _filter; std::string _moduleName; std::string _file; std::string _func; int _line; }; ////////////////////////////////////////////////////////////////////////// //! LoggerInfo ////////////////////////////////////////////////////////////////////////// struct LoggerInfo { //! attribute std::string _key; //logger key std::string _name; // one logger one name. std::string _path; //path for log file. int _level; //filter level bool _display; //display to screen bool _outfile; //output to file unsigned int _limitsize; //limit file's size, unit Million byte. bool _enable; //logger is enable bool _fileLine; //enable/disable the log's suffix.(file name:line number) //! runtime info time_t _curFileCreateTime; //file create time unsigned int _curWriteLen; //current file length Log4zFileHandler _handle; //file handle. LoggerInfo() { _enable = false; _path = LOG4Z_DEFAULT_PATH; _level = LOG4Z_DEFAULT_LEVEL; _display = LOG4Z_DEFAULT_DISPLAY; _outfile = LOG4Z_DEFAULT_OUTFILE; _limitsize = LOG4Z_DEFAULT_LIMITSIZE; _fileLine = LOG4Z_DEFAULT_SHOWSUFFIX; _curFileCreateTime = 0; _curWriteLen = 0; } }; class COutoutFileBuilder : public ILog4zManager::IOutputFileBuilder { public: virtual bool monthDir() const { return false; } virtual bool dayLog() const { return false; } virtual bool buildOutputFile(char *pszFileName,int nLen,tm time,const char * pszLogName,unsigned long pid,int curFileIndex) const { sprintf_s(pszFileName,nLen,"%s-%d.log",pszLogName,curFileIndex); return true; } }s_defOutputFileBuilder; ////////////////////////////////////////////////////////////////////////// //! LogerManager ////////////////////////////////////////////////////////////////////////// class LogerManager : public ThreadHelper, public TObjRefImpl { public: LogerManager(); virtual ~LogerManager(); bool configFromStringImpl(std::string content, bool isUpdate); //! 读取配置文件并覆写 virtual bool config(const char* configPath); virtual bool configFromString(const char* configContent); //! 覆写式创建 virtual LoggerId createLogger(const char* key); virtual bool start(); virtual bool stop(); virtual bool prePushLog(LoggerId id, int level); virtual bool prePushLog(const char * name, int level); virtual bool pushLog(LoggerId id, int level,const char * filter, const char * log, const char * file, int line, const char *func, const void * pRetAddr); virtual bool pushLog(const char * name, int level, const char * filter, const char * log, const char * file, int line, const char *func, const void * pRetAddr); //! 查找ID virtual LoggerId findLogger(const char* key); virtual bool enableLogger(LoggerId id, bool enable); virtual bool setLoggerName(LoggerId id, const char * name); virtual bool setLoggerPath(LoggerId id, const char * path); virtual bool setLoggerLevel(LoggerId id, int nLevel); virtual bool setLoggerFileLine(LoggerId id, bool enable); virtual bool setLoggerDisplay(LoggerId id, bool enable); virtual bool setLoggerOutFile(LoggerId id, bool enable); virtual bool setLoggerLimitsize(LoggerId id, unsigned int limitsize); virtual void setOutputFileBuilder(IOutputFileBuilder *pOutputFileBuilder); virtual bool setLoggerFileMaxNum(unsigned int maxnum); virtual bool setAutoUpdate(int interval); virtual bool updateConfig(); virtual bool isLoggerEnable(LoggerId id); virtual unsigned long long getStatusTotalWriteCount(){return _ullStatusTotalWriteFileCount;} virtual unsigned long long getStatusTotalWriteBytes(){return _ullStatusTotalWriteFileBytes;} virtual unsigned long long getStatusWaitingCount(){return _ullStatusTotalPushLog - _ullStatusTotalPopLog;} virtual unsigned int getStatusActiveLoggers(); virtual void setOutputListener(IOutputListener *pListener); protected: void showColorText(const char *text, int level = LOG_LEVEL_DEBUG); LoggerId getLoggerId(const char * name); bool openLogger(LogData * log); bool closeLogger(LoggerId id); bool popLog(LogData *& log); virtual void run(); private: //! thread status. bool _runing; //! hot change name or path for one logger LockHelper _hotLock; int _hotUpdateInterval; unsigned int _checksum; //! the process info. unsigned int _pid; std::string _proName; //! config file name std::string _configFile; //! logger id manager, [logger name]:[logger id]. std::map _ids; std::map _names; // the last used id of _loggers LoggerId _lastId; LoggerInfo _loggers[LOG4Z_LOGGER_MAX]; //! log queue std::list _logs; LockHelper _logLock; //show color lock LockHelper _scLock; //status statistics //write file unsigned long long _ullStatusTotalWriteFileCount; unsigned long long _ullStatusTotalWriteFileBytes; //Log queue statistics unsigned long long _ullStatusTotalPushLog; unsigned long long _ullStatusTotalPopLog; IOutputFileBuilder * m_pOutputFileBuilder; IOutputListener * m_pListener; unsigned int _maxFileNum; }; ////////////////////////////////////////////////////////////////////////// //! Log4zFileHandler ////////////////////////////////////////////////////////////////////////// const std::string Log4zFileHandler::readContent() { std::string content; if (!_file) { return content; } char buf[BUFSIZ]; size_t ret = 0; do { ret = fread(buf, sizeof(char), BUFSIZ, _file); content.append(buf, ret); } while (ret == BUFSIZ); return content; } ////////////////////////////////////////////////////////////////////////// //! utility ////////////////////////////////////////////////////////////////////////// void sleepMillisecond(unsigned int ms) { #if defined (WIN32) || defined(_WIN64) ::Sleep(ms); #else usleep(1000*ms); #endif } struct tm timeToTm(time_t t) { #if defined (WIN32) || defined(_WIN64) #if _MSC_VER < 1400 //VS2003 return * localtime(&t); #else //vs2005->vs2013-> struct tm tt = { 0 }; localtime_s(&tt, &t); return tt; #endif #else //linux struct tm tt = { 0 }; localtime_r(&t, &tt); return tt; #endif } bool isSameDay(time_t t1, time_t t2) { tm tm1 = timeToTm(t1); tm tm2 = timeToTm(t2); if ( tm1.tm_year == tm2.tm_year && tm1.tm_yday == tm2.tm_yday) { return true; } return false; } void fixPath(std::string &path) { if (path.empty()){return;} for (std::string::iterator iter = path.begin(); iter != path.end(); ++iter) { if (*iter == '\\'){*iter = '/';} } if (path.at(path.length()-1) != '/'){path.append("/");} } static void trimLogConfig(std::string &str, std::string extIgnore) { if (str.empty()){return;} extIgnore += "\r\n\t "; int length = (int)str.length(); int posBegin = 0; int posEnd = 0; //trim utf8 file bom if (str.length() >= 3 && (unsigned char)str[0] == 0xef && (unsigned char)str[1] == 0xbb && (unsigned char)str[2] == 0xbf) { posBegin = 3; } //trim character for (int i = posBegin; i splitPairString(const std::string & str, const std::string & delimiter) { std::string::size_type pos = str.find(delimiter.c_str()); if (pos == std::string::npos) { return std::make_pair(str, ""); } return std::make_pair(str.substr(0, pos), str.substr(pos+delimiter.length())); } static bool parseConfigLine(const std::string& line, int curLineNum, std::string & key, std::map & outInfo) { std::pair kv = splitPairString(line, "="); if (kv.first.empty()) { return false; } trimLogConfig(kv.first); trimLogConfig(kv.second); if (kv.first.empty() || kv.first.at(0) == '#') { return true; } if (kv.first.at(0) == '[') { trimLogConfig(kv.first, "[]"); key = kv.first; { std::string tmpstr = kv.first; std::transform(tmpstr.begin(), tmpstr.end(), tmpstr.begin(), ::tolower); if (tmpstr == "main") { key = "Main"; } } std::map::iterator iter = outInfo.find(key); if (iter == outInfo.end()) { LoggerInfo li; li._enable = true; li._key = key; li._name = key; outInfo.insert(std::make_pair(li._key, li)); } else { std::cout << "log4z configure warning: duplicate logger key:["<< key << "] at line:" << curLineNum << std::endl; } return true; } trimLogConfig(kv.first); trimLogConfig(kv.second); std::map::iterator iter = outInfo.find(key); if (iter == outInfo.end()) { std::cout << "log4z configure warning: not found current logger name:["<< key << "] at line:" << curLineNum << ", key=" << kv.first << ", value=" << kv.second << std::endl; return true; } std::transform(kv.first.begin(), kv.first.end(), kv.first.begin(), ::tolower); //! path if (kv.first == "path") { iter->second._path = kv.second; return true; } else if (kv.first == "name") { iter->second._name = kv.second; return true; } std::transform(kv.second.begin(), kv.second.end(), kv.second.begin(), ::tolower); //! level if (kv.first == "level") { if (kv.second == "trace" || kv.second == "all") { iter->second._level = ILog4zManager::LOG_LEVEL_TRACE; } else if (kv.second == "debug") { iter->second._level = ILog4zManager::LOG_LEVEL_DEBUG; } else if (kv.second == "info") { iter->second._level = ILog4zManager::LOG_LEVEL_INFO; } else if (kv.second == "warn" || kv.second == "warning") { iter->second._level = ILog4zManager::LOG_LEVEL_WARN; } else if (kv.second == "error") { iter->second._level = ILog4zManager::LOG_LEVEL_ERROR; } else if (kv.second == "alarm") { iter->second._level = ILog4zManager::LOG_LEVEL_ALARM; } else if (kv.second == "fatal") { iter->second._level = ILog4zManager::LOG_LEVEL_FATAL; } } //! display else if (kv.first == "display") { if (kv.second == "false" || kv.second == "0") { iter->second._display = false; } else { iter->second._display = true; } } //! output to file else if (kv.first == "outfile") { if (kv.second == "false" || kv.second == "0") { iter->second._outfile = false; } else { iter->second._outfile = true; } } //! limit file size else if (kv.first == "limitsize") { iter->second._limitsize = atoi(kv.second.c_str()); } //! display log in file line else if (kv.first == "fileline") { if (kv.second == "false" || kv.second == "0") { iter->second._fileLine = false; } else { iter->second._fileLine = true; } } //! enable/disable one logger else if (kv.first == "enable") { if (kv.second == "false" || kv.second == "0") { iter->second._enable = false; } else { iter->second._enable = true; } } return true; } static bool parseConfigFromString(std::string content, std::map & outInfo) { std::string key; int curLine = 1; std::string line; std::string::size_type curPos = 0; if (content.empty()) { return true; } do { std::string::size_type pos = std::string::npos; for (std::string::size_type i = curPos; i < content.length(); ++i) { //support linux/unix/windows LRCF if (content[i] == '\r' || content[i] == '\n') { pos = i; break; } } line = content.substr(curPos, pos - curPos); parseConfigLine(line, curLine, key, outInfo); curLine++; if (pos == std::string::npos) { break; } else { curPos = pos+1; } } while (1); return true; } bool isDirectory(std::string path) { #if defined (WIN32) || defined(_WIN64) return PathIsDirectoryA(path.c_str()) ? true : false; #else DIR * pdir = opendir(path.c_str()); if (pdir == NULL) { return false; } else { closedir(pdir); pdir = NULL; return true; } #endif } bool createRecursionDir(std::string path) { if (path.length() == 0) return true; std::string sub; fixPath(path); std::string::size_type pos = path.find('/'); while (pos != std::string::npos) { std::string cur = path.substr(0, pos-0); if (cur.length() > 0 && !isDirectory(cur)) { bool ret = false; #if defined (WIN32) || defined(_WIN64) ret = CreateDirectoryA(cur.c_str(), NULL) ? true : false; #else ret = (mkdir(cur.c_str(), S_IRWXU|S_IRWXG|S_IRWXO) == 0); #endif if (!ret) { return false; } } pos = path.find('/', pos+1); } return true; } unsigned int getProcessID() { #if defined (WIN32) || defined(_WIN64) return GetCurrentProcessId(); #else return getpid(); #endif } std::string getProcessName() { std::string name = "MainLog"; char buf[260] = {0}; #if defined (WIN32) || defined(_WIN64) if (GetModuleFileNameA(NULL, buf, 259) > 0) { name = buf; } std::string::size_type pos = name.rfind("\\"); if (pos != std::string::npos) { name = name.substr(pos+1, std::string::npos); } pos = name.rfind("."); if (pos != std::string::npos) { name = name.substr(0, pos-0); } #elif defined(__APPLE__) proc_name(getpid(), buf, 260); name = buf; return name;; #else sprintf(buf, "/proc/%d/cmdline", (int)getpid()); Log4zFileHandler i; i.open(buf, "rb"); if (!i.isOpen()) { return name; } name = i.readLine(); i.close(); std::string::size_type pos = name.rfind("/"); if (pos != std::string::npos) { name = name.substr(pos+1, std::string::npos); } #endif return name; } ////////////////////////////////////////////////////////////////////////// // LockHelper ////////////////////////////////////////////////////////////////////////// LockHelper::LockHelper() { #if defined (WIN32) || defined(_WIN64) InitializeCriticalSection(&_crit); #else //_crit = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&_crit, &attr); pthread_mutexattr_destroy(&attr); #endif } LockHelper::~LockHelper() { #if defined (WIN32) || defined(_WIN64) DeleteCriticalSection(&_crit); #else pthread_mutex_destroy(&_crit); #endif } void LockHelper::lock() { #if defined (WIN32) || defined(_WIN64) EnterCriticalSection(&_crit); #else pthread_mutex_lock(&_crit); #endif } void LockHelper::unLock() { #if defined (WIN32) || defined(_WIN64) LeaveCriticalSection(&_crit); #else pthread_mutex_unlock(&_crit); #endif } ////////////////////////////////////////////////////////////////////////// // SemHelper ////////////////////////////////////////////////////////////////////////// SemHelper::SemHelper() { #if defined (WIN32) || defined(_WIN64) _hSem = NULL; #elif defined(__APPLE__) _semid = NULL; #else _isCreate = false; #endif } SemHelper::~SemHelper() { #if defined (WIN32) || defined(_WIN64) if (_hSem != NULL) { CloseHandle(_hSem); _hSem = NULL; } #elif defined(__APPLE__) if (_semid) { dispatch_release(_semid); _semid = NULL; } #else if (_isCreate) { _isCreate = false; sem_destroy(&_semid); } #endif } bool SemHelper::create(int initcount) { if (initcount < 0) { initcount = 0; } #if defined (WIN32) || defined(_WIN64) if (initcount > 64) { return false; } _hSem = CreateSemaphore(NULL, initcount, 64, NULL); if (_hSem == NULL) { return false; } #elif defined(__APPLE__) _semid = dispatch_semaphore_create(initcount); if (!_semid) { return false; } #else if (sem_init(&_semid, 0, initcount) != 0) { return false; } _isCreate = true; #endif return true; } bool SemHelper::wait(int timeout) { #if defined (WIN32) || defined(_WIN64) if (timeout <= 0) { timeout = INFINITE; } if (WaitForSingleObject(_hSem, timeout) != WAIT_OBJECT_0) { return false; } #elif defined(__APPLE__) if (dispatch_semaphore_wait(_semid, dispatch_time(DISPATCH_TIME_NOW, timeout*1000)) != 0) { return false; } #else if (timeout <= 0) { return (sem_wait(&_semid) == 0); } else { struct timeval tm; gettimeofday(&tm, NULL); long long endtime = tm.tv_sec *1000 + tm.tv_usec/1000 + timeout; do { sleepMillisecond(50); int ret = sem_trywait(&_semid); if (ret == 0) { return true; } struct timeval tv_cur; gettimeofday(&tv_cur, NULL); if (tv_cur.tv_sec*1000 + tv_cur.tv_usec/1000 > endtime) { return false; } if (ret == -1 && errno == EAGAIN) { continue; } else { return false; } } while (true); return false; } #endif return true; } bool SemHelper::post() { #if defined (WIN32) || defined(_WIN64) return ReleaseSemaphore(_hSem, 1, NULL) ? true : false; #elif defined(__APPLE__) return dispatch_semaphore_signal(_semid) == 0; #else return (sem_post(&_semid) == 0); #endif } ////////////////////////////////////////////////////////////////////////// //! ThreadHelper ////////////////////////////////////////////////////////////////////////// bool ThreadHelper::start() { #if defined (WIN32) || defined(_WIN64) unsigned long long ret = _beginthreadex(NULL, 0, threadProc, (void *) this, 0, NULL); if (ret == -1 || ret == 0) { std::cout << "log4z: create log4z thread error! \r\n" < LOG_LEVEL_FATAL) { printf("%s", text); return; } #ifndef WIN32 printf("%s%s\e[0m", LOG_COLOR[level], text); #else AutoLock l(_scLock); HANDLE hStd = ::GetStdHandle(STD_OUTPUT_HANDLE); if (hStd == INVALID_HANDLE_VALUE) return; CONSOLE_SCREEN_BUFFER_INFO oldInfo; if (!GetConsoleScreenBufferInfo(hStd, &oldInfo)) { return; } else { SetConsoleTextAttribute(hStd, LOG_COLOR[level]); printf("%s", text); SetConsoleTextAttribute(hStd, oldInfo.wAttributes); } #endif return; } bool LogerManager::configFromStringImpl(std::string content, bool isUpdate) { unsigned int sum = 0; for (std::string::iterator iter = content.begin(); iter != content.end(); ++iter) { sum += (unsigned char)*iter; } if (sum == _checksum) { return true; } _checksum = sum; std::map loggerMap; if (!parseConfigFromString(content, loggerMap)) { std::cout << " !!! !!! !!! !!!" << std::endl; std::cout << " !!! !!! log4z load config file error" << std::endl; std::cout << " !!! !!! !!! !!!" << std::endl; return false; } for (std::map::iterator iter = loggerMap.begin(); iter != loggerMap.end(); ++iter) { LoggerId id = LOG4Z_INVALID_LOGGER_ID; id = findLogger(iter->second._key.c_str()); if (id == LOG4Z_INVALID_LOGGER_ID) { if (isUpdate) { continue; } else { id = createLogger(iter->second._key.c_str()); if (id == LOG4Z_INVALID_LOGGER_ID) { continue; } } } enableLogger(id, iter->second._enable); setLoggerName(id, iter->second._name.c_str()); setLoggerPath(id, iter->second._path.c_str()); setLoggerLevel(id, iter->second._level); setLoggerFileLine(id, iter->second._fileLine); setLoggerDisplay(id, iter->second._display); setLoggerOutFile(id, iter->second._outfile); setLoggerLimitsize(id, iter->second._limitsize); } return true; } //! read configure and create with overwriting bool LogerManager::config(const char* configPath) { if (!_configFile.empty()) { std::cout << " !!! !!! !!! !!!" << std::endl; std::cout << " !!! !!! log4z configure error: too many calls to Config. the old config file=" << _configFile << ", the new config file=" << configPath << " !!! !!! " << std::endl; std::cout << " !!! !!! !!! !!!" << std::endl; return false; } _configFile = configPath; Log4zFileHandler f; f.open(_configFile.c_str(), "rb"); if (!f.isOpen()) { std::cout << " !!! !!! !!! !!!" << std::endl; std::cout << " !!! !!! log4z load config file error. filename=" << configPath << " !!! !!! " << std::endl; std::cout << " !!! !!! !!! !!!" << std::endl; return false; } return configFromStringImpl(f.readContent().c_str(), false); } //! read configure and create with overwriting bool LogerManager::configFromString(const char* configContent) { return configFromStringImpl(configContent, false); } //! create with overwriting LoggerId LogerManager::createLogger(const char* key) { if (key == NULL) { return LOG4Z_INVALID_LOGGER_ID; } std::string copyKey = key; trimLogConfig(copyKey); LoggerId newID = LOG4Z_INVALID_LOGGER_ID; { std::map::iterator iter = _ids.find(copyKey); if (iter != _ids.end()) { newID = iter->second; } } if (newID == LOG4Z_INVALID_LOGGER_ID) { if (_lastId +1 >= LOG4Z_LOGGER_MAX) { showColorText("log4z: CreateLogger can not create|writeover, because loggerid need < LOGGER_MAX! \r\n", LOG_LEVEL_FATAL); return LOG4Z_INVALID_LOGGER_ID; } newID = ++ _lastId; _ids[copyKey] = newID; _names[newID] = copyKey; _loggers[newID]._enable = true; _loggers[newID]._key = copyKey; _loggers[newID]._name = copyKey; } return newID; } bool LogerManager::start() { if (_runing) { return false; } return ThreadHelper::start(); } bool LogerManager::stop() { if (_runing == true) { _runing = false; wait(); return true; } return false; } bool LogerManager::prePushLog(const char * name, int level) { LoggerId id = getLoggerId(name); if(id == LOG4Z_INVALID_LOGGER_ID) return false; return prePushLog(id,level); } bool LogerManager::prePushLog(LoggerId id, int level) { if (id < 0 || id > _lastId || !_runing || !_loggers[id]._enable) { return false; } if (level < _loggers[id]._level) { return false; } return true; } bool LogerManager::pushLog(const char * name, int level, const char * filter, const char * log, const char * file, int line, const char *func, const void * pRetAddr) { LoggerId id = getLoggerId(name); if(id == LOG4Z_INVALID_LOGGER_ID) return false; return pushLog(id,level,filter,log,file,line,func,pRetAddr); } bool LogerManager::pushLog(LoggerId id, int level, const char * filter, const char * log, const char * file, int line, const char *func, const void * pRetAddr) { // discard log if (id < 0 || id > _lastId || !_runing || !_loggers[id]._enable) { return false; } //filter log if (level < _loggers[id]._level) { return false; } //create log data LogData * pLog = new LogData; pLog->_id =id; pLog->_level = level; pLog->_filter = filter; pLog->_content = log; if(file != NULL) pLog->_file = file; pLog->_func = func; pLog->_line = line; //append precise time to log { #if defined (WIN32) || defined(_WIN64) FILETIME ft; GetSystemTimeAsFileTime(&ft); unsigned long long now = ft.dwHighDateTime; now <<= 32; now |= ft.dwLowDateTime; now /=10; now -=11644473600000000ULL; now /=1000; pLog->_time = now; #else struct timeval tm; gettimeofday(&tm, NULL); pLog->_time = tm.tv_sec*1000+tm.tv_usec/1000; #endif } #if defined (WIN32) || defined(_WIN64) char szPath[MAX_PATH]={0}; char *pModuleName = szPath; if(pRetAddr) { HMODULE hMod = 0; GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,(LPCSTR)pRetAddr,&hMod); GetModuleFileNameA(hMod,szPath,100); pModuleName = strrchr(szPath,'\\')+1; } pLog->_moduleName = pModuleName; pLog->_pid = GetCurrentProcessId(); pLog->_tid = GetCurrentThreadId(); #endif AutoLock l(_logLock); _logs.push_back(pLog); _ullStatusTotalPushLog ++; return true; } //! 查找ID from name LoggerId LogerManager::findLogger(const char * key) { std::map::iterator iter; iter = _ids.find(key); if (iter != _ids.end()) { return iter->second; } return LOG4Z_INVALID_LOGGER_ID; } bool LogerManager::enableLogger(LoggerId id, bool enable) { if (id <0 || id > _lastId) return false; _loggers[id]._enable = enable; return true; } bool LogerManager::setLoggerLevel(LoggerId id, int level) { if (id <0 || id > _lastId || level < LOG_LEVEL_TRACE || level >LOG_LEVEL_FATAL) return false; _loggers[id]._level = level; return true; } bool LogerManager::setLoggerDisplay(LoggerId id, bool enable) { if (id <0 || id > _lastId) return false; _loggers[id]._display = enable; return true; } bool LogerManager::setLoggerOutFile(LoggerId id, bool enable) { if (id <0 || id > _lastId) return false; _loggers[id]._outfile = enable; return true; } void LogerManager::setOutputFileBuilder(IOutputFileBuilder *pOutputFileBuilder) { AutoLock l(_logLock); m_pOutputFileBuilder = pOutputFileBuilder; } bool LogerManager::setLoggerFileMaxNum(unsigned int maxnum) { _maxFileNum = maxnum; return true; } bool LogerManager::setLoggerLimitsize(LoggerId id, unsigned int limitsize) { if (id <0 || id > _lastId) return false; if (limitsize == 0 ) {limitsize = (unsigned int)-1;} _loggers[id]._limitsize = limitsize; return true; } bool LogerManager::setLoggerFileLine(LoggerId id, bool enable) { if (id <0 || id > _lastId) return false; _loggers[id]._fileLine = enable; return true; } bool LogerManager::setLoggerName(LoggerId id, const char * name) { if (id <0 || id > _lastId) return false; //the name by main logger is the process name and it's can't change. if (id == LOG4Z_MAIN_LOGGER_ID) return false; if (name == NULL || strlen(name) == 0) { return false; } AutoLock l(_hotLock); if (_loggers[id]._name != name) { _loggers[id]._name = name; } return true; } bool LogerManager::setLoggerPath(LoggerId id, const char * path) { if (id <0 || id > _lastId) return false; std::string copyPath; if (path == NULL || strlen(path) == 0) { copyPath = LOG4Z_DEFAULT_PATH; } else { copyPath = path; } { char ch = copyPath.at(copyPath.length() - 1); if (ch != '\\' && ch != '/') { copyPath.append("/"); } } AutoLock l(_hotLock); if (copyPath != _loggers[id]._path) { _loggers[id]._path = copyPath; } return true; } bool LogerManager::setAutoUpdate(int interval) { _hotUpdateInterval = interval; return true; } bool LogerManager::updateConfig() { if (_configFile.empty()) { //LOGW("log4z update config file error. filename is empty."); return false; } Log4zFileHandler f; f.open(_configFile.c_str(), "rb"); if (!f.isOpen()) { std::cout << " !!! !!! !!! !!!" << std::endl; std::cout << " !!! !!! log4z load config file error. filename=" << _configFile << " !!! !!! " << std::endl; std::cout << " !!! !!! !!! !!!" << std::endl; return false; } return configFromStringImpl(f.readContent().c_str(), true); } bool LogerManager::isLoggerEnable(LoggerId id) { if (id <0 || id > _lastId) return false; return _loggers[id]._enable; } unsigned int LogerManager::getStatusActiveLoggers() { unsigned int actives = 0; for (int i=0; i<= _lastId; i++) { if (_loggers[i]._enable) { actives ++; } } return actives; } bool LogerManager::openLogger(LogData * pLog) { int id = pLog->_id; if (id < 0 || id >_lastId) { showColorText("log4z: openLogger can not open, invalid logger id! \r\n", LOG_LEVEL_FATAL); return false; } LoggerInfo * pLogger = &_loggers[id]; if (!pLogger->_enable || !pLogger->_outfile || pLog->_level < pLogger->_level) { return false; } bool needChangeFile = pLogger->_curWriteLen > pLogger->_limitsize * 1024 * 1024; if ( needChangeFile) { if (pLogger->_handle.isOpen()) { pLogger->_handle.close(); } std::string name = pLogger->_name; char buf[MAX_PATH]; tm t = timeToTm(pLogger->_curFileCreateTime); const int KMaxLogIndex = _maxFileNum; m_pOutputFileBuilder->buildOutputFile(buf,100,t,name.c_str(),_pid,KMaxLogIndex); std::string path = pLogger->_path+buf; remove(path.c_str()); for(int i=KMaxLogIndex;i>0;i--)//max to _maxFileNum log index. { m_pOutputFileBuilder->buildOutputFile(buf,100,t,name.c_str(),_pid,i-1); std::string pathOld = pLogger->_path+buf; m_pOutputFileBuilder->buildOutputFile(buf,100,t,name.c_str(),_pid,i); std::string pathNew = pLogger->_path+buf; rename(pathOld.c_str(),pathNew.c_str()); } } if (!pLogger->_handle.isOpen()) { pLogger->_curFileCreateTime = pLog->_time; pLogger->_curWriteLen = 0; tm t = timeToTm(pLogger->_curFileCreateTime); std::string name; std::string path; _hotLock.lock(); name = pLogger->_name; path = pLogger->_path; _hotLock.unLock(); char buf[100] = { 0 }; if (!isDirectory(path)) { createRecursionDir(path); } m_pOutputFileBuilder->buildOutputFile(buf,100,t,name.c_str(),_pid,0); path += buf; pLogger->_handle.open(path.c_str(), "ab"); if (!pLogger->_handle.isOpen()) { pLogger->_outfile = false; return false; } pLogger->_curWriteLen = pLogger->_handle.size(); return true; } return true; } bool LogerManager::closeLogger(LoggerId id) { if (id < 0 || id >_lastId) { showColorText("log4z: closeLogger can not close, invalid logger id! \r\n", LOG_LEVEL_FATAL); return false; } LoggerInfo * pLogger = &_loggers[id]; if (pLogger->_handle.isOpen()) { pLogger->_handle.close(); return true; } return false; } bool LogerManager::popLog(LogData *& log) { AutoLock l(_logLock); if (_logs.empty()) { return false; } log = _logs.front(); _logs.pop_front(); return true; } void LogerManager::run() { _runing = true; pushLog(0, LOG_LEVEL_ALARM, "logger", "----------------- log4z thread started! ----------------------------", __FILE__, __LINE__ , __FUNCTION__,_ReturnAddress()); for (int i = 0; i <= _lastId; i++) { if (_loggers[i]._enable) { std::stringstream ss; ss <<"logger id=" <_id]; if (!curLogger._enable || pLog->_level _time/1000); if (pLog->_file.empty() || !_loggers[pLog->_id]._fileLine) { #if defined (WIN32) || defined(_WIN64) int ret = _snprintf_s(pszBuf, LOG4Z_LOG_BUF_SIZE, _TRUNCATE, "pid=%u tid=%u %d-%02d-%02d %02d:%02d:%02d.%03d %s %s %s \"%s\"\r\n", pLog->_pid, pLog->_tid, tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec, (int)(pLog->_time%1000), LOG_STRING[pLog->_level], pLog->_moduleName.c_str(), pLog->_filter.c_str(), pLog->_content.c_str()); if (ret == -1) { ret = LOG4Z_LOG_BUF_SIZE - 1; } nContentLen = ret; #else int ret = snprintf(pszBuf, LOG4Z_LOG_BUF_SIZE, "%d-%02d-%02d %02d:%02d:%02d.%03d %s %s %s\r\n", tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec, (int)(pLog->_time%1000), LOG_STRING[pLog->_level],pLog->_filter.c_str(), pLog->_content.c_str()); if (ret == -1) { ret = 0; } if (ret >= LOG4Z_LOG_BUF_SIZE) { ret = LOG4Z_LOG_BUF_SIZE-1; } nContentLen = ret; #endif } else { #if defined (WIN32) || defined(_WIN64) const char * pNameBegin = strrchr(pLog->_file.c_str(),'\\'); #else const char * pNameBegin = strrchr(pLog->_file.c_str(),'/'); #endif if(!pNameBegin) pNameBegin = pLog->_file.c_str(); else pNameBegin ++; #if defined (WIN32) || defined(_WIN64) int ret = _snprintf_s(pszBuf, LOG4Z_LOG_BUF_SIZE, _TRUNCATE, "pid=%u tid=%u %d-%02d-%02d %02d:%02d:%02d.%03d %s %s %s \"%s\" %s (%s):%d\r\n", pLog->_pid, pLog->_tid, tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec, (int)(pLog->_time%1000), LOG_STRING[pLog->_level], pLog->_moduleName.c_str(), pLog->_filter.c_str(), pLog->_content.c_str(), pLog->_func.c_str(), pNameBegin, pLog->_line); if (ret == -1) { ret = LOG4Z_LOG_BUF_SIZE - 1; } nContentLen = ret; #else int ret = snprintf(pszBuf, LOG4Z_LOG_BUF_SIZE, "%d-%02d-%02d %02d:%02d:%02d.%03d %s %s %s (%s):%d %s\r\n", tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec, (int)(pLog->_time%1000), LOG_STRING[pLog->_level], pLog->_filter.c_str(), pLog->_content.c_str(), pNameBegin, pLog->_line, pLog->_func.c_str()); if (ret == -1) { ret = 0; } if (ret >= LOG4Z_LOG_BUF_SIZE) { ret = LOG4Z_LOG_BUF_SIZE-1; } nContentLen = ret; #endif } } if(m_pListener) { m_pListener->onOutputLog(pLog->_level,pLog->_filter.c_str(),pLog->_content.c_str(),pLog->_content.length(),pLog->_time); } if (curLogger._display) { showColorText(pszBuf, pLog->_level); #if defined (WIN32) || defined(_WIN64) OutputDebugStringA(pszBuf); #endif } if (curLogger._outfile) { if (!openLogger(pLog)) { delete pLog; pLog = NULL; continue; } curLogger._handle.write(pszBuf, nContentLen); curLogger._curWriteLen += (unsigned int)nContentLen; needFlush[pLog->_id] ++; _ullStatusTotalWriteFileCount++; _ullStatusTotalWriteFileBytes += nContentLen; } else { _ullStatusTotalWriteFileCount++; _ullStatusTotalWriteFileBytes += nContentLen; } delete pLog; pLog = NULL; } for (int i=0; i<=_lastId; i++) { if (_loggers[i]._enable && needFlush[i] > 0) { _loggers[i]._handle.flush(); needFlush[i] = 0; } if(!_loggers[i]._enable && _loggers[i]._handle.isOpen()) { _loggers[i]._handle.close(); } } //! delay. sleepMillisecond(100); //! quit if (!_runing && _logs.empty()) { break; } if (_hotUpdateInterval != 0 && time(NULL) - lastCheckUpdate > _hotUpdateInterval) { updateConfig(); lastCheckUpdate = time(NULL); } } for (int i=0; i <= _lastId; i++) { if (_loggers[i]._enable) { _loggers[i]._enable = false; closeLogger(i); } } delete []pszBuf; } void LogerManager::setOutputListener(IOutputListener *pListener){ AutoLock l(_hotLock); m_pListener = pListener; } namespace SOUI{ namespace LOG4Z{ SOUI_COM_C BOOL SOUI_COM_API SCreateInstance(IObjRef **ppLogMgr) { *ppLogMgr = new LogerManager; return TRUE; } } }