注:使用 cursor 生成大致框架后,自己调整的,感觉不错,就记录了,如果后续有使用日志,会优先使用这个。
一、源代码
1.1 头文件
#pragma once
#include <string>
#include <iostream>
#include <fstream>
#include <mutex>
#include <memory>
enum class LogLevel {
DEBUG = 0,
INFO = 1,
WARNING = 2,
M_ERROR = 3
};
class Logger {
public:
/*
* 获取 Logger 的单例实例
* @return Logger 的引用
*/
static Logger& GetInstance();
/*
* 设置日志级别,低于该级别的日志将不会被输出
* @param [level] 要设置的日志级别
*/
void SetLogLevel(LogLevel level);
/*
* 设置是否启用控制台输出
* @param [enable] true 表示启用,false 表示禁用
*/
void SetConsoleOutput(bool enable);
/*
* 设置是否启用文件输出
* @param [enable] true 表示启用,false 表示禁用
* @param [logFilePath] 日志文件的路径(可选)
*/
void SetFileOutput(bool enable, const std::wstring& logFilePath = L"");
/*
* 输出 DEBUG 级别的日志
* @param [message] 日志消息
*/
void Debug(const std::wstring& message);
/*
* 输出 INFO 级别的日志
* @param [message] 日志消息
*/
void Info(const std::wstring& message);
/*
* 输出 WARNING 级别的日志
* @param [message] 日志消息
*/
void Warning(const std::wstring& message);
/*
* 输出 ERROR 级别的日志
* @param [message] 日志消息
*/
void Error(const std::wstring& message);
private:
Logger();
~Logger();
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
void WriteLog(LogLevel level, const std::wstring& message);
std::wstring GetTimestamp();
std::wstring GetLevelString(LogLevel level);
LogLevel m_logLevel;
bool m_consoleOutput;
bool m_fileOutput;
std::wstring m_logFilePath;
std::wofstream m_logFile;
std::mutex m_mutex;
};
// 便捷宏定义
#define LOG_DEBUG(msg) Logger::GetInstance().Debug(msg)
#define LOG_INFO(msg) Logger::GetInstance().Info(msg)
#define LOG_WARNING(msg) Logger::GetInstance().Warning(msg)
#define LOG_ERROR(msg) Logger::GetInstance().Error(msg)
1.2 源文件
#include "Logger.h"
#include <windows.h>
#include <sstream>
#include <iomanip>
#include <cstdarg>
/*
* 构造函数
* 私有,以实现单例模式
*/
Logger::Logger()
: m_logLevel(LogLevel::INFO)
, m_consoleOutput(true)
, m_fileOutput(false)
, m_logFilePath(L"")
{
}
/*
* 析构函数
* 关闭日志文件
*/
Logger::~Logger() {
if (m_logFile.is_open()) {
m_logFile.close();
}
}
/*
* 获取 Logger 的单例实例
* @return Logger 的引用
*/
Logger& Logger::GetInstance() {
static Logger instance;
return instance;
}
/*
* 设置日志级别,低于该级别的日志将不会被输出
* @param [level] 要设置的日志级别
*/
void Logger::SetLogLevel(LogLevel level) {
std::lock_guard<std::mutex> lock(m_mutex);
m_logLevel = level;
}
/*
* 设置是否启用控制台输出
* @param [enable] true 表示启用,false 表示禁用
*/
void Logger::SetConsoleOutput(bool enable) {
std::lock_guard<std::mutex> lock(m_mutex);
m_consoleOutput = enable;
}
/*
* 设置是否启用文件输出
* @param [enable] true 表示启用,false 表示禁用
* @param [logFilePath] 日志文件的路径(可选)
*/
void Logger::SetFileOutput(bool enable, const std::wstring& logFilePath) {
std::lock_guard<std::mutex> lock(m_mutex);
m_fileOutput = enable;
if (enable) {
if (!logFilePath.empty()) {
m_logFilePath = logFilePath;
} else {
// 默认日志文件路径
m_logFilePath = L"debug.log";
}
// 关闭之前的文件
if (m_logFile.is_open()) {
m_logFile.close();
}
// 打开新的日志文件
m_logFile.open(m_logFilePath.c_str(), std::ios::app);
if (m_logFile.is_open()) {
try {
m_logFile.imbue(std::locale(""));
} catch (const std::runtime_error& e) {
// 在某些系统上,设置本地化可能会失败,这里捕获异常并打印警告
std::wcout << L"Warning: Failed to set locale for log file. " << e.what() << std::endl;
}
}
} else {
if (m_logFile.is_open()) {
m_logFile.close();
}
}
}
/*
* 输出 DEBUG 级别的日志
* @param [message] 日志消息
*/
void Logger::Debug(const std::wstring& message) {
WriteLog(LogLevel::DEBUG, message);
}
/*
* 输出 INFO 级别的日志
* @param [message] 日志消息
*/
void Logger::Info(const std::wstring& message) {
WriteLog(LogLevel::INFO, message);
}
/*
* 输出 WARNING 级别的日志
* @param [message] 日志消息
*/
void Logger::Warning(const std::wstring& message) {
WriteLog(LogLevel::WARNING, message);
}
/*
* 输出 ERROR 级别的日志
* @param [message] 日志消息
*/
void Logger::Error(const std::wstring& message) {
WriteLog(LogLevel::M_ERROR, message);
}
/*
* 将日志消息写入目标(控制台/文件)
* @param [level] 日志级别
* @param [message] 日志消息
*/
void Logger::WriteLog(LogLevel level, const std::wstring& message) {
// 检查日志级别
if (level < m_logLevel) {
return;
}
std::lock_guard<std::mutex> lock(m_mutex);
// 构建日志消息
std::wstring logMessage = GetTimestamp() + L" [" + GetLevelString(level) + L"] " + message + L"\n";
// 输出到控制台
if (m_consoleOutput) {
std::wcout << logMessage;
std::wcout.flush();
}
// 输出到文件
if (m_fileOutput && m_logFile.is_open()) {
m_logFile << logMessage;
m_logFile.flush();
}
}
/*
* 获取当前时间的格式化字符串
* @return 格式化后的时间字符串
*/
std::wstring Logger::GetTimestamp() {
SYSTEMTIME st;
GetLocalTime(&st);
std::wstringstream ss;
ss << std::setfill(L'0') << std::setw(4) << st.wYear << L"-"
<< std::setfill(L'0') << std::setw(2) << st.wMonth << L"-"
<< std::setfill(L'0') << std::setw(2) << st.wDay << L" "
<< std::setfill(L'0') << std::setw(2) << st.wHour << L":"
<< std::setfill(L'0') << std::setw(2) << st.wMinute << L":"
<< std::setfill(L'0') << std::setw(2) << st.wSecond;
return ss.str();
}
/*
* 获取日志级别的字符串表示
* @param [level] 日志级别
* @return 日志级别的字符串
*/
std::wstring Logger::GetLevelString(LogLevel level) {
switch (level) {
case LogLevel::DEBUG: return L"DEBUG";
case LogLevel::INFO: return L"INFO";
case LogLevel::WARNING: return L"WARNING";
case LogLevel::M_ERROR: return L"ERROR";
default: return L"UNKNOWN";
}
}
二、使用示例
#include <Logger.h>
int main(int argc, char* argv[]) {
Logger& logger = Logger::GetInstance(); // 单例模式,获取实例
logger.SetLogLevel(LogLevel::INFO); // 设置日志等级,低于这个级别的日志不会输出
logger.SetConsoleOutput(true); // 设置是否要输出的控制台
logger.SetFileOutput(true, L"xxx.log"); // 设置输出日志的文件
LOG_DEBUG(msg); // 输出 debug 信息
LOG_INFO(msg); // 输出通知信息
LOG_WARNING(msg); // 输出警告信息
LOG_ERROR(msg); // 输出错误信息
}