日志是记录软件运行过程中发生的事件的一种手段,通常包含以下内容:
- 时间戳:记录日志条目创建的确切时间。这对于追踪事件发生的时间顺序至关重要。
- 日志级别:表示日志信息的严重性或重要性,常见的级别包括 DEBUG、INFO、WARNING、ERROR 和 FATAL。
- 消息:日志条目的主要文本内容,描述了发生了什么事件或情况。
- 来源:生成日志条目的组件、模块、类或函数名,有助于定位日志信息在代码中的位置。
- 线程信息:在多线程应用程序中,记录产生日志的线程ID或名称。
- 进程信息:记录产生日志的进程ID或名称。
- 用户信息:如果是用户驱动的应用程序,可能需要记录进行操作的用户ID或名称。
- 主机信息:记录生成日志的服务器或主机的名称或IP地址。
- 异常信息:如果日志是由于异常情况而产生的,通常包括异常的类型、消息、堆栈跟踪等。
- 自定义上下文:根据应用程序的需要,可能包括与事件相关的特定上下文信息,如交易ID、会话ID、请求参数等。
在C++标准库中,并没有直接提供专门的日志记录功能,然而,C++标准库中的一些组件可以用来模拟实现日志记录。
需要实现:
- 日志级别:Debug, Info, Warning, Error, Fatal:这些枚举值定义了不同的日志级别。
- 日志输出风格:Screen, OneFile, ClassFile:定义了日志消息的输出方式,分别对应控制台输出、单个文件输出和按类别分文件输出。
- 获取当前时间戳:TimeStampToLocalTime获取当前时间戳并转换为本地时间。
- 输出日志消息:屏幕、单个文件、多类文件。
Log.hpp如下:
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <cstdarg>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctime>
#include <fcntl.h>
#include <unistd.h>
// 日志级别
enum
{
Debug = 0,
Info,
Warning,
Error,
Fatal
};
// 输出风格
enum
{
Screen = 10,
OneFile,
ClassFile
};
// 日志级别的字符串输出
std::string LevelToString(int level)
{
switch (level)
{
case Debug:
return "Debug";
case Info:
return "Info";
case Warning:
return "Warning";
case Error:
return "Error";
case Fatal:
return "Fatal";
default:
return "Unknown";
}
}
// 获取当前时间戳并转换为本地时间
std::string TimeStampToLocalTime()
{
time_t curtime = time(nullptr);
struct tm *cur = localtime(&curtime);
char time_buffer[128];
snprintf(time_buffer, sizeof(time_buffer), "%d/%d/%d %d:%d:%d",
cur->tm_year + 1900, cur->tm_mon + 1, cur->tm_mday,
cur->tm_hour, cur->tm_min, cur->tm_sec);
return time_buffer;
}
const int default_Style = Screen;
const std::string default_filename = "log.";
const std::string default_logdir = "log"; // 文件的默认存储路径
class Log
{
public:
Log()
: _style(default_Style),
_filename(default_filename)
{
mkdir(default_logdir.c_str(), 0775);
}
// 更改输出风格
void ChangeStyle(int style)
{
_style = style;
}
// 输出消息到单个文件
void WriteLogToOneFile(const std::string &logname, const std::string &message)
{
umask(0);
int fd = open(logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
if (fd < 0)
return;
write(fd, message.c_str(), message.size());
close(fd);
}
// 输出消息到多类文件
void WriteLogToClassFile(const std::string &levelstr, const std::string &message)
{
std::string logname = default_logdir;
logname += "/";
logname += _filename;
logname += levelstr;
WriteLogToOneFile(logname, message);
}
void WriteLog(const std::string &levelstr, const std::string &message)
{
switch (_style)
{
case Screen:
std::cout << message << std::endl;
break;
case OneFile:
WriteLogToOneFile("all", message);
break;
case ClassFile:
WriteLogToClassFile(levelstr, message);
break;
default:
break;
}
}
void LogMessage(int level, const char *format, ...)
{
char leftbuffer[1024];
std::string levelstr = LevelToString(level);
std::string curtime = TimeStampToLocalTime();
std::string pid = std::to_string(getpid());
snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",
levelstr.c_str(),curtime.c_str(),pid.c_str());
char rightbuffer[1024];
va_list args; //用于访问可变参数列表
va_start(args, format); //使用 va_start 宏初始化 args,使其指向 format 后面的第一个可变参数
// args 指向了可变参数部分
vsnprintf(rightbuffer,sizeof(rightbuffer),format,args);//使用 vsnprintf 函数将可变参数列表按照 format 格式化后的字符串写入 rightbuffer,同时限制写入的字符数不超过 rightbuffer 的大小
va_end(args); //args = nullptr;
std::string loginfo = leftbuffer;
loginfo += rightbuffer;
WriteLog(levelstr, loginfo);
}
~Log()
{}
private:
int _style; // 输出风格
std::string _filename; // 文件名称
};
Main.cc如下:
#include "Log.hpp"
int main()
{
Log lg;
// lg.ChangeStyle(Screen);
lg.ChangeStyle(ClassFile);
lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);
lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);
return 0;
}
虽然C++标准库可以用来实现基本的日志记录功能,但如果需要一个更完整、功能更强大的日志系统,通常会选择使用专门的日志库,如spdlog、Log4cpp、Boost.Log等。这些库提供了更高级的日志管理功能,包括日志级别控制、异步日志记录、日志回滚等