【项目小技巧】【C++】编写日志函数


注:使用 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); // 输出错误信息
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值