Linux 日志文件管理——限制大小
设计思路:
1 用一个INI配置文件管理日志目录,日志文件限制的大小,特殊的日志名,特殊日志的大小限制。
2 读取INI文件中的所有信息:每一个日志目录对应的大小限制,每一个特殊日志对应的大小限制。如特殊日志在既定的日志目录中需去除。
3 按设置的大小循环检测并清理每一个日志文件。
4 监听有名管道的信号,如读取到了修改了INI文件的信号,则重新开始读取,循环。
代码:
LogSizeLimit.h
#ifndef LOGSIZELIMIT_H_ #define LOGSIZELIMIT_H_ #include <map> #include <vector> #include <string.h> #include "GlobDefine.h" #include "Mutex.h" static const INT8 *IniFileName = "/LogService/LogSizeManage.ini"; //static const UINT32 LogFileSizeDefault = 2 * 1024 * 1024; static const INT8 * LogFileSizeDefault = "2M"; static const INT8 *LogPipeName = "/LogService/LOGSIZE.fifo"; static const INT8 *ChangedStr = "changed"; static const UINT32 CleanInterval = 4; static const UINT32 LogFilePathMaxLen = 64; static const UINT32 LogFileAttrMaxLen = 8; class LogSizeLimit { // static Mutex mutex; public: LogSizeLimit(const string iniFile); ~LogSizeLimit(); bool start(); bool getChangeFlag() { return changeFlag; } void setChangeFlag(bool status) { changeFlag = status; } private: string _iniFile; map<string, string> _catelogAttr; map<string, string> _specialFileAttr; map<string, vector<string> > _logFiles; bool changeFlag; static void *run(void *args); static void *listenChanged(void*args); bool readConfig(); bool limitLogFile(); bool readFileList(const INT8 *mapStrPath, const INT8 *basePath); bool checkAndCleanLog(string logName, INT32 size); INT32 transSizeToBytes(const INT8 * size); bool readPipeMsg(); }; #endif /* LOGSIZELIMIT_H_ */
LogSizeLimit.cpp
#include <pthread.h> #include <unistd.h> #include <dirent.h> #include <sys/stat.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include "LogSizeLimit.h" #include "IniConfigFile.h" #include "Logger.h" #include "FileOperation.h" using namespace FrameWork; LogSizeLimit::LogSizeLimit(const string iniFile) : _iniFile(iniFile), changeFlag(true) { } LogSizeLimit::~LogSizeLimit() { } bool LogSizeLimit::start() { pthread_t tid, changetid; pthread_create(&tid, NULL, run, (void*) this); pthread_create(&changetid, NULL, listenChanged, (void*) this); return true; } bool LogSizeLimit::readConfig() { //Clean map vars map<string, string>::iterator iter_tmp = _catelogAttr.begin(); while (iter_tmp != _catelogAttr.end()) { _catelogAttr.erase(iter_tmp); iter_tmp++; } //Clean map vars iter_tmp = _specialFileAttr.begin(); while (iter_tmp != _specialFileAttr.end()) { _specialFileAttr.erase(iter_tmp); iter_tmp++; } //Clean map vars map<string, vector<string> >::iterator mvIter_tmp = _logFiles.begin(); while (mvIter_tmp != _logFiles.end()) { _logFiles.erase(mvIter_tmp); mvIter_tmp++; } if (!FileOperation::isExistFile(_iniFile)) { Logger::GetInstance().Fatal("Can not find ini file : %s !", _iniFile.c_str()); return false; } //Create ini file handling var. IniConfigFile iniSet(_iniFile.c_str()); INT8 logPath[LogFilePathMaxLen] = { 0 }; INT8 logFileAttr[LogFileAttrMaxLen] = { 0 }; INT8 specialFile_1[LogFilePathMaxLen] = { 0 }; INT8 specialFileAttr_1[LogFileAttrMaxLen] = { 0 }; const INT8 *catelogBase = "catelog_"; const INT8 *limitBase = "logSizeLimit_"; const INT8 *specialFileBase = "specialFile_"; const INT8 *specialLimitBase = "specialFileSizeLimit_"; INT8 catelogCnt = '1'; bool isExist = true; INT8 catelogIndex[32] = { 0 }; INT8 logSizeIndex[32] = { 0 }; while (isExist) { memset(catelogIndex, 0, 32); memset(logSizeIndex, 0, 32); memset(logPath, 0, LogFilePathMaxLen); memset(logFileAttr, 0, LogFileAttrMaxLen); memcpy(catelogIndex, catelogBase, strlen(catelogBase)); memcpy(catelogIndex + strlen(catelogBase), &catelogCnt, 1); memcpy(logSizeIndex, limitBase, strlen(limitBase)); memcpy(logSizeIndex + strlen(limitBase), &catelogCnt, 1); //section key value do { if (iniSet.readIniConfFile("LogSizeManage", catelogIndex, logPath, LogFilePathMaxLen) != true) { Logger::GetInstance().Error("Can not locate %s !", catelogIndex); isExist = false; break; } else Logger::GetInstance().Info("Get a catelog %s=%s !", catelogIndex, logPath); if (iniSet.readIniConfFile("LogSizeManage", logSizeIndex, logFileAttr, LogFilePathMaxLen) != true) { Logger::GetInstance().Error( "Can not get size %s, using default size %s !", logSizeIndex, LogFileSizeDefault); memcpy(logFileAttr, LogFileSizeDefault, strlen(LogFileSizeDefault)); } else Logger::GetInstance().Info("Get a log size attr %s !", logFileAttr); _catelogAttr[logPath] = logFileAttr; isExist = true; map<string, string>::iterator iter; iter = _catelogAttr.begin(); for (UINT32 i = 1; i < _catelogAttr.size(); i++) iter++; cout << "_catelogAttr_size : " << _catelogAttr.size() << " begin : " << iter->first << " end : " << iter->second << endl; } while (0); // read the normal catelogs memset(catelogIndex, 0, 32); memset(logSizeIndex, 0, 32); memset(logPath, 0, LogFilePathMaxLen); memset(logFileAttr, 0, LogFileAttrMaxLen); memcpy(catelogIndex, specialFileBase, strlen(specialFileBase)); memcpy(catelogIndex + strlen(specialFileBase), &catelogCnt, 1); memcpy(logSizeIndex, specialLimitBase, strlen(specialLimitBase)); memcpy(logSizeIndex + strlen(specialLimitBase), &catelogCnt, 1); do { if (iniSet.readIniConfFile("LogSizeManage", catelogIndex, logPath, LogFilePathMaxLen) != true) { Logger::GetInstance().Error("Can not locate a special log %s !", catelogIndex); break; } else Logger::GetInstance().Info("Get a special log %s=%s !", catelogIndex, logPath); if (iniSet.readIniConfFile("LogSizeManage", logSizeIndex, logFileAttr, LogFilePathMaxLen) != true) { Logger::GetInstance().Error( "Can not get log size %s, using default size %s !", logFileAttr, LogFileSizeDefault); memcpy(logFileAttr, LogFileSizeDefault, strlen(LogFileSizeDefault)); // break; } else Logger::GetInstance().Info("Get a special log size %s !", logFileAttr); if (!isExist) isExist = true; _specialFileAttr[logPath] = logFileAttr; map<string, string>::iterator iter; iter = _specialFileAttr.begin(); cout << "_specialFileAttr_size : " << _specialFileAttr.size() << " begin : " << iter->first << " end : " << iter->second << endl; } while (0); // read the special log files catelogCnt++; } //while return true; } //struct dirent //{ // long d_ino; /* inode number 索引节点号 */ // off_t d_off; /* offset to this dirent 在目录文件中的偏移 */ // unsigned short d_reclen; /* length of this d_name 文件名长 */ // unsigned char d_type; /* the type of d_name 文件类型 */其中d_type表明该文件的类型:文件(8)、目录(4)、链接文件(10)等。 // char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */ //} bool LogSizeLimit::readFileList(const INT8 *mapStrPath, const INT8 *basePath) { DIR *dir; struct dirent *direntPtr; char base[LogFilePathMaxLen] = { 0 }; if ((dir = opendir(basePath)) == NULL) { Logger::GetInstance().Error("Can not open directory %s ! ", basePath); return false; } while ((direntPtr = readdir(dir)) != NULL) { if (strcmp(direntPtr->d_name, ".") == 0 || strcmp(direntPtr->d_name, "..") == 0) ///current dir OR parrent dir continue; else if (direntPtr->d_type == 8) { ///file string fileName; fileName += basePath; if (basePath[strlen(basePath) - 1] != '/') fileName += '/'; fileName += direntPtr->d_name; _logFiles[mapStrPath].push_back(fileName); } else if (direntPtr->d_type == 10) ///link file Logger::GetInstance().Info("A link file : %s !", direntPtr->d_name); else if (direntPtr->d_type == 4) ///dir { memset(base, '\0', sizeof(base)); strcpy(base, basePath); if (base[strlen(base) - 1] != '/') strcat(base, "/"); strcat(base, direntPtr->d_name); readFileList(mapStrPath, base); } } closedir(dir); return true; } bool LogSizeLimit::limitLogFile() { changeFlag = true; map<string, string>::iterator mIter; for (mIter = _catelogAttr.begin(); mIter != _catelogAttr.end(); mIter++) { if (!FileOperation::isExisteDirectory(mIter->first)) { Logger::GetInstance().Fatal("Catelog %s is not existed !", mIter->first.c_str()); } else { Logger::GetInstance().Info("catelog %s is existed !", mIter->first.c_str()); readFileList(mIter->first.c_str(), mIter->first.c_str()); } } // map<string, vector<string> >::iterator mvIter = _logFiles.begin(); vector<string>::iterator vIter; bool deleteFlag = false; while (mvIter != _logFiles.end()) { vIter = mvIter->second.begin(); while (vIter != mvIter->second.end()) { Logger::GetInstance().Info("Log file : %s : %s ", mvIter->first.c_str(), vIter->c_str()); mIter = _specialFileAttr.begin(); for (; mIter != _specialFileAttr.end(); mIter++) { deleteFlag = false; //Remove the special log file from the normal catelog if (strncmp(vIter->c_str(), mIter->first.c_str(), strlen(vIter->c_str())) == 0) { Logger::GetInstance().Info( "Remove log file for special log : %s !", vIter->c_str()); vIter = _logFiles[mvIter->first].erase(vIter); deleteFlag = true; break; } } if (!deleteFlag) vIter++; } mvIter++; } // Check change signal while (changeFlag) { mIter = _catelogAttr.begin(); for (; mIter != _catelogAttr.end(); mIter++) { vIter = _logFiles[mIter->first].begin(); for (; vIter != _logFiles[mIter->first].end(); vIter++) { // cout << "log clean : " << *vIter << " size ; " // << transSizeToBytes(mIter->second.c_str()) << endl; checkAndCleanLog(*vIter, transSizeToBytes(mIter->second.c_str())); } } mIter = _specialFileAttr.begin(); for (; mIter != _specialFileAttr.end(); mIter++) { // cout << "special log clean : "<<mIter->first<<endl; checkAndCleanLog(mIter->first, transSizeToBytes(mIter->second.c_str())); } sleep(CleanInterval); } return true; } bool LogSizeLimit::checkAndCleanLog(string logName, INT32 size) { struct stat statbuff; if (-1 == stat(logName.c_str(), &statbuff)) { // Logger::GetInstance().Error("Can not Stat() log file %s !", // logName.c_str()); return false; } //Clean file if (statbuff.st_size >= size) { fstream fout(logName.c_str(), ios::out | ios::trunc); fout.close(); } return true; } // Get bytes INT32 LogSizeLimit::transSizeToBytes(const INT8 * size) { if (size[strlen(size) - 1] == 'M' || size[strlen(size) - 1] == 'm') { INT8 msize[32] = { 0 }; memcpy(msize, size, strlen(size) - 1); return atoi(msize) * 1024 * 1024; } else if (size[strlen(size) - 1] == 'K' || size[strlen(size) - 1] == 'k') { INT8 ksize[32] = { 0 }; memcpy(ksize, size, strlen(size) - 1); return atoi(ksize) * 1024; } else { Logger::GetInstance().Error("Unknow size %s !", size); return 0; } } void *LogSizeLimit::run(void *args) { LogSizeLimit *logLimit = (LogSizeLimit*) args; do { logLimit->readConfig(); logLimit->limitLogFile(); } while (logLimit->getChangeFlag() == false); return NULL; } void *LogSizeLimit::listenChanged(void*args) { LogSizeLimit *logLimit = (LogSizeLimit*) args; logLimit->readPipeMsg(); return NULL; } // Listen fifo signal bool LogSizeLimit::readPipeMsg() { INT8 buf_r[100]; INT32 fd; INT32 nread; if ((mkfifo(LogPipeName, O_CREAT | O_EXCL) < 0) && (errno != EEXIST)) Logger::GetInstance().Error("Can not create fifo server !"); memset(buf_r, 0, sizeof(buf_r)); fd = open(LogPipeName, O_RDONLY | O_NONBLOCK, 0); if (fd == -1) { Logger::GetInstance().Error("Can not open fifo %s for %s !", LogPipeName, strerror(errno)); return false; } while (1) { memset(buf_r, 0, sizeof(buf_r)); if ((nread = read(fd, buf_r, 100)) == -1) { // if (errno == EAGAIN) // printf("no data yet\n"); } if ((strncmp(buf_r, ChangedStr, strlen(ChangedStr))) == 0) { Logger::GetInstance().Info("Get changed cmd !"); setChangeFlag(false); } sleep(2); } unlink(LogPipeName); return true; }
使用
echo changed > /LogService/LOGSIZE.fifo
可以使更新的INI配置立即生效。
INI配置文件:
[LogSizeManage] catelog_1=/nand/log/ logSizeLimit_1=2M catelog_2=/var/log specialFile_1=/nand/log/App.log specialFileSizeLimit_1=4M
种树最好的时间是十年前,其次是现在。