log.h
#ifndef __LOG_H__
#define __LOG_H__
#include <stdio.h>
#include <pthread.h>
namespace log
{
#define LOG_MAX_LENGTH 4096
#define FLUSH_LINE 2
#define EMERG 8 //
#define ALERT 7 //
#define CRIT 6 //
#define WARNING 5 //
#define ERR 4 //错误
#define NOTICE 3 //注意信息
#define INFO 2 //普通信息
#define DEBUG 1 //调试信息
#define NONE 0 //
static const char g_alertStr[128] = "[Alert]:" ;//比warning级别要高,表示系统可能处于异常运行状态,如连接数据库失败,无法正常向数据库进行操作等
static const char g_errorStr[128] = "[Error]:" ;//可以确定是程序上bug,需开发人员检查程序逻辑
static const char g_warningStr[128] = "[Warning]:" ;//虽不是服务器端错误,但需要高度关注
static const char g_infoStr[128] = "[Info]:" ;//一些有用信息,它比普通信息要略高,但可能没有Warning那么严重
static const char g_messageStr[128] = "[Message]:" ;//普通信息
static const char g_sqlStr[128] = "[Sql]:";//数据库操作语句
#ifndef TIME_CHN_FMT
#define TIME_CHN_FMT "%Y-%m-%d %H:%M:%S"
#endif
/*
time_t转时间字符串
*/
char* timeToString(time_t tmTime, const char *fmt,char *strTime, int len);
class CLog
{
public:
//the singleton
static CLog* getInstance();
//初始化
int init(const char* path,const char* fileName, short level=DEBUG,unsigned int logFileMaxNum=50,unsigned int logFileMaxSize=10*1024*1024);
//关闭
int close();
//写日志
void plog(short level, const char *fmt, ...);
private:
CLog();
virtual ~CLog();
//检测文件大小是否超过上限
bool fileSizeOverflow(const char *fileName);
//创建新文件
void createNewFile();
private:
FILE *mFile;
//日志文件路径
char mFilePath[1024];
//日志缓冲区
char mLogBuf[LOG_MAX_LENGTH];
//lock
pthread_mutex_t mWriteLogLock;
//上一条日志重复次数
int mRepeatCount;
//当前日志文件大小
unsigned int mCurrLogFileSize;
//日志文件大小上限
unsigned int mLogFileMaxSize;
//当前的日志级别
int mLogLevel;
//是否已关毕
bool mClosed;
//日志的最大文件数
unsigned int mLogFileMaxNum;
//日志的当前文件数
unsigned int mLogFileCurrNum;
};
}//end of namespace log
#endif
log.cpp
#include "log.h"
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <string.h>
using std::cout;
using std::endl;
namespace log
{
/*
时间转字符串
*/
char* timeToString(time_t tmTime, const char *fmt,char *strTime, int len)
{
struct tm gmTime;
localtime_r(&tmTime, &gmTime);
strftime(strTime, (size_t) len, fmt, &gmTime);
return strTime;
}
/*
the singleton
*/
CLog* CLog::getInstance()
{
static CLog log;
return &log;
}
CLog::CLog()
{
mFile = NULL;
mRepeatCount = 0;
mCurrLogFileSize = 0;
mLogFileMaxSize = 0;
mLogFileMaxNum = 0;
memset(mLogBuf, 0, sizeof(mLogBuf));
mClosed = false;
mLogFileCurrNum = 0;
}
CLog::~CLog()
{
close();
}
/*
关闭
*/
int CLog::close()
{
if(mClosed){
return -1;
}
if (mFile)
{
fflush(mFile);
fclose(mFile);
}
pthread_mutex_destroy(&mWriteLogLock);
mClosed = true;
return 0;
}
/*
初始化
*/
int CLog::init(const char *path, const char *fileName, short level, unsigned int logFileMaxNum, unsigned int logFileMaxSize)
{
if (path == NULL || fileName == NULL || logFileMaxNum<1 || level < NONE || logFileMaxSize<1){
return -1;
}
if(pthread_mutex_init(&mWriteLogLock, NULL)!=0)
{
pthread_mutex_destroy(&mWriteLogLock);
return -1;
}
mLogFileMaxNum = logFileMaxNum;
mLogFileMaxSize = logFileMaxSize;
if(access(path,F_OK)<0)
{
#ifdef _WIN32
mkdir(path);
#else
mkdir(path,0777);
#endif
}
char fileFullName[256];
memset(fileFullName,0,sizeof(fileFullName));
strcpy(fileFullName,path);
strcat(fileFullName,fileName);
memset(mFilePath, 0, sizeof(mFilePath));
strncpy(mFilePath, fileFullName, sizeof(mFilePath)-1);
//备份老日志,创建新日志。
if(fileSizeOverflow(fileFullName)){
createNewFile();
}
mFile = fopen(fileFullName, "a+");
if (mFile == NULL){
return -1;
}
mLogLevel = level;
fprintf(mFile, "\n\n\n\n\n");
fprintf(mFile, "--------------------------------------------------------\n");
fprintf(mFile, " 日志建立日期: %s %s\n", __DATE__, __TIME__);
fprintf(mFile, "--------------------------------------------------------\n");
fprintf(mFile, "\n");
fflush(mFile);
return 0;
}
/*
写日志
*/
void CLog::plog(short level, const char *fmt, ...)
{
//与配置文件中的日志级别对比,选择输出日志
if (0 == mLogLevel || level < mLogLevel){
return;
}
if (NULL == mFile)
{
mFile = fopen(mFilePath, "a+");
if (NULL == mFile){
return;
}
mCurrLogFileSize = 0;
}
pthread_mutex_lock(&mWriteLogLock);
static int lastBufLen = 0;
static char lastBuf[LOG_MAX_LENGTH];
static int flushCount = 0;
time_t t = time(NULL);
char timeStr[64];
memset(timeStr,0,sizeof(timeStr));
timeToString(t, TIME_CHN_FMT, timeStr, sizeof(timeStr)-1);
char printf_buf[LOG_MAX_LENGTH];
memset(printf_buf,0,sizeof(printf_buf));
va_list args;
va_start(args, fmt);
vsprintf(printf_buf, fmt, args);
va_end(args);
int len = strlen(printf_buf) + 1;
if (len == lastBufLen)
{
if (strcmp(lastBuf, printf_buf) == 0)
{
++mRepeatCount;
pthread_mutex_unlock(&mWriteLogLock);
return;
}
}
else
{
if (mRepeatCount > 0){
mCurrLogFileSize += fprintf(mFile, "[%s]: 上一条日志重复%u次。\n", timeStr, mRepeatCount);
}
lastBufLen = len;
mRepeatCount = 0;
memcpy(lastBuf, printf_buf, len);
}
mCurrLogFileSize += fprintf(mFile, "[%s] ", timeStr);
mCurrLogFileSize += fprintf(mFile, "%s", printf_buf);
if (INFO < level)
{
fflush(mFile);
}else if (++flushCount >= FLUSH_LINE)
{
flushCount = 0;
fflush(mFile);
}
if (mCurrLogFileSize > mLogFileMaxSize)
{
createNewFile();
mFile = fopen(mFilePath, "a+");
if (mFile)
{
mCurrLogFileSize = 0;
timeToString(t, TIME_CHN_FMT, timeStr, sizeof(timeStr)-1);
mCurrLogFileSize += fprintf(mFile, "[%s]: ", timeStr);
mCurrLogFileSize += fprintf(mFile, "%s", printf_buf);
}
}
//如果是调试模式,则会将日志输出在终端上
if(mLogLevel==DEBUG){
cout << printf_buf << endl;
}
pthread_mutex_unlock(&mWriteLogLock);
}
/*
重命名旧文件,创建新文件
*/
void CLog::createNewFile()
{
char newFullName[1024];
memset(newFullName,0,sizeof(newFullName));
//如果是第一次则先遍历一次
if(mLogFileCurrNum==0)
{
for(unsigned int i=1;i<=mLogFileMaxNum;i++)
{
snprintf(newFullName,sizeof(newFullName), "%s.%02d", mFilePath, i);
//不存在,则创建
if((access(newFullName,F_OK))<0)
{
mLogFileCurrNum = i;
break;
}
//如果文件存在,则继续找,如果找到最后也存在则删除第一个
if(i==mLogFileMaxNum)
{
snprintf(newFullName,sizeof(newFullName), "%s.%02d", mFilePath, 1);
unlink(newFullName);
mLogFileCurrNum = 1;
}
}
}else
{
mLogFileCurrNum++;
if(mLogFileCurrNum>mLogFileMaxNum){
mLogFileCurrNum = 1;
}
snprintf(newFullName,sizeof(newFullName), "%s.%02d", mFilePath, mLogFileCurrNum);
//已存在,则删除
if((access(newFullName,F_OK))>=0){
unlink(newFullName);
}
fclose(mFile);
mFile = NULL;
}
if (0 != rename(mFilePath, newFullName)){
return;
}
int ret = creat(mFilePath, 0664);
::close(ret);
}
/*
检测文件大小是否超过上限
*/
bool CLog::fileSizeOverflow(const char *fileName)
{
if (NULL == fileName){
return false;
}
char path[1024];
memset(path,0,sizeof(path));
strncpy(path, fileName, sizeof(path)-1);
path[sizeof(path)-1] = '\0';
FILE *fp = fopen(path, "r");
if (fp == NULL){
return false;
}
int ret = fseek(fp, 0, SEEK_END);
if (0 != ret)
{
fclose(fp);
return false;
}
mCurrLogFileSize = ftell(fp);
if (mCurrLogFileSize < mLogFileMaxSize)
{
fclose(fp);
return false;
}
mCurrLogFileSize = 0;
fclose(fp);
return true;
}
}//end of namespace log
main
#include "log.h"
#include <unistd.h>
using namespace log;
int main()
{
CLog::getInstance()->init("/usr/local/code/MyLog_debug/","test.log"/*,1,10,1024*1024*/);
int i = 0;
while(true)
{
i++;
CLog::getInstance()->plog(NOTICE,"%s h:%d\n",g_messageStr,i);
usleep(1000);
}
}