版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Oyj1020/article/details/76222450
说明:这是一个Linux 环境下的C语言日志类,使用面向对象的编程方式实现。它可以循环生成日志、设置日志最大数量、日志生成周期、日志等级设置
logger.h文件
#ifndef _LOGGER_H
#define _LOGGER_H
/*
作 者: ouyongjiu
说明:这是一个C语言日志文件类,使用面向对象编程方式,参看handy logging类编写。它可以设置循环生成日志,并以时间命名。设置允许的最大日志数量,设置日志生成周期,日志等级
使用方法:
1.初始化日志结构体
LoggerInit(&gLogger, KLALL,time(NULL));
2.设置日志目录、日志文件名
gLogger.setFileName(&gLogger, "./log/","mis.log");
3.设置日志循环周期。单位:秒
gLogger.setRotateInterval(&gLogger, 60);
4.设置日志允许的最大日志数量
gLogger.setNumOfLog(&gLogger, 2);
5.使用日志类写入日志
trace("test mis");
6.程序结束时,关闭日志。
gLogger.closefile(&gLogger);
例子:
LoggerInit(&gLogger, KLALL,time(NULL));
gLogger.setFileName(&gLogger, "./log/","mis.log");
gLogger.setRotateInterval(&gLogger, 60);
gLogger.setNumOfLog(&gLogger, 2);
int cnt = 0;
for (;;)
{
trace("test mis");
sleep(1);
cnt++;
if (cnt==60)
{
printf("60 second\n");
cnt = cnt % 60;
}
}
gLogger.closefile(&gLogger);
*/
#define LFATAL 0
#define LERROR 1
#define LUERR 2
#define LWARN 3
#define LINFO 4
#define LDEBUG 5
#define LTRACE 6
#define LALL 7
#define PRIVATE
#define PUBLIC
typedef struct _tgLogger
{
PRIVATE //私有成员变量
char levelStrs_[LALL + 1][20]; //日志等级名称,写日志内容时,一并写入日志等级
int fd_; //文件描述符
int level_; //日志等级
long lastRotate_; //最近一次周期时间戳
long long realRotate_; //新文件开始的时间戳
long rotateInterval_; //新建文件周期,单位:秒
char fileFullname_[100]; //文件全名=文件路径+文件名
char filedir_[100]; //文件路径、文件目录
char filename_[100]; //文件名
int nNumofLog_; //允许的最大日志文件数
PUBLIC //公有函数指针变量
/*************************************************************************************
作 者: ouyongjiu
函数功能:将日志写入到文件
输入参数:
logger 日志结构体
level 日志等级
file 日志内容记录的文件名
line 日志内容所在目标记录文件所在行
func __VA_ARGS__,可变参数
fmt 可变参数
... 可变参数
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*logv)(struct _tgLogger* logger, int level, const char* file, int line, const char* func, const char* fmt ,...);
/*************************************************************************************
作 者: ouyongjiu
函数功能:设置文件路径和文件名
输入参数:
logger 日志结构体
filedir 文件路径
filename 文件名
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*setFileName)(struct _tgLogger* logger, const char* filedir, const char* filename);
/*************************************************************************************
作 者: ouyongjiu
函数功能:设置文件路径和文件名
输入参数:
logger 日志结构体
level 日志等级,取值域={KLFATAL,KLERROR, KLUERR, KLWARN, KLINFO, KLDEBUG, KLTRACE, KLALL}
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*setLogLevel)(struct _tgLogger* logger, int level);
/*************************************************************************************
作 者: ouyongjiu
函数功能:关闭文件描述符
输入参数:
logger 日志结构体
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*closefile)(struct _tgLogger* logger);
/*************************************************************************************
作 者: ouyongjiu
函数功能:日志文件生成周期
输入参数:
logger 日志结构体
rotateInterval 日志文件生成周期,建议>=60s。单位:秒
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*setRotateInterval)(struct _tgLogger* logger, long rotateInterval);
/*************************************************************************************
作 者: ouyongjiu
函数功能:设置日志最大的个数
输入参数:
logger 日志结构体
nNum 允许的最大日志数
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*setNumOfLog)(struct _tgLogger* logger, int nMaxNum);
PRIVATE //私有函数指针变量
/*************************************************************************************
作 者: ouyongjiu
函数功能:检查文件周期,文件从生成的到现在时间大于时间周期,则保存文件并重命名,开启新的文件
输入参数:
logger 日志结构体
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*_maybeRotate)(struct _tgLogger* logger);
/*************************************************************************************
作 者: ouyongjiu
函数功能:删除时间最早的文件。约束条件:1.日志目录下的文件个数大于允许生成的日志数。
输入参数:
logger 日志结构体
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void(*_delFileRecursive)(struct _tgLogger*);
}Logger;
PUBLIC //提供外部使用的函数
void logv(Logger* logger, int level, const char* file, int line, const char* func, const char* fmt, ...);
void setFileName(Logger* logger, const char* filedir, const char* filename);
void setLogLevel(Logger* logger, int level);
void setRotateInterval(Logger* logger, long rotateInterval);
void setNumOfLog(Logger* logger, int nNum);
void closefile(Logger* logger);
PRIVATE //内部使用
void _maybeRotate(Logger*);
void _delFileRecursive(Logger* logger);
//////////////////////////////////////////////////////////////////////////
/*************************************************************************************
作 者: ouyongjiu
函数功能:初始化日志结构体
输入参数:
logger 日志结构体
level 日志等级
lastRotate 初始化的时间戳
输出参数: 无
返 回 值: 无
备 注:
*************************************************************************************/
void LoggerInit(Logger* logger, int level, long lastRotate);
extern Logger gLogger;//定义一个全局的日志类
Logger gLogger;
#define hlog(level, ...) \
do { \
if (level<=gLogger.level_) { \
gLogger.logv(&gLogger,level, __FILE__, __LINE__, __func__, __VA_ARGS__); \
} \
} while(0)
#define trace(...) hlog(LTRACE, __VA_ARGS__)
#define info(...) hlog(LINFO, __VA_ARGS__)
#define debug(...) hlog(LDEBUG, __VA_ARGS__)
#define warn(...) hlog(LWARN, __VA_ARGS__)
#define error(...) hlog(LERROR, __VA_ARGS__)
#define fatal(...) hlog(LFATAL, __VA_ARGS__)
#define fatalif(b, ...) do { if((b)) { hlog(LFATAL, __VA_ARGS__); } } while (0)
#define check(b, ...) do { if((b)) { hlog(LFATAL, __VA_ARGS__); } } while (0)
#define exitif(b, ...) do { if ((b)) { hlog(LERROR, __VA_ARGS__); _exit(1); }} while(0)
#define Logger_setloglevel(l) gLogger.setLogLevel(&gLogger,l)
#endif
logger.c文件
#include "logger.h"
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <time.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <dirent.h>
#define MIN(x,y) (((x)<(y))?(x) : (y))
void LoggerInit(Logger* logger, int level, long lastRotate)
{
logger->logv = logv;
logger->setFileName = setFileName;
logger->setLogLevel = setLogLevel;
logger->closefile = closefile;
logger->_maybeRotate = _maybeRotate;
logger->setRotateInterval = setRotateInterval;
logger->setNumOfLog = setNumOfLog;
logger->_delFileRecursive= _delFileRecursive;
memset(logger->levelStrs_, 0, sizeof(logger->levelStrs_));
strcpy(logger->levelStrs_[0], "FATAL");
strcpy(logger->levelStrs_[1], "ERROR");
strcpy(logger->levelStrs_[2], "UERR");
strcpy(logger->levelStrs_[3], "WARN");
strcpy(logger->levelStrs_[4], "INFO");
strcpy(logger->levelStrs_[5], "DEBUG");
strcpy(logger->levelStrs_[6], "TRACE");
strcpy(logger->levelStrs_[8], "ALL");
memset(logger->fileFullname_, 0, sizeof(logger->fileFullname_));
memset(logger->filedir_, 0, sizeof(logger->filedir_));
memset(logger->filename_, 0, sizeof(logger->filename_));
logger->rotateInterval_ = 60 * 60 * 24;
logger->nNumofLog_ = 24;
logger->fd_ = -1;
logger->level_ = level;
logger->realRotate_ = logger->lastRotate_ = lastRotate;
}
void closefile(Logger* logger)
{
if (logger->fd_ != -1) {
close(logger->fd_);
}
}
void logv(Logger* logger, int level, const char* file, int line, const char* func, const char* fmt, ...)
{
if (level > logger->level_) {
return;
}
logger->_maybeRotate(logger);
char buffer[4 * 1024];
char* p = buffer;
char* limit = buffer + sizeof(buffer);
struct timeval now_tv;
gettimeofday(&now_tv, NULL);
const time_t seconds = now_tv.tv_sec;
struct tm t;
localtime_r(&seconds, &t);
p += snprintf(p, limit - p,
"%04d/%02d/%02d-%02d:%02d:%02d.%06d %s:",
t.tm_year + 1900,
t.tm_mon + 1,
t.tm_mday,
t.tm_hour,
t.tm_min,
t.tm_sec,
(int)(now_tv.tv_usec),
logger->levelStrs_[level]);
va_list args;
va_start(args, fmt);
p += vsnprintf(p, limit - p, fmt, args);
va_end(args);
p = MIN(p, limit - 2);
//trim the ending \n
while (*--p == '\n') {
}
*++p = '\n';
*++p = '\0';
int fd = logger->fd_ == -1 ? 1 : logger->fd_;
int err = write(fd, buffer, p - buffer);
if (err != p - buffer) {
printf("write log file %s failed. written %d errmsg: %s\n",logger->fileFullname_);
}
if (level <= LERROR) {
syslog(LOG_ERR, "%s", buffer + 27);
}
if (level == LFATAL) {
printf("%s", buffer);
assert(0);
}
}
void setFileName(Logger* logger, const char* filedir, const char* filename)
{
char file[100];
memset(file, 0, 100);
strcat(file, filedir);
strcat(file, filename);
int fd = open(file, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, DEFFILEMODE);
if (fd < 0) {
printf("open log file %s failed. msg: %s ignored\n");
return;
}
strcpy(logger->filedir_, filedir);
strcpy(logger->filename_, filename);
strcpy(logger->fileFullname_, file);
if (logger->fd_ == -1) {
logger->fd_ = fd;
}
else {
int r = dup2(fd, logger->fd_);
printf("dup2 failed");
close(fd);
}
}
void setLogLevel(Logger* logger, int level)
{
if (level<LALL+1)
{
logger->level_ = level;
}
else
{
logger->level_ = LINFO;
}
}
void _maybeRotate(Logger* logger)
{
time_t now = time(NULL);
if (strlen(logger->fileFullname_)==0 || (now - timezone) / logger->rotateInterval_ == (logger->lastRotate_ - timezone) / logger->rotateInterval_) {
return;
}
logger->lastRotate_ = now;
long old = logger->realRotate_;
logger->realRotate_ = now;
//如果realRotate的值是新的,那么返回,否则,获得了旧值,进行rotate
if ((old - timezone) / logger->rotateInterval_ == (logger->lastRotate_ - timezone) / logger->rotateInterval_) {
return;
}
struct tm ntm;
localtime_r(&now, &ntm);
char newname[4096];
snprintf(newname, sizeof(newname), "%s.%d%02d%02d%02d%02d",
logger->fileFullname_, ntm.tm_year + 1900, ntm.tm_mon + 1, ntm.tm_mday,
ntm.tm_hour, ntm.tm_min);
const char* oldname = logger->fileFullname_;
int err = rename(oldname, newname);
if (err != 0) {
printf("rename logfile %s -> %s failed \n", oldname, newname);
return;
}
printf("rename logfile %s -> %s \n", oldname, newname);
int fd = open(logger->fileFullname_, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, DEFFILEMODE);
if (fd < 0) {
printf("open log file %s failed. msg: %s ignored\n");
return;
}
dup2(fd, logger->fd_);
close(fd);
_delFileRecursive(logger);
}
void setRotateInterval(Logger* logger, long rotateInterval)
{
logger->rotateInterval_ = rotateInterval;
}
void setNumOfLog(Logger* logger, int nNum)
{
logger->nNumofLog_ = nNum;
}
int _runShell(char* cmd, char* result, int resultsize,int n)
{
FILE *fstream = NULL; char buff[1024] = { 0 }; int cntc = 0; int irtimes = 0;
if (strlen(cmd) == 0 || NULL==result || n <0){ return 0; }
if (NULL == (fstream = popen(cmd, "r"))){ return -1; }
while (NULL != fgets(buff, sizeof(buff), fstream)){
cntc += strlen(buff);
irtimes++;
if (cntc > resultsize ){ break; }
if (irtimes > n){ break; }
strcat(result, buff);
}
pclose(fstream);
}
void _delFileRecursive(Logger* logger)
{
int cameraCount = 0;
FILE *fstream = NULL;
char buff[1024] = {0};
char cmdls[100] = {0};
char cmdsort[100] = { 0 };
char cmdDel[100] = {0};
char delFilename[100] = { 0 };
char cmdCntFileNum[100] = { 0 };
char result[1024 * 1024] = { 0 };
char cntresult[10] = { 0 };
int cnt = 0; int fileNum = 0; char*token;
sprintf(cmdCntFileNum, "ls %s -l |grep \"^-\"|wc -l", logger->filedir_);
_runShell(cmdCntFileNum, cntresult, sizeof(cntresult),1);
result[strlen(cntresult) - 1] = 0;
fileNum = atoi(cntresult);
if (fileNum > logger->nNumofLog_)
{
sprintf(cmdls, "ls %s > list.txt", logger->filedir_);
system(cmdls);
sprintf(cmdsort, "%s", "sort -u list.txt");
_runShell(cmdsort, result, sizeof(result),2);
token = strtok(result, "\n");
if (strcmp(token, logger->filename_) == 0)
{
token = strtok(NULL, "\n");
}
strcpy(delFilename, token);
sprintf(cmdDel, "rm -f %s/%s", logger->filedir_, delFilename);
system(cmdDel);
trace("del %s/%s\n", logger->filedir_, delFilename);
printf("del %s/%s\n", logger->filedir_, delFilename);
}
}