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;
}
posted @ 2025-03-17 11:22  小犟  阅读(49)  评论(0)    收藏  举报