c++11标准实现简单日志(windows、linux通用的)
## threadbase2.h
// c++11
#ifndef _THREADBASE2_H_
#define _THREADBASE2_H_
#pragma execution_character_set("utf-8")
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
// 封装互斥锁操作的类
class MutexWrapper {
public:
void lock() {
mtx.lock();
}
void unlock() {
mtx.unlock();
}
private:
std::mutex mtx;
};
// 封装条件变量操作的类
class ConditionVariableWrapper {
public:
void wait(std::unique_lock<std::mutex>& lock) {
cv.wait(lock);
}
void notify_one() {
cv.notify_one();
}
void notify_all() {
cv.notify_all();
}
private:
std::condition_variable cv;
};
// 封装信号量操作的类
// 使用C++11的互斥锁和条件变量模拟信号量
class SemaphoreWrapper {
public:
SemaphoreWrapper(int count) : count_(count) {}
void acquire() {
std::unique_lock<std::mutex> lock(mutex_);
// 检查count_防止虚假唤醒
cv_.wait(lock, [this] { return count_ > 0; });
--count_;
}
void release() {
std::unique_lock<std::mutex> lock(mutex_);
++count_;
cv_.notify_one();
}
private:
std::mutex mutex_;
std::condition_variable cv_;
int count_;
};
#endif
## ThreadPool.h
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <stdexcept>
#include "threadbase2.h"
#include <functional>
#include <future>
class ThreadPool
{
public:
// 获取单例实例的静态方法。保证只有一个实例存在.
static ThreadPool &getInstance(int ThreadNum, int SemNum)
{
static ThreadPool instance(ThreadNum, SemNum);
return instance;
}
// 禁用拷贝构造函数和赋值运算符
ThreadPool(const ThreadPool &) = delete;
ThreadPool &operator=(const ThreadPool &) = delete;
// 默认4个线程, 初始化信号量为0
ThreadPool(size_t threads = 4, int initsem = 0)
: stop(false), semaphore(initsem)
{
for (size_t i = 0; i < threads; ++i)
// emplace_back 直接在容器中构造元素,而不是先创建一个临时对象,然后再将其复制到容器中
workers.emplace_back([this]
{
while (true) {
std::function<void()> task;
{
this->semaphore.acquire();
if (this->stop && this->tasks.empty())
return;
std::unique_lock<MutexWrapper> lock(this->queue_mutex);
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
this->semaphore.release();
} });
}
// 模板类型参数`F` 与可变参数模板集合
template <class F, class... Args>
auto enqueue(F &&f, Args &&...args)
// std::future 是一个类模板,用于表示异步操作的结果。
// std::result_of 是一个类型特征,用于获取函数调用的返回类型。
// `typename` 这里用于指定模板参数类型
-> std::future<typename std::result_of<F(Args...)>::type>
{
struct result_of_wrapper
{
// 使用typedef定义类型别名,否则编译器会认为是一个变量声明而报错
using type = typename std::result_of<F(Args...)>::type;
};
// 这里需要根据具体情况修正,示例代码暂不完整,假设修正为合适的类型
using return_type = typename std::result_of<F(Args...)>::type;
// std::make_shared 用于创建一个 std::shared_ptr 对象,并将其初始化为指向一个新分配的对象。这里指向新分配的`std::packaged_task` 对象
// std::packaged_task 是一个类模板,用于包装可调用对象,以便异步执行和获取结果。这里主要是为了异步!!!
// `std::packaged_task` 调用后不会直接返回结果,而是将结果存储在`std::future` 对象中,需通过`std::future::get()` 方法获取
// std::bind 用于创建一个函数对象,将参数绑定到可调用对象上。
// std::forward 用于完美转发参数,保持参数的左值或右值属性。
// 假设修正为合适的代码,需要根据具体情况调整
auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> res = task->get_future();
// 加入{}确保在添加任务到队列后,尽快释放互斥锁,减少其他线程等待锁的时间,提高并发性能。
{
std::unique_lock<MutexWrapper> lock(queue_mutex);
if (stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
// 将打包好的任务对象(std::packaged_task)封装成无参的void函数
tasks.emplace([task](){ (*task)(); });
this->semaphore.release();
}
return res;
}
~ThreadPool()
{
{
// 这里可以使用带时间的锁,避免死锁
std::unique_lock<MutexWrapper> lock(queue_mutex);
stop = true;
}
// 所有线程检查停止标志
for (size_t i = 0; i < workers.size(); ++i)
{
this->semaphore.release();
}
// 等待所有线程完成
for (std::thread &worker : workers)
{
worker.join();
}
}
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
// 这里互斥锁标记的是任务队列
MutexWrapper queue_mutex;
// 这里信号量标记的是任务数量
SemaphoreWrapper semaphore;
bool stop;
};
#endif
## Log.h
# ifndef LOG_H
#define LOG_H
#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <future>
#include "ThreadPool.h"
#include <string>
#include <vector>
#include <iomanip>
#include <ctime>
#include <sys/stat.h>
#include <algorithm>
#include <sstream>
#ifdef _WIN32
#include <windows.h>
#endif
#include <unistd.h>
class Log
{
public:
Log(int ThreadNum=4,int SemNum=0) : pool(ThreadPool::getInstance(ThreadNum,SemNum)) {}
~Log() {}
template <class F, class... Args>
// 这里主要是为了隐藏线程池的实现细节,让用户只需要调用enqueue方法即可
auto enqueue(F &&f, Args &&...args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
return pool.enqueue(std::forward<F>(f), std::forward<Args>(args)...);
}
private:
// 使用引用避免拷贝
ThreadPool &pool;
};
// 日志级别枚举
enum class LogLevel
{
DEBUG,
INFO,
WARNING,
ERROR_LEVEL,
};
// 日志记录类
class Logger
{
public:
Logger(int MaxLogNumber = 10, \
int MaxLogNumberDigits = 2,\
int MaxLogLine = 100, \
int MaxLogChar = 200, \
int ThreadCount = 10,\
int SemCount = 0):\
// 最大日志数量
MAX_LOG_NUMBER(MaxLogNumber),\
// 日志数量占用位数
MAX_LOG_NUMBER_DIGITS(MaxLogNumberDigits), \
// 最大日志行数
MAX_LOG_LINE(MaxLogLine), \
// 最大每行字符数
MAX_LOG_CHAR(MaxLogChar), \
// 线程数量
THREAD_COUNT(ThreadCount), \
// 信号量数量
SEM_COUNT(SemCount)
{
currentLogCount = 0;
generateLogFileName();
}
// 懒汉模式单例实现
static Logger &getInstance(
int MaxLogNumber = 10, \
int MaxLogNumberDigits = 2,\
int MaxLogLine = 100, \
int MaxLogChar = 200, \
int ThreadCount = 10,\
int SemCount = 0
){
static Logger instance(MaxLogNumber,MaxLogNumberDigits,MaxLogLine,MaxLogChar,ThreadCount,SemCount);
return instance;
}
// 同步日志记录
void logSync(LogLevel level, const std::string &message)
{
std::lock_guard<std::mutex> lock(mutex);
if (currentLogCount >= MAX_LOG_LINE)
{
generateLogFileName();
currentLogCount = 0;
}
std::string trimmedMessage = message;
// 限制每条日志的最大字符数
if (trimmedMessage.length() > MAX_LOG_CHAR)
{
trimmedMessage = trimmedMessage.substr(0, MAX_LOG_CHAR);
}
writeLog(level, trimmedMessage);
currentLogCount++;
}
// 异步日志记录
void logAsync(LogLevel level, const std::string &message)
{
Log log;
std::string trimmedMessage = message;
if (trimmedMessage.length() > MAX_LOG_CHAR)
{
trimmedMessage = trimmedMessage.substr(0, MAX_LOG_CHAR);
}
log.enqueue([this, level, trimmedMessage]() {
this->logSync(level, trimmedMessage);
});
}
private:
// 日志名称
std::string logFile;
// 当前日志行数
int currentLogCount;
// 互斥锁
std::mutex mutex;
// 当前最大日志编号
int MAX_LOG_NUMBER = 10;
// 当前日志编号占用位数
int MAX_LOG_NUMBER_DIGITS = 2;
// 当前日志最大行数
int MAX_LOG_LINE = 100;
// 当前日志最大每行字符数
int MAX_LOG_CHAR = 200;
// 线程数量
int THREAD_COUNT = 10;
// 信号量数量
int SEM_COUNT = 0;
// 写入日志文件
void writeLog(LogLevel level, const std::string &message)
{
std::ofstream file(logFile, std::ios::app);
if (file.is_open())
{
std::string levelStr;
switch (level)
{
case LogLevel::DEBUG:
levelStr = "DEBUG";
break;
case LogLevel::INFO:
levelStr = "INFO";
break;
case LogLevel::WARNING:
levelStr = "WARNING";
break;
case LogLevel::ERROR_LEVEL:
levelStr = "ERROR";
break;
}
// 给每条日志加上时间戳
std::time_t now = std::time(nullptr);
std::tm localTime;
#ifdef _WIN32
localtime_s(&localTime, &now);
#else
localtime_r(&now, &localTime);
#endif
file << "[" << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S") << "] "
<< "[" << levelStr << "] " << message << std::endl;
file.close();
}
}
void generateLogFileName()
{
// 确保包含<filesystem>头文件
#ifdef _WIN32
TCHAR directory[MAX_PATH];
DWORD len = GetCurrentDirectory(MAX_PATH, directory);
if (len == 0 || len > MAX_PATH) {
std::cerr << "Get current directory failed" << std::endl;
return;
}
if (!CreateDirectory(directory, NULL)) {
if (GetLastError() != ERROR_ALREADY_EXISTS) {
std::cerr << "Create directory failed" << GetLastError() << std::endl;
return;
}
}
#else
// Linux/macOS实现
char directory[FILENAME_MAX];
getcwd(directory, FILENAME_MAX);
struct stat info;
if (stat(directory, &info) != 0 || !(info.st_mode & S_IFDIR)) {
mkdir(directory, 0755);
}
#endif
std::time_t now = std::time(nullptr);
std::tm localTime;
// 修改为使用 localtime 函数
localTime = *std::localtime(&now);
// 日志文件名首先加上日期
std::ostringstream oss;
oss << std::put_time(&localTime, "%Y%m%d");
std::string datePrefix = oss.str();
int LogNumber = 0;
for (int i = 0; LogNumber < MAX_LOG_NUMBER; ++i)
{
std::ostringstream oss1;
// 日志文件名加上序号
oss1 << datePrefix << std::setw(MAX_LOG_NUMBER_DIGITS) << std::setfill('0') << i << ".log";
std::string logFileName = std::string(directory) + "/" + oss1.str();
struct stat fileInfo;
if (stat(logFileName.c_str(), &fileInfo) != 0)
{
LogNumber = i;
break;
}
// 检查日志行数
std::ifstream file(logFileName);
int lineCount = std::count(std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>(),
'\n');
if (lineCount < MAX_LOG_LINE)
{
LogNumber = i;
break;
}
}
if (LogNumber == MAX_LOG_NUMBER)
{
std::cout << "The number of logs has reached the limit, and a new log file cannot be generated." << std::endl;
return;
}
// 日志文件名加上序号
std::ostringstream oss2;
oss2 << datePrefix << std::setw(MAX_LOG_NUMBER_DIGITS) << std::setfill('0') << LogNumber << ".log";
logFile = std::string(directory) + "/" + oss2.str();
}
};
#define DEBUG(message) Logger::getInstance().logSync(LogLevel::DEBUG, message);
#define INFO(message) Logger::getInstance().logSync(LogLevel::INFO, message);
#define WARNING(message) Logger::getInstance().logSync(LogLevel::WARNING, message);
#define ERROR_LOG(message) Logger::getInstance().logSync(LogLevel::ERROR_LEVEL, message);
// #define DEBUG(message) Logger::getInstance().logAsync(LogLevel::DEBUG, message);
// #define INFO(message) Logger::getInstance().logAsync(LogLevel::INFO, message);
// #define WARNING(message) Logger::getInstance().logAsync(LogLevel::WARNING, message);
// #define ERROR_LOG(message) Logger::getInstance().logAsync(LogLevel::ERROR_LEVEL, message);
#endif LOG_H
## Log.cpp
#include "Log.h"
int main(){
// 假设这里调用Log类的单例模式实例并进行一些操作
Logger& logInstance = Logger::getInstance(3,2,100,200,4, 0);
// 可以在这里添加对logInstance的使用代码
int i=0;
// 这里开始记录时间
auto start = std::chrono::high_resolution_clock::now();
while(i<301){
// 生成1-4的随机数
int randomNum = rand() % 4 + 1;
switch (randomNum)
{
case 1:
DEBUG("aabb")
break;
case 2:
INFO("sadfgasd")
break;
case 3:
ERROR_LOG("123")
break;
case 4:
WARNING("78999")
break;
default:
break;
}
i++;
}
// 这里结束记录时间
auto end = std::chrono::high_resolution_clock::now();
// 计算时间差
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
// 输出时间差
std::cout << "Time taken: " << duration.count() << " microseconds" << std::endl;
//经测试,4线程异步输入日志的效率为同步日志的10倍左右。
system("pause");
return 0;
}