前言:
日志系统在程序运行中有着非常大的作用,用于记录程序的运行情况,在程序出错后查看日志,方便地定位出错的大概范围。 在设计日志系统之前,先考虑一下日志需要输出什么信息呢?什么信息才是有用的信息,都知道写日志是一种对文件的io操作,所以尽可能避免输出没用的信息。 有用的信息:关键变量的值、运行的位置(哪个文件、哪个函数、哪一行)、时间、线程号、进程号等等。
一、日志系统的设计
1、日志的级别 在测试、调试、交付等场景需要输出不同的级别日志。
enum LOGLEVEL
{
LOG_LEVEL_NONE,
LOG_LEVEL_ERROR,
LOG_LEVEL_WARNING,
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
};
2、日志的输出地 日志输出的地方可能不同,终端、控制台、UI界面、文件等等都有。
enum LOGTARGET
{
LOG_TERM = 0x00,
LOG_FILE = 0x01,
LOG_UI = 0x10
};
3、日志的作用域 日志做到什么时候都可以输出,可作用于全程序文件,考虑到多线程情况下,必须保证日志的输出需要得到线程安全的保障,所以需要一个全局且唯一的日志器。 使用设计模式中的单例模式-----日志器 单例模式在上一节中讲过,可使用现代单例模式写法。
二、C++版本的日志系统的实现
Logger.h
#ifndef __LOGGER_H__
#define __LOGGER_H__
#include <string>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <list>
#define LogInfo(...) Logger::GetInstance().AddToQueue("INFO", __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
#define LogWarning(...) Logger::GetInstance().AddToQueue("WARNING", __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
#define LogError(...) Logger::GetInstance().AddToQueue("ERROR", __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
class Logger
{
public:
static Logger& GetInstance();
void SetFileName(const char* filename);
bool Start();
void Stop();
void AddToQueue(const char* pszLevel, const char* pszFile, int lineNo, const char* pszFuncSig, char* pszFmt, ...);
private:
Logger() = default;
Logger(const Logger& rhs) = delete;
Logger& operator =(Logger& rhs) = delete;
void threadfunc();
private:
std::string filename_;
FILE* fp_{};
std::shared_ptr<std::thread> spthread_;
std::mutex mutex_;
std::condition_variable cv_;
bool exit_{false};
std::list<std::string> queue_;
};
#endif
Logger.cpp
#include "Logger.h"
#include <time.h>
#include <stdio.h>
#include <memory>
#include <stdarg.h>
Logger& Logger::GetInstance()
{
static Logger logger;
return logger;
}
void Logger::SetFileName(const char* filename)
{
filename_ = filename;
}
bool Logger::Start()
{
if (filename_.empty())
{
time_t now = time(NULL);
struct tm* t = localtime(&now);
char timestr[64] = { 0 };
sprintf(timestr, "%04d%02d%02d%02d%02d%02d.imserver.log", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
filename_ = timestr;
}
fp_ = fopen(filename_.c_str(), "wt+");
if (fp_ == NULL)
return false;
spthread_.reset(new std::thread(std::bind(&Logger::threadfunc, this)));
return true;
}
void Logger::Stop()
{
exit_ = true;
cv_.notify_one();
spthread_->join();
}
void Logger::AddToQueue(const char* pszLevel, const char* pszFile, int lineNo, const char* pszFuncSig, char* pszFmt, ...)
{
char msg[256] = { 0 };
va_list vArgList;
va_start(vArgList, pszFmt);
vsnprintf(msg, 256, pszFmt, vArgList);
va_end(vArgList);
time_t now = time(NULL);
struct tm* tmstr = localtime(&now);
char content[512] = { 0 };
sprintf(content, "[%04d-%02d-%02d %02d:%02d:%02d][%s][0x%04x][%s:%d %s]%s\n",
tmstr->tm_year + 1900,
tmstr->tm_mon + 1,
tmstr->tm_mday,
tmstr->tm_hour,
tmstr->tm_min,
tmstr->tm_sec,
pszLevel,
std::this_thread::get_id(),
pszFile,
lineNo,
pszFuncSig,
msg);
{
std::lock_guard<std::mutex> guard(mutex_);
queue_.emplace_back(content);
}
cv_.notify_one();
}
void Logger::threadfunc()
{
if (fp_ == NULL)
return;
while (!exit_)
{
std::unique_lock<std::mutex> guard(mutex_);
while (queue_.empty())
{
if (exit_)
return;
cv_.wait(guard);
}
const std::string& str = queue_.front();
fwrite((void*)str.c_str(), str.length(), 1, fp_);
fflush(fp_);
queue_.pop_front();
}
}
三、C版本的日志系统的实现
#ifndef _LOG_H_
#define _LOG_H_
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <time.h>
#define LOGC(type, format, ...) logC(__func__, __FILE__, __LINE__, type, format, ##__VA_ARGS__)
#define DEBUG_PRINT 0
#define LOG_PRINT(fmt, ...) do{\
if(DEBUG_PRINT)\
{\
printf(fmt" [line:%d] [%s]\n", ##__VA_ARGS__, __LINE__, __FUNCTION__);\
}\
}while(0);
#define LOG_DEBUG(format, ...) logC(__func__, __FILE__, __LINE__, "DEBUG", format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) logC(__func__, __FILE__, __LINE__, "ERROR", format, ##__VA_ARGS__)
#define LOG_WARNING(format, ...) logC(__func__, __FILE__, __LINE__, "WARNING", format, ##__VA_ARGS__)
#define LOG_ACTION(format, ...) logC(__func__, __FILE__, __LINE__, "ACTION", format, ##__VA_ARGS__)
#define LOG_SYSTEM(format, ...) logC(__func__, __FILE__, __LINE__, "SYSTEM", format, ##__VA_ARGS__)
static unsigned char mPrintLogPlaceFlag = 0;
static unsigned char mPrintDebugLogFlag = 1;
static FILE* mFile = NULL;
static char* mFileName = "./main.log";
static unsigned int mFileSize;
static const char* getSystemTime()
{
char *time_str = (char*)malloc(sizeof(char)*128);
time_t loacl_time;
time(&loacl_time);
strftime(time_str, sizeof(time_str)*128, "[%Y.%m.%d %X]", localtime(&loacl_time));
return time_str;
}
static void setLogPrintLogPlace(unsigned char flag)
{
if(flag != 0 && flag != 1) return ;
mPrintLogPlaceFlag = flag;
}
static void setPrintDebugLog(unsigned char flag)
{
if(flag != 0 && flag != 1) return ;
mPrintDebugLogFlag = flag;
}
void setPrintFileName(char* fileName)
{
if(fileName == NULL) return ;
mFileName = fileName;
}
static long getFileSize(const char *path)
{
long filesize = -1;
struct stat statbuff;
if(stat(path, &statbuff) < 0){
return filesize;
}
else{
filesize = statbuff.st_size;
}
return filesize;
}
static void openLogFile()
{
if(NULL != mFile)
{
LOG_PRINT("[ACTION] file opened!");
return ;
}
if(NULL == mFileName)
{
LOG_PRINT("[ERROR] file name is NULL.");
return ;
}
if (access(mFileName, F_OK) < 0)
{
LOG_PRINT("[WARNING] file not exist!");
}
mFile = fopen(mFileName, "a");
if(NULL == mFile)
{
LOG_PRINT("[ERROR] open file failed!");
return ;
}
LOG_PRINT("[DEBUG] open file name = %s", mFileName);
}
void LogInit()
{
openLogFile();
}
void logC(const char *func, const char *file, const int line,
const char *type, const char *format, ...)
{
const char* time_str = getSystemTime();
va_list ap;
va_start(ap, format);
char fmt_str[2048];
vsnprintf(fmt_str, sizeof(fmt_str), format, ap);
va_end(ap);
if(mPrintLogPlaceFlag == 0 && mPrintDebugLogFlag == 1)
{
fprintf(stderr, "[%s]%s[%s@%s:%d] %s\n", type, time_str, func,
file, line, fmt_str);
}
else if(mPrintLogPlaceFlag == 1)
{
if(mFile == NULL)
{
openLogFile();
}
fprintf(mFile, "[%s]%s[%s@%s:%d] %s\n", type, time_str, func,
file, line, fmt_str);
}
}
void LogDestroy(void)
{
if(mFile != NULL)
{
fclose(mFile);
mFile = NULL;
}
return;
}
#endif
|