自定义日志打印功能--C++

一、介绍

日志是计算机程序中用于记录运行时事件和状态的重要工具。通过记录关键信息和错误情况,日志可以帮助程序开发人员和维护人员追踪程序的执行过程,排查问题和改进性能。 在软件开发中,日志通常记录如下类型的信息:

  1. 事件信息:记录程序执行过程中的重要事件,如启动和关闭,特定操作完成等。
  2. 错误信息:记录发生的错误和异常情况,包括错误类型、位置和可能的原因。
  3. 警告信息:记录潜在问题或需要注意的情况,但不是严重到导致程序崩溃的程度。
  4. 调试信息:记录用于调试程序的详细信息,方便追踪程序状态和流程。 日志的记录一般包括时间戳、事件类型、事件描述等信息。这些记录可以写入文件、数据库或发送至远程服务器,以供后续分析和监控。

通过分析日志,开发人员可以了解程序的运行情况,发现潜在问题并优化程序性能。因此,良好的日志记录和管理对于确保软件运行稳定、故障排除和改进具有重要意义。

二、日志级别

#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4

const char* gLevelMap[]={
    "DEBUG",
    "NORMAL",
    "WARNING",
    "ERROR",
    "FATAL"
};

日志级别是一种对日志信息进行分类和标记的方法,用于帮助程序员和系统管理员更好地理解和处理日志信息。日志级别包括DEBUG(调试)、NORMAL(正常)、WARNING(警告)、ERROR(错误)和FATAL(严重错误)五种级别。通过宏定义将这五种级别编号化,可以在程序中方便地使用对应的编号来表示不同的日志级别。同时,通过使用一个数组 gLevelMap[],可以将编号与日志级别名对应起来,从而方便地在程序中根据编号找到对应的级别名,并进行相应的处理。这种分类和标记的方法有助于开发人员根据日志级别进行精细化的调试、监控和错误处理。

三、日志函数logMessage

// 日志打印 文件名
#define LOGFILE "./LOG.log"

void logMessage(int level, const char* format, ...)
{
    char timebuffer[1024];
    // 日志时间
    time_t timestamp = time(nullptr);
    tm* ltm = localtime(&timestamp);
    int year = ltm->tm_year+1900;
    int month = ltm->tm_mon+1;
    int day = ltm->tm_mday;
    int hour = ltm->tm_hour;
    int min = ltm->tm_min;
    int second = ltm->tm_sec;

    snprintf(timebuffer, sizeof timebuffer, "[%s][%04d-%02d-%02d %02d:%02d:%02d]",
                gLevelMap[level], year, month, day, hour, min, second);

    // 日志内容
    char logBuffer[1024];
    va_list args;  // 初始化参数信息
    va_start(args, format);  // 初始化参数列表
    vsnprintf(logBuffer, sizeof logBuffer, format, args);  //日志内容存入logBuffe中
    va_end(args);

    // 显示器打印日志信息
    printf("%s %s\n", timebuffer, logBuffer);

    // //往文件打印
    // FILE *fp = fopen(LOGFILE, "a");
    // fprintf(fp, "%s %s\n", timebuffer, logBuffer);
    // fclose(fp);
}   

logMessage(int level, const char* format, ...)该日志函数level代表日志等级、format表示日志内容、...是可变参数列表。这样使日志的使用跟printf()使用方法类似。

timebuffer:用于存储日志的等级和时间信息。

logBuffer:用于存储日志的内容。

内部解析

#define LOGFILE "./LOG.log"定义的一个日志文件,可以设置把打印的日志显示到这个文件里面。

    // 日志时间
    time_t timestamp = time(nullptr);
    tm* ltm = localtime(&timestamp);
    int year = ltm->tm_year+1900;
    int month = ltm->tm_mon+1;
    int day = ltm->tm_mday;
    int hour = ltm->tm_hour;
    int min = ltm->tm_min;
    int second = ltm->tm_sec;

time_t timestamp = time(nullptr);获取系统当前的时间戳。

tm* ltm = localtime(&timestamp);将时间戳转为用本地时区表示,比如Thu Aug 23 09:12:05 2012

localtime():原型(struct tm *localtime(const time_t *timer))localtime的函数声明。返回的是tm结构体,其结构如下:

struct tm {
   int tm_sec;         /* 秒,范围从 0 到 59*/
   int tm_min;         /* 分,范围从 0 到 59*/
   int tm_hour;        /* 小时,范围从 0 到 23*/
   int tm_mday;        /* 一月中的第几天,范围从 1 到 31 */
   int tm_mon;         /* 月份,范围从 0 到 11*/
   int tm_year;        /* 自 1900 起的年数 */
   int tm_wday;        /* 一周中的第几天,范围从 0 到 6 */
   int tm_yday;        /* 一年中的第几天,范围从 0 到 365 */
   int tm_isdst;       /* 夏令时*/    
};

所以通过调用ltm对象的内部变量,就可以获取想要的时间格式。例如int year = ltm->tm_year+1900;可以获取当前时间的年份(+1900是因为获取的年份是从1900年至今的)。

snprintf(timebuffer, sizeof timebuffer, "[%s][%04d-%02d-%02d %02d:%02d:%02d]",
                gLevelMap[level], year, month, day, hour, min, second);

使用格式化输出字符串snprintf函数,我们可以将当前的日志级别、时间,按指定的格式输入到缓冲区timebuffer中。这样,timebuffer中的内容就如同这个样子:([日志级别][时间])

    // 日志内容
    char logBuffer[1024];
    va_list args;  // 初始化参数信息
    va_start(args, format);  // 初始化参数列表
    vsnprintf(logBuffer, sizeof logBuffer, format, args);  //日志内容存入logBuffe中
    va_end(args);

format是传进来的日志内容,因为logMessge()有一个可变的参数列表...。所以需要va_list args;va_start(args, format);

va_list是C语言中的一个宏定义,用于表示一个变长参数列表。它是一个指向变长参数列表的指针,可以通过宏va_start、va_arg和va_end对变长参数列表进行访问和操作。在函数中需要接收不定数量的参数时,可以使用va_list来处理这些参数。

va_start:它的作用是初始化一个va_list类型的变量,使其指向可变参数列表的第一个参数。

vsnprintf()用于向一个字符串缓冲区打印格式化字符串,且可以限定打印的格式化字符串的最大长度。用它把日志的内容输入到logBuffer中。

va_end:是一个宏,用于结束使用 va_start 和 va_arg 宏定义的可变参数列表。它的作用是清理 va_list 类型变量,以便该变量可以被再次使用。

	// 打印日志信息
    printf("%s %s\n", timebuffer, logBuffer);

    // //往文件打印
	FILE *fp = fopen(LOGFILE, "a");
    fprintf(fp, "%s %s\n", timebuffer, logBuffer);
    fclose(fp);

打印日志内容有两种方式,一种是向显示器上打,一种是往文件里写。通过将timebufferlogBuffer把时间和内容组合到一起,就是一条完整的日志信息了。

测试

简单的测试,将日志信息打印在显示器上:

#include "log.hpp"

int main()
{

    printf("ssd\n");

    int n=1;
    logMessage(DEBUG, "测试日志信息输出%d", n);
    return 0;
}

四、完整代码

#pragma once
#include <iostream>
#include <cstring>
#include <time.h>
#include <stdarg.h>
#include <cstdio>

//日志级别
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4

const char* gLevelMap[]={
    "DEBUG",
    "NORMAL",
    "WARNING",
    "ERROR",
    "FATAL"
};

// 日志打印 文件名
#define LOGFILE "./LOG.log"

void logMessage(int level, const char* format, ...)
{
    char timebuffer[1024];
    // 日志时间
    time_t timestamp = time(nullptr);
    tm* ltm = localtime(&timestamp);
    int year = ltm->tm_year+1900;
    int month = ltm->tm_mon+1;
    int day = ltm->tm_mday;
    int hour = ltm->tm_hour;
    int min = ltm->tm_min;
    int second = ltm->tm_sec;

    snprintf(timebuffer, sizeof timebuffer, "[%s][%04d-%02d-%02d %02d:%02d:%02d]",
                gLevelMap[level], year, month, day, hour, min, second);

    // 日志内容
    char logBuffer[1024];
    va_list args;  // 初始化参数信息
    va_start(args, format);  // 初始化参数列表
    vsnprintf(logBuffer, sizeof logBuffer, format, args);  //日志内容存入logBuffe中
    va_end(args);

    // 打印日志信息
    printf("%s %s\n", timebuffer, logBuffer);

    // //往文件打印
    // FILE *fp = fopen(LOGFILE, "a");
    // fprintf(fp, "%s %s\n", timebuffer, logBuffer);
    // fclose(fp);
}   

 

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来为您添加日志轮换功能。首先,我需要在 `Logger` 类中添加一个 `rotating_file_sink` 对象,并将其作为 sink 传递给 logger 对象。然后,我需要设置日志文件的最大大小和最大文件数,以控制日志轮换的行为。 以下是更新后的 `Logger` 类代码: ```c++ #include <spdlog/spdlog.h> #include <spdlog/sinks/basic_file_sink.h> #include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/rotating_file_sink.h> class Logger { public: Logger() { // 创建控制台输出 sink auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); // 创建文件输出 sink auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("log.txt", 1024 * 1024 * 50, 3); // 创建 logger logger_ = std::make_shared<spdlog::logger>("my_logger", spdlog::sinks_init_list{console_sink, file_sink}); // 设置日志级别 logger_->set_level(spdlog::level::trace); } void log(const char* file, int line, spdlog::level::level_enum level, const std::string& msg) { // 根据日志级别选择不同的颜色 switch (level) { case spdlog::level::trace: logger_->trace("{}:{} - {}", file, line, msg); break; case spdlog::level::debug: logger_->debug("{}:{} - {}", file, line, msg); break; case spdlog::level::info: logger_->info("{}:{} - {}", file, line, msg); break; case spdlog::level::warn: logger_->warn("{}:{} - {}", file, line, msg); break; case spdlog::level::err: logger_->error("{}:{} - {}", file, line, msg); break; case spdlog::level::critical: logger_->critical("{}:{} - {}", file, line, msg); break; default: break; } } private: std::shared_ptr<spdlog::logger> logger_; }; int main() { // 初始化 logger Logger logger; // 打印日志 for (int i = 0; i < 100000; ++i) { logger.log(__FILE__, __LINE__, spdlog::level::info, "Hello, world!"); } return 0; } ``` 在上述示例中,我使用 `rotating_file_sink_mt` 创建了一个支持日志轮换的文件输出 sink。其中,第一个参数是日志文件名,第二个参数是单个日志文件的最大大小(这里设置为 50MB),第三个参数是最大的日志文件数(这里设置为 3)。这意味着,当一个日志文件达到 50MB 时,会自动创建一个新的日志文件,最多会保留 3 个日志文件(包括当前日志文件)。如果您需要自定义日志轮换行为,可以参考 spdlog 提供的其他 sink,并根据自己的需求进行修改。 现在,在程序运行时,如果日志文件大小达到 50MB,或者超过 3 个日志文件时,会自动按照一定的规则进行日志轮换,保证日志文件不会过大,也不会占用过多的磁盘空间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值