CPP服务器03--日志类实现

日志类实现

功能需求:
- 允许多个线程同时写,但刷入磁盘必须保持相对顺序
实现思路:
  • 组件:

    • 阻塞队列 std::unique_ptr<BlockQueuestd::string> queue_;
    • 刷盘线程 std::unique_ptrstd::thread writeThread_;
    • 日志容器 Buffer buff_;
    • 互斥锁 std::mutex mtx_;
  • 思路:

    • 单例模式实现一个日志类;
      • 外界调用不断添加日志进阻塞队列queue_;
      • 写线程不断从阻塞队列取日志刷盘;
    • 互斥锁用来控制对buff_的访问
头文件
#ifndef LOG_H
#define LOG_H

#include "blockqueue.hpp"
#include "../buffer/buffer.h"

#include <mutex>
#include <string>
#include <thread>
#include <sys/time.h>
#include <string.h>
#include <stdarg.h>           // vastart va_end
#include <assert.h>
#include <sys/stat.h>         //mkdir

class Log {
public:
    static Log* Instance(); //静态实例
    static void FlushLogThread();//刷盘线程传函
    
    void init(int level, const char* path = "./log",
                int maxQueueCapacity = 1024);
    void write(int level, const char *format,...);//写入阻塞队列
    void flush();//清空队列
    int GetLevel();
    
private:
    Log();
    void AppendLogLevelTitle_(int level);
    virtual ~Log();
    void AsyncWrite_();

private:
    const char* path_;
    int level_;

    FILE* fp_;
    Buffer buff_;

    std::unique_ptr<BlockQueue<std::string>> queue_; 
    std::unique_ptr<std::thread> writeThread_;
    std::mutex mtx_;

};

#define LOG_BASE(level, format, ...) \
    do {\
        Log* log = Log::Instance();\
        if (log->GetLevel() <= level) {\
            log->write(level, format, ##__VA_ARGS__); \
            log->flush();\
        }\
    } while(0);

#define LOG_DEBUG(format, ...) do {LOG_BASE(0, format, ##__VA_ARGS__)} while(0);
#define LOG_INFO(format, ...) do {LOG_BASE(1, format, ##__VA_ARGS__)} while(0);
#define LOG_WARN(format, ...) do {LOG_BASE(2, format, ##__VA_ARGS__)} while(0);
#define LOG_ERROR(format, ...) do {LOG_BASE(3, format, ##__VA_ARGS__)} while(0);

#endif //LOG_H
源文件
#include "log.h"

using namespace std;

Log::Log() {
    writeThread_ = nullptr;
    queue_ = nullptr;
    fp_ = nullptr;
}

Log::~Log() {
    if(writeThread_ && writeThread_->joinable()) {
        while(! queue_->empty()) {
             queue_->flush();
        };
         queue_->Close();
        writeThread_->join();
    }
    if(fp_) {
        lock_guard<mutex> locker(mtx_);
        flush();
        fclose(fp_);
    }
}

int Log::GetLevel() {
    lock_guard<mutex> locker(mtx_);
    return level_;
}

void Log::init(int level = 1, const char* path, int maxQueueSize) {
    level_ = level;
    path_ = path;

    unique_ptr<BlockQueue<std::string>> newQueue(new BlockQueue<std::string>);
    queue_ = move(newQueue);
    std::unique_ptr<std::thread> NewThread(new thread(FlushLogThread));
    writeThread_ = move(NewThread);

    time_t timer = time(nullptr);
    struct tm *sysTime = localtime(&timer);
    struct tm t = *sysTime;
    char fileName[256] = {0};
    snprintf(fileName, 256 - 1, "%s/%04d_%02d_%02d%s", 
            path_, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, ".log");

    lock_guard<mutex> locker(mtx_);
    buff_.RetrieveAll();
    if(fp_) { 
        flush();
        fclose(fp_); 
    }

    fp_ = fopen(fileName, "a");
    if(fp_ == nullptr) {
        mkdir(path_, 0777);
        fp_ = fopen(fileName, "a");
    } 
    assert(fp_ != nullptr);
}

void Log::write(int level, const char *format, ...) {
    struct timeval now = {0, 0};
    gettimeofday(&now, nullptr);
    time_t tSec = now.tv_sec;
    struct tm *sysTime = localtime(&tSec);
    struct tm t = *sysTime;
    va_list vaList;

    unique_lock<mutex> locker(mtx_);
    
    int n = snprintf(buff_.BeginWrite(), 128, "%d-%02d-%02d %02d:%02d:%02d.%06ld ",
                t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
                t.tm_hour, t.tm_min, t.tm_sec, now.tv_usec);
                
    buff_.HasWritten(n);
    AppendLogLevelTitle_(level);

    va_start(vaList, format);
    int m = vsnprintf(buff_.BeginWrite(), buff_.WritableBytes(), format, vaList);
    va_end(vaList);

    buff_.HasWritten(m);
    buff_.Append("\n\0", 2);

    if(queue_ && ! queue_->full()) {
            queue_->push(buff_.RetrieveAllToStr());
    } else {
        fputs(buff_.Peek(), fp_);
    }
    buff_.RetrieveAll();
}

void Log::AppendLogLevelTitle_(int level) {
    switch(level) {
    case 0:
        buff_.Append("[debug]: ", 9);
        break;
    case 1:
        buff_.Append("[info] : ", 9);
        break;
    case 2:
        buff_.Append("[warn] : ", 9);
        break;
    case 3:
        buff_.Append("[error]: ", 9);
        break;
    default:
        buff_.Append("[info] : ", 9);
        break;
    }
}

void Log::flush() {
    queue_->flush(); 
    fflush(fp_);
}

void Log::AsyncWrite_() {
    string str = "";
    while( queue_->pop(str)) {
        lock_guard<mutex> locker(mtx_);
        fputs(str.c_str(), fp_);
    }
}

Log* Log::Instance() {
    static Log inst;
    return &inst;
}

void Log::FlushLogThread() {
    Log::Instance()->AsyncWrite_();
}
测试代码1
#include "../log/blockqueue.hpp"
#include "../log/log.h"

#include "pthread.h"
#include <cstdlib>
#include <ctime>
#include <unistd.h>



void * writeLog(void *arg)
{
    srand(time(0));
    for(int i=0; i<5; i++)
    {
        sleep(rand() % 3);
        LOG_DEBUG("DEBUG: %ld>>%d",pthread_self(), i);
        LOG_INFO("INFO: %ld>>%d",pthread_self(), i);
        LOG_WARN("WARN: %ld>>%d",pthread_self(), i);
        LOG_ERROR("ERROR: %ld>>%d",pthread_self(), i);
    }
    
}


int main()
{
    int logLevel = 0;
    Log::Instance()->init(logLevel, "./log");

    for(int i=0; i<3; i++)
    {
        pthread_t tid;
        pthread_create(&tid, NULL, writeLog, NULL);
        pthread_join(tid, NULL);// log析构函数中用到了join方法
    }

    return 0;
}
引用
  • 源代码:https://github.com/Aged-cat/WebServer
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ColaForced

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

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

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

打赏作者

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

抵扣说明:

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

余额充值