一个简洁的 C++ 日志模块实现

一个简洁的 C++ 日志模块实现

1. 引言

日志功能在软件开发中扮演着至关重要的角色,它帮助开发者追踪程序执行过程、诊断问题以及监控系统运行状态。本文介绍一个使用 C++ 实现的轻量级日志模块,该模块支持多日志级别、线程安全,并提供了简洁易用的接口。

2. 代码实现

2.1 主程序 (main.cpp)

#include "log.h"

int main(int argc, char *argv[])
{
    Log::GetInstance().Init("./log");
    Log::GetInstance().WriteLog(ERROR, "error");
    Log::GetInstance().WriteLog(WARNING, "warning");
    Log::GetInstance().WriteLog(DEBUG, "debug");
    Log::GetInstance().WriteLog(INFO, "info");
    
    return 0;
}

2.2 头文件 (log.h)

#ifndef LOG_H
#define LOG_H

#include <iostream>
#include <pthread.h>
#include <cstdio>

using namespace std;

// 日志级别枚举
typedef enum {
    ERROR = 0,
    WARNING,
    DEBUG,
    INFO,
    LEVEL_MAX
} LogLevel_en;

// 日志状态枚举
typedef enum {
    LOG_INIT_STATE,
    LOG_READY_STATE,
    LOG_INVALID_STATE
} LogState_en;

#define LOG_NUM 255U

class Log {
private:
    char m_log_buf[LOG_NUM];        // 日志缓冲区
    pthread_mutex_t m_mutex;        // 互斥锁,保证线程安全
    FILE *m_log_fd;                 // 日志文件指针
    LogState_en m_log_state;        // 日志模块状态

public:
    static Log& GetInstance(void);  // 获取单例实例
    void Init(const char *dir_file_name);  // 初始化日志系统
    void WriteLog(LogLevel_en log_level, const char* format, ...);  // 写日志接口

private:
    Log(void) {
        m_log_state = LOG_INVALID_STATE;
    };
    ~Log(void) = default;
};

#endif

2.3 实现文件 (log.cpp)

#include "log.h"
#include <ctime>
#include <cstring>
#include <cstdarg>

// 获取日志单例实例
Log& Log::GetInstance(void)
{
    static Log instance;
    return instance;
}

// 初始化日志系统
void Log::Init(const char *dir_file_name)
{
    if(nullptr != dir_file_name)
    {
        m_log_state = LOG_INIT_STATE;
        cout << "Log file: " << dir_file_name << endl;
        m_log_fd = fopen(dir_file_name, "a");
        if (m_log_fd != nullptr) {
            m_log_state = LOG_READY_STATE;
        }
    }
}

// 写入日志信息
void Log::WriteLog(LogLevel_en log_level, const char* format, ...)
{
    // 参数有效性检查
    if((LEVEL_MAX <= log_level) || (nullptr == format)) {
        return;
    }
    
    // 日志状态检查
    if (LOG_READY_STATE != m_log_state) {
        return;
    }
    
    // 设置日志级别字符串
    char buffer_log_level[16] = {0};
    switch (log_level) {
        case ERROR:
            strcpy(buffer_log_level, "[error]:");
            break;
        case WARNING:
            strcpy(buffer_log_level, "[warning]:");
            break;
        case DEBUG:
            strcpy(buffer_log_level, "[debug]:");
            break;
        case INFO:
            strcpy(buffer_log_level, "[info]:");
            break;
        default:
            cout << "Invalid log level" << endl;
            return;
    }

    // 设置时间戳字符串
    time_t time_val = time(nullptr);
    struct tm *p_tm_val = localtime(&time_val);
    char buffer_time[48] = {0};
    snprintf(buffer_time, 48, "%.4d-%.2d-%.2d-%.2d-%.2d-%.2d:", 
        p_tm_val->tm_year + 1900,
        p_tm_val->tm_mon + 1,
        p_tm_val->tm_mday,
        p_tm_val->tm_hour,
        p_tm_val->tm_min,
        p_tm_val->tm_sec
    );

    // 加锁保证线程安全
    pthread_mutex_lock(&m_mutex);
    
    // 组装日志前缀
    memset(m_log_buf, 0, sizeof(m_log_buf));
    u_int8_t log_len = strlen(buffer_log_level) + strlen(buffer_time) + 1;
    int n = snprintf(m_log_buf, log_len, "%s%s", buffer_log_level, buffer_time);
    
    if(n > 0) {
        // 处理可变参数
        va_list variable_list;
        va_start(variable_list, format);
        int m = vsnprintf(m_log_buf + n, LOG_NUM - n - 1, format, variable_list);
        va_end(variable_list);
        
        if(m > 0) {
            // 输出到控制台和文件
            cout << m_log_buf << endl;
            m_log_buf[n + m] = '\n';
            m_log_buf[n + m + 1] = '\0';
            fputs(m_log_buf, m_log_fd);
            fflush(m_log_fd);  // 确保数据写入磁盘
        } else {
            cout << "vsnprintf error!" << endl;
        }
    } else {
        cout << "snprintf error!" << endl;
    }
    
    // 释放锁
    pthread_mutex_unlock(&m_mutex);
}

2.4 Makefile 构建配置

# 目标程序名称
TGT := app

CUR_DIR := $(shell pwd)

# 源文件设置
SRC := $(wildcard *.cpp)
OBJ := $(patsubst %.cpp,%.o,$(SRC))

# 编译选项设置
CPPFLAGS := -I.
CPPFLAGS += -pthread
CPPFLAGS += -I${CUR_DIR}/include

# 编译器标志
CXXFLAGS := -Wall -O2

# 默认构建目标
all: $(TGT)
	@echo "Build successful"

$(TGT): $(OBJ)
	$(CXX) -std=c++11 $(CPPFLAGS) $(CXXFLAGS) $^ -o $@

%.o: %.cpp
	$(CXX) -std=c++11 $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

# 清理构建文件
clean:
ifneq ($(wildcard $(OBJ)),)
	@rm $(OBJ)
else
	@echo "No object files to remove"
endif
ifneq ($(wildcard $(TGT)),)
	@rm $(TGT) 
else
	@echo "No target executable to remove"
endif

# 仅清理对象文件
obj_clean:
ifneq ($(wildcard $(OBJ)),)
	@rm $(OBJ)
else
	@echo "No object files to remove"
endif

.PHONY: obj_clean clean all

3. 编译与运行结果

执行构建命令:

$ make
g++ -std=c++11 -I. -pthread -I/mnt/code/01_comprehensive/log_demo/include -Wall -O2 -c log.cpp -o log.o
g++ -std=c++11 -I. -pthread -I/mnt/code/01_comprehensive/log_demo/include -Wall -O2 -c main.cpp -o main.o
g++ -std=c++11 -I. -pthread -I/mnt/code/01_comprehensive/log_demo/include -Wall -O2 log.o main.o -o app
Build successful

运行程序:

$ ./app 
Log file: ./log
[error]:2024-06-29-11-33-03:error
[warning]:2024-06-29-11-33-03:warning
[debug]:2024-06-29-11-33-03:debug
[info]:2024-06-29-11-33-03:info

查看生成的日志文件:

$ cat log
[error]:2024-06-29-11-23-43:error
[warning]:2024-06-29-11-23-43:warning
[debug]:2024-06-29-11-23-43:debug
[info]:2024-06-29-11-23-43:info

4. 设计特点

  1. 单例模式:确保整个应用程序中只有一个日志实例
  2. 线程安全:使用互斥锁保护共享资源,支持多线程环境
  3. 多日志级别:支持 ERROR、WARNING、DEBUG、INFO 四种级别
  4. 时间戳:每条日志都包含精确到秒的时间信息
  5. 双重输出:日志同时输出到控制台和文件
  6. 格式化支持:支持 printf 风格的格式化输出

这个日志模块虽然简洁,但提供了基本日志功能所需的核心特性,适合在中小型项目中使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青草地溪水旁

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

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

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

打赏作者

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

抵扣说明:

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

余额充值