C++:可变参数实现日志系统

文章目录

最近在写项目,在处理到日志模块的过程中考虑到使用不方便,所以特此实现一个日志系统,并且里面要实现一个和printf效果类似的一个可变参数版的打印信息

在这篇博客中,我们将深入探讨一个用C++编写的自定义日志类的设计与实现细节。该日志类允许开发者根据不同的需求选择输出日志的方式(屏幕输出、单文件输出或按等级分类输出到不同文件),并提供了便捷的方法生成格式化的日志信息。

首先,我们引入了必要的头文件,包括iostream用于标准输入输出,time.h用于获取时间信息,以及一些系统调用相关头文件如stdarg.h、sys/types.h、sys/stat.h、fcntl.h、unistd.h和stdlib.h,这些在打开、关闭文件和文件操作中起到关键作用。

接下来定义了一些宏常量,如SIZE作为缓冲区大小,以及各种日志级别(Info、Debug、Warning、Error、Fatal)和输出方式(Screen、Onefile、Classfile)。同时预设了一个默认的日志文件名"log.txt"。

Log类的核心功能如下:

  1. 构造函数初始化时,默认设置输出方式为屏幕打印,并设定日志存储路径为当前目录下的"log/"。

  2. Enable()方法用于启用指定的日志输出方式。

  3. levelToString()是一个转换函数,将整型的日志级别转换为对应的字符串形式。

  4. printLog()是核心的打印日志方法,根据设置的输出方式进行日志打印。如果选择的是Screen,则直接输出到控制台;如果是Onefile则将所有日志写入同一文件"log.txt";若选择Classfile,则按日志级别创建不同文件分别记录对应级别的日志。

  5. printOneFile()printClassFile()负责具体文件的写入操作。其中,printOneFile()直接写入单一文件,printClassFile()则是先调用levelToString()得到对应日志级别的字符串,然后附加到"log.txt"后面形成新的文件名进行写入。

  6. 类中还定义了一个析构函数,确保资源正确释放。

  7. 最后,重载了运算符(),以实现简洁的日志记录语法。它接受日志级别、格式化字符串及可变参数列表,通过va_arg处理可变参数,并结合当前时间信息生成格式化的日志文本,最后调用printLog()进行输出。

实例化后的全局对象lg,使得开发人员可以方便地在程序任何地方使用lg(level, format, ...)来记录日志,从而大大简化了日志系统的使用和管理。

// 头文件包含部分
#pragma once // 防止头文件被重复包含
#include <iostream> // 提供标准输入输出流操作
#include <time.h> // 提供与时间相关的函数,如获取当前时间
#include <stdarg.h> // 提供可变参数列表的支持
#include <sys/types.h> // 定义基本系统数据类型
#include <sys/stat.h> // 提供文件状态结构体和文件操作权限等信息
#include <fcntl.h> // 提供文件描述符标志和文件打开模式常量
#include <unistd.h> // 提供与文件I/O、进程控制等相关函数
#include <stdlib.h> // 提供通用的函数,如内存分配和错误处理

// 定义日志级别和输出方式常量
#define SIZE 1024 // 日志缓冲区大小
#define Info 0 // 信息级别日志
#define Debug 1 // 调试级别日志
#define Warning 2 // 警告级别日志
#define Error 3 // 错误级别日志
#define Fatal 4 // 致命级别日志
#define Screen 1 // 输出到屏幕
#define Onefile 2 // 所有日志记录在一个文件中
#define Classfile 3 // 按日志级别分类存放在不同文件中
#define LogFile "log.txt" // 默认日志文件名

// 定义日志类
class Log
{
public:
    // 构造函数,初始化日志对象
    Log()
    {
        // 设置默认的日志输出方式为屏幕打印
        printMethod = Screen;
        // 设置日志文件存放路径为当前目录下的"log/"子目录
        path = "./log/";
    }

    // 启用指定的日志输出方式
    void Enable(int method)
    {
        printMethod = method;
    }

    // 将日志级别转换为对应的字符串形式
    std::string levelToString(int level)
    {
        // 使用switch-case语句根据不同日志级别返回相应字符串
        switch (level)
        {
            case Info:
                return "Info";
            case Debug:
                return "Debug";
            case Warning:
                return "Warning";
            case Error:
                return "Error";
            case Fatal:
                return "Fatal";
            default:
                return "None"; // 如果级别不合法,则返回"None"
        }
    }

    // 核心日志打印方法,根据设置的输出方式输出日志
    void printLog(int level, const std::string &logtxt)
    {
        switch (printMethod)
        {
            case Screen:
                // 输出到标准输出流(通常指控制台)
                std::cout << logtxt << std::endl;
                break;
            case Onefile:
                // 将所有日志记录在同一文件中
                printOneFile(LogFile, logtxt);
                break;
            case Classfile:
                // 按日志级别分类输出到不同文件
                printClassFile(level, logtxt);
                break;
            default:
                // 若未指定有效输出方式,则不做任何操作
                break;
        }
    }

    // 将日志写入指定文件
    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        // 构造完整的文件路径
        std::string _logname = path + logname;

        // 打开或追加写入文件,创建文件不存在时
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
        if (fd < 0)
        {
            // 文件打开失败,则返回
            return;
        }

        // 将日志内容写入文件
        write(fd, logtxt.c_str(), logtxt.size());

        // 关闭已打开的文件描述符
        close(fd);
    }

    // 按日志级别分类输出到不同文件
    void printClassFile(int level, const std::string &logtxt)
    {
        // 构造带有日志级别后缀的文件名
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level);

        // 调用printOneFile方法,将日志写入具有级别后缀的文件中
        printOneFile(filename, logtxt);
    }

    // 析构函数,无特殊操作
    ~Log()
    {
    }

    // 重载运算符(),用于简化日志记录过程
    void operator()(int level, const char *format, ...)
    {
        // 获取当前时间
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);

        // 创建左侧时间与日志级别的标签
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", 
                 levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        // 处理可变参数列表
        va_list s;
        va_start(s, format);
        
        // 创建右侧格式化日志内容
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);

        // 结束可变参数列表的处理
        va_end(s);

        // 将左右两部分拼接成完整日志文本
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);

        // 打印最终的格式化日志
        printLog(level, logtxt);
    }

private:
    // 成员变量
    int printMethod; // 当前使用的日志输出方式
    std::string path; // 日志文件存放路径

    // 私有方法:由于已经通过printLog公开调用,因此这里仅列出而不做注释
    // void printOneFile(const std::string &, const std::string &);
    // void printClassFile(int, const std::string &);
};

// 全局Log对象实例,便于在程序任何地方调用记录日志
Log lg;

总结来说,这个C++日志类提供了一种灵活且实用的方式来管理和输出日志信息,可以根据项目需求调整日志输出模式,并支持日志级别分类和时间戳标记,有助于提高软件的调试效率和运行状态监控能力。

相关推荐

  1. C++:可变参数实现日志系统

    2024-03-24 23:04:02       22 阅读
  2. C++ 可变参数模板

    2024-03-24 23:04:02       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-24 23:04:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-24 23:04:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-24 23:04:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-24 23:04:02       20 阅读

热门阅读

  1. 求一元二次方程的根---PTA实验C++

    2024-03-24 23:04:02       19 阅读
  2. C语言——数组

    2024-03-24 23:04:02       18 阅读
  3. SQL管理员高频面试问题

    2024-03-24 23:04:02       18 阅读
  4. Spring 主要模块有哪些?

    2024-03-24 23:04:02       19 阅读
  5. 【无标题】

    2024-03-24 23:04:02       15 阅读
  6. 5467: 【搜索】流浪奶牛

    2024-03-24 23:04:02       17 阅读
  7. Linux系统docker创建MySQL服务

    2024-03-24 23:04:02       19 阅读
  8. opencv瑕疵检测一般流程

    2024-03-24 23:04:02       15 阅读
  9. 话题——AI大模型学习

    2024-03-24 23:04:02       20 阅读
  10. VisionPro 9.0 64位下载地址

    2024-03-24 23:04:02       20 阅读