在实际生产中,日志是非常重要的调试工具,日志内容至少需要包括时间戳、日志级别、日志内容
推荐的日志库有:
google/glog: C++ implementation of the Google logging module (github.com)
Apache Log4cxx: Apache Log4cxx
自己实现的话, 日志内容应该包括,精确到微秒的时间戳,日志级别(DEBUG / INFO / WARN / ERROR / FATAL),日志写入时的代码文件名,代码行号和函数名,例如
2024-05-25 23:46:07.998429 [FATAL] This is a fatal message (File=C:/Users/Yezi/Desktop/Logger/main.cpp Function=main Line=9)
并且我希望日志是这样使用的
#include "logger.h"int main() {Logger::InitLogger("logfile.txt");LOG(LogLevel::DEBUG, "This is a debug message");LOG(LogLevel::INFO, "This is an info message");LOG(LogLevel::WARN, "This is a warning message");LOG(LogLevel::ERROR, "This is an error message");LOG(LogLevel::FATAL, "This is a fatal message");return 0;
}
而不是这样使用的
int main() {Logger logger("logfile.txt");// 示例使用LOG(logger, LogLevel::DEBUG, "This is a debug message");LOG(logger, LogLevel::INFO, "This is an info message");LOG(logger, LogLevel::WARN, "This is a warning message");LOG(logger, LogLevel::ERROR, "This is an error message");LOG(logger, LogLevel::FATAL, "This is a fatal message");return 0;
}
这意味着我们需要一个单例模式的实现,需要将类实例静态化,由一个静态函数返回类实例的引用,由于静态变量只会初始化一次,所以每次返回的都是同一个实例
同时我们希望能够保留可以更改类实例初始化的参数,例如日志文件名,因此需要一个初始化的静态函数来进行类实例的初始化
//
// Created by YEZI on 2024/5/25.
//#ifndef LOGGER_H
#define LOGGER_H#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>// 定义日志级别
enum class LogLevel { DEBUG, INFO, WARN, ERROR, FATAL };class Logger {
private:int fd_ = -1;// 获取当前时间戳static std::string getCurrentTime() {// 获取当前时间点auto now = std::chrono::system_clock::now();// 将时间点转换为time_tauto now_time_t = std::chrono::system_clock::to_time_t(now);// 获取tm结构体std::tm time_info = *std::localtime(&now_time_t);// 构造时间字符串std::ostringstream oss;oss << std::put_time(&time_info, "%Y-%m-%d %H:%M:%S"); // 格式化时间// 获取微秒部分auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()) % 1000000;oss << "." << std::setfill('0') << std::setw(6) << microseconds.count(); // 微秒部分return oss.str();}// 私有化构造函数explicit Logger(const char *filename) {// 打开日志文件fd_ = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd_ == -1) {std::cerr << "Failed to open log file\n";}}public:~Logger() {// 关闭日志文件if (fd_ != -1) {close(fd_);}}//初始化日志文件static void InitLogger(const char *filename) {getInstance(filename);}// 禁用拷贝构造函数和赋值运算符Logger(const Logger &) = delete;Logger &operator=(const Logger &) = delete;// 获取 Logger 实例的静态方法static Logger &getInstance(const char *filename = nullptr) {// 静态变量只会初始化一次static Logger instance(filename);return instance;}// 写日志函数void log(LogLevel level, const char *message, const char *file, int line, const char *function) const {std::string logLevelStr;switch (level) {case LogLevel::DEBUG:logLevelStr = "DEBUG";break;case LogLevel::INFO:logLevelStr = "INFO";break;case LogLevel::WARN:logLevelStr = "WARN";break;case LogLevel::ERROR:logLevelStr = "ERROR";break;case LogLevel::FATAL:logLevelStr = "FATAL";break;}// 构建日志消息std::string logMessage = getCurrentTime() + " [" + logLevelStr + "] " + message +" (File=" + file + " Function=" + function + " Line=" + std::to_string(line) + ")\n";// 写入日志到文件write(fd_, logMessage.c_str(), logMessage.size());}
};// 宏定义简化日志调用
#define LOG(level, message) Logger::getInstance().log(level, message, __FILE__, __LINE__, __FUNCTION__)#endif //LOGGER_H
代码维护在GitHub
MaolinYe/Logger: C++实现的日志类,记录日志写入时的时间,可选的日志级别(DEBUG / INFO / WARN / ERROR / FATAL),日志内容,日志写入时的代码文件,代码行号和函数名 (github.com)