设计思路:
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