C++日志Logger类

// Logger.hpp
#ifndef LOGGER_HPP
#define LOGGER_HPP

/**
 * @file Logger.hpp
 * @brief A flexible and thread-safe logging utility for C++ applications.
 *
 * This file defines a singleton Logger class that supports multiple log levels,
 * log file rotation by date, and customizable error callbacks. Logs can be
 * written to both the console and a file, with timestamps precise to milliseconds.
 *
 * Features:
 * - Multiple log levels: Trace, Debug, Info, Warning, Error, Fatal.
 * - Log file rotation by date (e.g., 2025-02-15.log).
 * - Thread-safe logging.
 * - Customizable error callbacks for Error and Fatal logs.
 * - Support for formatted log messages (like printf).
 *
 * Usage:
 * - Include this header file in your project.
 * - Use the provided macros (TRACE, DEBUG, INFO, WARN, ERROR, FATAL) to log messages.
 * - Set the log directory using Logger::getInstance().setLogDirectory().
 * - Configure log levels using Logger::getInstance().setLogLevelsFromString().
 *
 * Example:
 * @code
 * Logger::getInstance().setLogDirectory("logs");
 * Logger::getInstance().setLogLevelsFromString("Debug,Error");
 * DEBUG("This is a debug message: %s", "Debugging information");
 * ERROR("Failed to connect to %s", "opc.tcp://localhost:4840");
 * @endcode
 *
 * @author IT Cat
 * @date 2025-02-15
 * @version 1.0
 * @copyright ITCat
 */

#include <fstream>
#include <iostream>
#include <string>
#include <cstdarg>
#include <memory>
#include <vector>
#include <mutex>
#include <set>
#include <functional>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <unordered_map>
#include <algorithm>

#ifdef _WIN32
#include <WinSock2.h>
#include <Windows.h>
#else
#include <pthread.h>
#endif // WIN32

#include "String.hpp"

enum LogLevel
{
	Trace,   // 最详细的日志,用于跟踪程序执行流程
	Debug,   // 调试信息,用于开发阶段
	Info,    // 常规信息,用于记录程序运行状态
	Warning, // 警告信息,表示潜在问题
	Error,   // 错误信息,表示程序错误
	Fatal    // 严重错误,可能导致程序崩溃
};

class Logger
{
public:
	using LoggerErrorCallback = std::function<void(const std::string&)>;

	static Logger& getInstance()
	{
		static Logger instance;
		return instance;
	}

	// 设置日志记录的目录
	void setLogPath(const std::string& path = "./");

	// 通过字符串列表设置日志级别(如 {"F", "E"})
	void setLogLevels(const std::vector<std::string>& levels);

	// 通过字符串设置日志级别(如 "F=1,E=1,W=1,I=1,D=1,T=1")
	void setLogLevel(const std::string& levels = "F=1,E=1,W=1,I=1,D=1,T=1");

	// 添加日志级别
	void addLogLevel(LogLevel level);

	// 移除日志级别
	void removeLogLevel(LogLevel level);

	// 启用所有日志级别
	void enableAllLogLevels();

	// 禁用所有日志级别
	void disableAllLogLevels();

	// 设置错误日志回调函数
	void setErrorCallback(LoggerErrorCallback callback);

	void log(LogLevel level, int line, const char* function, const char* format, ...);
	void log(LogLevel level, int line, const char* function, const std::string& filename, const char* format, ...);
	void log(LogLevel level, int line, const char* function, std::ofstream& file, const char* format, ...);

	static unsigned long ProcessId();
	static std::string NumberToHex(long num);

private:
	std::ofstream m_logFile;
	std::string m_logPath;                  // 日志文件目录
	std::string m_currentDate;              // 当前日志文件的日期
	mutable std::mutex m_logMutex;
	std::set<LogLevel> m_enabledLogLevels;  // 存储启用的日志级别
	LoggerErrorCallback m_errorCallback;          // 错误日志回调函数
	std::unordered_map<std::string, LogLevel> m_logLevelMap; // 字符串到日志级别的映射
	std::unordered_map<int, std::string> m_logLevelMapRef;   // 字符串到日志级别的反映射

	Logger();
	~Logger();

	Logger(const Logger&) = delete;
	Logger& operator=(const Logger&) = delete;

	void openLogFile();
	void closeLogFile();
	void logToFile(const std::string& message);
	void logToFile(const std::string& message, std::ofstream& file);
	void logToConsole(const std::string& message) const;
	std::string formatString(const std::string& format, va_list args) const;
	std::string getCurrentDate() const;
	std::string getCurrentTime() const;
	void initializeLogLevelMap();
};

inline Logger::Logger()
{
	initializeLogLevelMap();

	// 默认启用所有日志级别
	enableAllLogLevels();
}

inline Logger::~Logger()
{
	closeLogFile();
}

inline void Logger::setLogPath(const std::string& path)
{
	std::lock_guard<std::mutex> lock(m_logMutex);
	m_logPath = path;
	if (!m_logPath.empty() && m_logPath.back() != '/')
	{
		m_logPath += '/';
	}
	openLogFile(); // 打开新的日志文件
}

inline void Logger::setLogLevels(const std::vector<std::string>& levels)
{
	std::lock_guard<std::mutex> lock(m_logMutex);
	m_enabledLogLevels.clear();

	for (const auto& levelStr : levels)
	{
		std::string upperLevelStr = levelStr;
		std::transform(upperLevelStr.begin(), upperLevelStr.end(), upperLevelStr.begin(), ::towupper);

		auto it = m_logLevelMap.find(upperLevelStr);
		if (it != m_logLevelMap.end())
		{
			m_enabledLogLevels.insert(it->second);
		}
	}
}

inline void Logger::setLogLevel(const std::string& levels)
{
	std::lock_guard<std::mutex> lockm_(m_logMutex);
	m_enabledLogLevels.clear();

	std::stringstream ss(levels);
	String levelStr;
	while (std::getline(ss, levelStr, ','))
	{
		std::transform(levelStr.begin(), levelStr.end(), levelStr.begin(), ::toupper);
		std::vector<std::string> level = levelStr.split("=");
		if (level.size() == 2)
		{
			if (level.at(1) == "1")
			{
				m_enabledLogLevels.insert(m_logLevelMap[level.at(0)]);
			}
		}
	}
}

inline void Logger::addLogLevel(LogLevel level)
{
	std::lock_guard<std::mutex> lock(m_logMutex);
	m_enabledLogLevels.insert(level);
}

inline void Logger::removeLogLevel(LogLevel level)
{
	std::lock_guard<std::mutex> lock(m_logMutex);
	m_enabledLogLevels.erase(level);
}

inline void Logger::enableAllLogLevels()
{
	std::lock_guard<std::mutex> lock(m_logMutex);
	for (const auto& pair : m_logLevelMap)
	{
		m_enabledLogLevels.insert(pair.second);
	}
}

inline void Logger::disableAllLogLevels()
{
	std::lock_guard<std::mutex> lock(m_logMutex);
	m_enabledLogLevels.clear();
}

inline void Logger::setErrorCallback(LoggerErrorCallback callback)
{
	std::lock_guard<std::mutex> lock(m_logMutex);
	m_errorCallback = callback;
}

inline void Logger::log(LogLevel level, int line, const char* function, const char* format, ...)
{
	std::lock_guard<std::mutex> lock(m_logMutex);

	// 格式化日志消息
	va_list va;
	va_start(va, format);
	std::string message = formatString(format, va);
	va_end(va);
	unsigned long pid = ProcessId();
	// 添加时间戳和调用位置信息
	std::string fullMessage = "[" + m_logLevelMapRef[(int)level] + "][0x" + NumberToHex(pid) + "]" + getCurrentTime() + " " + std::string(function) + ":" + std::to_string(line) + " " + message;

	// 如果是 Error 或 Fatal 日志,调用回调函数
	if ((level == LogLevel::Error || level == LogLevel::Fatal) && m_errorCallback)
	{
		m_errorCallback(fullMessage);
	}

	// 检查日志级别是否启用
	if (m_enabledLogLevels.find(level) == m_enabledLogLevels.end())
	{
		return;
	}
	std::string today = getCurrentDate();
	if (today != m_currentDate)
	{
		closeLogFile();
		m_currentDate = today;
		openLogFile();
	}

	// 输出到文件
	logToFile(fullMessage);

	// 输出到控制台
	logToConsole(fullMessage);
}

inline void Logger::log(LogLevel level, int line, const char* function, const std::string& filename, const char* format, ...)
{
	// 格式化日志消息
	va_list va;
	va_start(va, format);
	std::string message = formatString(format, va);
	va_end(va);
	unsigned long pid = ProcessId();
	// 添加时间戳和调用位置信息
	std::string fullMessage = "[" + m_logLevelMapRef[(int)level] + "][0x" + NumberToHex(pid) + "]" + getCurrentTime() + " " + std::string(function) + ":" + std::to_string(line) + " " + message;

	std::ofstream file(filename, std::ios::out | std::ios::app);
	if (!file.is_open())
	{
		throw std::runtime_error("Failed to open log file: " + filename);
	}
	// 输出到文件
	logToFile(fullMessage, file);

	// 输出到控制台
	logToConsole(fullMessage);
}

inline void Logger::log(LogLevel level, int line, const char* function, std::ofstream& file, const char* format, ...)
{
	// 格式化日志消息
	va_list va;
	va_start(va, format);
	std::string message = formatString(format, va);
	va_end(va);
	unsigned long pid = ProcessId();
	// 添加时间戳和调用位置信息
	std::string fullMessage = "[" + m_logLevelMapRef[(int)level] + "][0x" + NumberToHex(pid) + "]" + getCurrentTime() + " " + std::string(function) + ":" + std::to_string(line) + " " + message;

	// 输出到文件
	logToFile(fullMessage, file);

	// 输出到控制台
	logToConsole(fullMessage);
}

inline unsigned long Logger::ProcessId()
{
#ifdef _WIN32
	DWORD pid = GetCurrentProcessId();
#else
	pthread_t pid = pthread_self();
#endif
	return pid;
}

inline std::string Logger::NumberToHex(long num)
{
	std::stringstream ss;
	ss << std::hex << num;
	return ss.str();
}

inline void Logger::openLogFile()
{
	if (m_logPath.empty())
	{
		return;
	}
	std::string filename = m_logPath + m_currentDate + ".log";
	m_logFile.open(filename, std::ios::out | std::ios::app);
	if (!m_logFile.is_open())
	{
		throw std::runtime_error("Failed to open log file: " + filename);
	}
}
inline void Logger::closeLogFile()
{
	if (m_logFile.is_open())
	{
		m_logFile.close();
	}
}
inline void Logger::logToFile(const std::string& message)
{
	if (m_logFile.is_open())
	{
		m_logFile << message << std::endl;
	}
}

inline void Logger::logToFile(const std::string& message, std::ofstream& file)
{
	if (file.is_open())
	{
		file << message << std::endl;
	}
}

inline void Logger::logToConsole(const std::string& message) const
{
	std::cerr << message << std::endl;
}

inline std::string Logger::formatString(const std::string& format, va_list args) const
{
	// 计算需要的缓冲区大小
	va_list va;
	va_copy(va, args);
	int size = std::vsnprintf(nullptr, 0, format.c_str(), va);
	va_end(va);

	if (size < 0)
	{
		throw std::runtime_error("Failed to format log message");
	}

	// 分配缓冲区并格式化字符串
	std::vector<char> buffer(size + 1);
	std::vsnprintf(buffer.data(), buffer.size(), format.c_str(), args);

	return std::string(buffer.data());
}

inline std::string Logger::getCurrentDate() const
{
	auto now = std::chrono::system_clock::now();
	auto time = std::chrono::system_clock::to_time_t(now);
	std::tm tm = *std::localtime(&time);
	std::stringstream ss;
	ss << std::put_time(&tm, "%Y-%m-%d");
	return ss.str();
}

inline std::string Logger::getCurrentTime() const
{
	// 获取当前时间点
	auto now = std::chrono::system_clock::now();

	// 转换为时间戳(秒级)
	auto time = std::chrono::system_clock::to_time_t(now);
	std::tm tm = *std::localtime(&time);

	// 获取毫秒部分
	auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(
		now.time_since_epoch()
	) % 1000;

	// 格式化输出
	std::stringstream ss;
	ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << "." << std::setfill('0') << std::setw(3) << milliseconds.count();
	return ss.str();
}

inline void Logger::initializeLogLevelMap()
{
	// 初始化日志级别映射
	m_logLevelMap = {
		{"T", LogLevel::Trace},
		{"D", LogLevel::Debug},
		{"I", LogLevel::Info},
		{"W", LogLevel::Warning},
		{"E", LogLevel::Error},
		{"F", LogLevel::Fatal}
	};
	for (std::unordered_map<std::string, LogLevel>::iterator it = m_logLevelMap.begin(); it != m_logLevelMap.end(); it++)
	{
		m_logLevelMapRef[(int)it->second] = it->first;
	}
}

// 日志宏定义
#define LOG(level, format, ...) \
    Logger::getInstance().log(level, __LINE__, __FUNCTION__, format, ##__VA_ARGS__)

#define TRACE(format, ...) LOG(LogLevel::Trace, format, ##__VA_ARGS__)
#define DEBUG(format, ...) LOG(LogLevel::Debug, format, ##__VA_ARGS__)
#define INFO(format, ...)  LOG(LogLevel::Info, format, ##__VA_ARGS__)
#define WARN(format, ...)  LOG(LogLevel::Warning, format, ##__VA_ARGS__)
#define ERROR(format, ...) LOG(LogLevel::Error, format, ##__VA_ARGS__)
#define FATAL(format, ...) LOG(LogLevel::Fatal, format, ##__VA_ARGS__)

#define LogFile(level, file, format, ...) Logger::getInstance().log(level, __LINE__, __FUNCTION__, file, format, ##__VA_ARGS__)
#define LogToFile(level, file, format, ...) Logger::getInstance().log(level, __LINE__, __FUNCTION__, file, format, ##__VA_ARGS__)

#endif // LOGGER_HPP
// String.hpp
#pragma once

#include <string>
#include <vector>
#include <regex>

class String :public std::string
{
public:
	String() :std::string("") { }
	String(const String& str) :std::string(str) { }
	String(const std::string& str) :std::string(str) { }
	String(const char* str) :std::string(str) { }
	std::vector<std::string> split(const std::string& delimiter);
};

inline std::vector<std::string> String::split(const std::string& delimiter)
{
	std::vector<std::string> tokens;
	std::regex reg(delimiter);
	std::sregex_token_iterator iter(this->begin(), this->end(), reg, -1);
	std::sregex_token_iterator end;
	while (iter != end)
	{
		tokens.push_back(*iter++);
	}
	return tokens;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT灰猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值