大家在进行C++代码开发的时候,往往都需要进行日志记录。今天整理了一个简单的日志模块实现,大家如果用得着可以参考,它支持以下特性:
- 支持日志级别。
- 支持日志级别不同,采用不同的颜色显示。
- 支持日志转储至文件。
- 使用方便,用户直接使用LOG_XXX接口即可。
源码包含两个文件:logger.hpp(头文件)和logger.cpp(实现文件),大家可以直接把这两个文件放到你的工程里即可。
logger.hpp文件:
#pragma once
#include <cstdio>
// 日志功能接口宏
// Debug日志宏
#define LOG_DEBUG(...) \
logger::Log(__FILE__, __LINE__, logger::Severity::DEBUG, __VA_ARGS__)
// Info日志宏
#define LOG_INFO(...) \
logger::Log(__FILE__, __LINE__, logger::Severity::INFO, __VA_ARGS__)
// Warning日志宏
#define LOG_WARN(...) \
logger::Log(__FILE__, __LINE__, logger::Severity::WARN, __VA_ARGS__)
// Error日志宏
#define LOG_ERROR(...) \
logger::Log(__FILE__, __LINE__, logger::Severity::ERROR, __VA_ARGS__)
// Panic日志宏
#define LOG_PANIC(...) \
logger::Log(__FILE__, __LINE__, logger::Severity::PANIC, __VA_ARGS__)
namespace logger {
enum class Severity {
NONE = -2, // 所有级别都不打印
ALL = -1, // 所有级别都打印
PANIC = 0,
ERROR,
WARN,
INFO,
DEBUG,
COUNT // 代表有多少个级别
};
extern void Log(const char* file, int line, Severity severity, const char* txt, ...);
// 重定向日志到文件
void Redirect(std::FILE* file, Severity severity = Severity::ALL);
// 设置日志级别
void SetSeverity(Severity severity);
}
logger.cpp文件:
#include <sys/time.h>
#include <ctime>
#include <cstdio>
#include <cstdarg>
#include "logger.hpp"
namespace logger
{
// 默认各种级别的日志输出到哪个地方
std::FILE* s_sinks[] = {
stderr, // PANIC
stderr, // ERROR
stdout, // WARNING
stdout, // INFO
stdout // DEBUG
};
// 每种级别下的日志输出样式,包含了颜色
const char* s_patterns[] = {
"\033[1;3;31m%s.%03ld PANIC %s@%d: %s\033[0m\n",
"\033[1;31m%s.%03ld ERROR %s@%d: %s\033[0m\n",
"\033[33m%s.%03ld WARN %s@%d: %s\033[0m\n",
"\033[0m%s.%03ld INFO %s@%d: %s\033[0m\n",
"\033[90m%s.%03ld DEBUG %s@%d: %s\033[0m\n"
};
// 默认的日志级别
Severity s_severity = Severity::DEBUG;
static void LoggingFunction(const char* file, int line, Severity severity, const char* log)
{
if (severity < Severity::PANIC || severity > Severity::DEBUG) {
std::fprintf(stderr, "Invalid severity");
return;
}
// 对于日志级别大于默认级别的忽略不打印
if (s_severity == Severity::NONE || severity > s_severity) {
return;
}
int severityIndex = static_cast<int>(severity);
std::FILE* outStream = s_sinks[severityIndex];
struct tm* tmInfo;
struct timeval tv;
gettimeofday(&tv, NULL);
struct tm localTm;
tmInfo = localtime_r(&tv.tv_sec, &localTm);
char timeStr[20];
strftime(timeStr, 20, "%Y-%m-%d %H:%M:%S", tmInfo);
std::fprintf(outStream, s_patterns[severityIndex], timeStr, tv.tv_usec / 1000, file, line, log);
std::fflush(outStream);
}
// 不定长参数函数
void Log(const char* file, int line, Severity severity, const char* txt, ...)
{
va_list args1;
va_start(args1, txt); // args1指向txt参数后的那一个参数
va_list args2;
va_copy(args2, args1);
int bufSize = std::vsnprintf(NULL, 0, txt, args1); // 获取打印buffer需要的长度
char* buffer = new char[bufSize + 1]; // vsnprintf只返回字符个数,不包含null
std::vsnprintf(buffer, bufSize, txt, args2); // 此时不能用args1了
LoggingFunction(file, line, severity, buffer); // 真正的打印函数
va_end(args1);
va_end(args2);
delete [] buffer;
}
void Redirect(std::FILE* file, Severity severity)
{
if (severity == Severity::ALL) {
for (int i = 0; i < static_cast<int>(Severity::COUNT); i++) {
s_sinks[i] = file;
}
return;
}
int severityIndex = static_cast<int>(severity);
s_sinks[severityIndex] = file;
}
void SetSeverity(Severity severity)
{
if (severity < Severity::PANIC || severity > Severity::DEBUG) {
std::fprintf(stderr, "SetSeverity: invalid severity\n");
return;
}
s_severity = severity;
}
}
测试文件:
#include <iostream>
#include "logger.hpp"
const char* severityName[] = {
"PANIC",
"ERROR",
"WARN",
"INFO",
"DEBUG"
};
void TestLog(logger::Severity severity)
{
std::cout << "Set default severity to " << severityName[static_cast<int>(severity)] << std::endl;
logger::SetSeverity(severity);
LOG_DEBUG("This is a debug\n");
LOG_INFO("This is a info\n");
LOG_WARN("This is a warn\n");
LOG_ERROR("This is an error\n");
LOG_PANIC("This is a panic\n");
}
int main()
{
TestLog(logger::Severity::DEBUG);
TestLog(logger::Severity::INFO);
TestLog(logger::Severity::WARN);
TestLog(logger::Severity::ERROR);
TestLog(logger::Severity::PANIC);
std::cout << "Redirect log to file now" << std::endl;
std::FILE* logFile = std::fopen("./log", "a+");
logger::Redirect(logFile);
TestLog(logger::Severity::DEBUG);
return 0;
}