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

 

posted @ 2018-07-26 10:18  bobojiang2016  阅读(8335)  评论(0编辑  收藏  举报