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

news2024/11/24 19:43:00

一、介绍

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

  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);
}   

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1305907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二蛋赠书十一期:《TypeScript入门与区块链项目实战》

前言 大家好&#xff01;我是二蛋&#xff0c;一个热爱技术、乐于分享的工程师。在过去的几年里&#xff0c;我一直通过各种渠道与大家分享技术知识和经验。我深知&#xff0c;每一位技术人员都对自己的技能提升和职业发展有着热切的期待。因此&#xff0c;我非常感激大家一直…

【EI会议征稿】2024年人工智能与大模型国际学术会议(AIFM 2024)

2024年人工智能与大模型国际学术会议(AIFM 2024) 2024 International Conference on Artificial Intelligence and Foundation Model 2024年人工智能与大模型国际学术会议(AIFM 2024)将于2024年1月19-21日在南昌召开。本次会议围绕人工智能与大模型的发展应用&#xff0c;聚集…

静态路由原理与配置

文章目录 静态路由原理与配置一、路由器的工作原理1、路由概述2、路由器的工作原理 二、路由表的形成1、路由表2、路由表的形成 三、静态路由和默认路由1、静态路由的缺点2、默认路由&#xff08;是特殊的静态路由&#xff09;3、查看路由表 四、路由器转发数据包的封装过程五、…

【产品经理】产品的实现,需要做好战略规划

产品的实现需要做好产品规划&#xff0c;而产品的规划决定了产品的方向。本文从战略规划的重要性、产品定位、设计产品架构图三个方向&#xff0c;详细地为大家梳理了产品实现的前期准备。 我们知晓了如何去发掘问题&#xff0c;并找到解决方案。 可对于问题的处理&#xff0c…

2023全国职业院校技能大赛信息安全管理与评估赛项正式赛(模块二)

全国职业院校技能大赛高等职业教育组信息安全管理与评估 任务书 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xff0c;拥有完整的培训体系。团队拥有国赛选手、大厂…

《PySpark大数据分析实战》-05.PySpark库介绍

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…

亚马逊云科技:向量数据存储在生成式人工智能应用程序中的作用

生成式人工智能深受大众喜爱&#xff0c;并且由于具备回答问题、写故事、创作艺术品甚至生成代码的功能&#xff0c;推动了行业的转变&#xff0c;那么如何才能在自己的企业中充分地利用生成式人工智能等应运而生问题。许多客户已经积累了大量特定领域的数据&#xff08;财务记…

go-libp2p-example-chat学习

1.案例下载 https://github.com/libp2p/go-libp2p/tree/master/examples 2.chat案例 这段代码是一个简单的基于libp2p的P2P聊天应用程序的示例。它允许两个节点通过P2P连接进行聊天。前提是&#xff1a; 两者都有私有IP地址&#xff08;同一网络&#xff09;。至少其中一个…

【计算机视觉】Harris角点检测

角点指的是窗口延任意方向移动&#xff0c;都有很大变化量的点。 用数学公式表示为&#xff1a; 这个公式表示移动后的窗口&#xff0c;与移动前的窗口对应元素相减的平方&#xff0c;为每个像素点的权重 反映了如何移动窗口&#xff0c;以及移动窗口后的响应值 为了让 和 直…

海思越影系列3516DV500/3519DV500/3519AV200/SD3403平台的AI一体化工业相机设计思路

随着工业自动化的发展&#xff0c;生产线对机器视觉的数量要求越来越多&#xff0c;由于数量的增加&#xff0c;视觉系统占的空间也越来越大&#xff0c;给生产线的布局带来困扰。 另一方面随着视觉SOC的发展&#xff0c;越来越多的视觉SOC都逐渐带有一定的算力&#xff0c;一体…

头歌——HBase 开发:使用Java操作HBase

第1关&#xff1a;创建表 题目 任务描述 本关任务&#xff1a;使用Java代码在HBase中创建表。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.如何使用Java连接HBase数据库&#xff0c;2.如何使用Java代码在HBase中创建表。 如何使用Java连接HBase数据库…

玩转大数据15:常用的分类算法和聚类算法

前言 分类算法和聚类算法是数据挖掘和机器学习中的两种常见方法。它们的主要区别在于处理数据的方式和目标。 分类算法是在已知类别标签的数据集上训练的&#xff0c;用于预测新的数据点的类别。聚类算法则是在没有任何类别标签的情况下&#xff0c;通过分析数据点之间的相似性…

微信小程序改变checkbox大小

.weui-cell__hd {transform: scale(0.6,0.6);} <checkbox color"#447189" />

stm32 HAL库 发送接受 到了一定的字符串后就卡在.s文件中

问题介绍&#xff1a; 某个项目开发过程中&#xff0c;串口接收中断&#xff0c;开启了DMA数据传输&#xff0c;开启了DMA中断&#xff0c;开启DMA半满中断。然后程序运行的过程中&#xff0c;接收了一部分数据后就会卡在启动文件的DMA1_Ch4_7_DMA2_Ch3_5_IRQHandler 中断里。…

源码角度简单介绍LinkedList

LinkedList是一种常见的数据结构&#xff0c;但是大多数开发者并不了解其底层实现原理&#xff0c;以至于存在很多误解&#xff0c;在这篇文章中&#xff0c;将带大家一块深入剖析LinkedList的源码&#xff0c;并为你揭露它们背后的真相。首先想几个问题&#xff0c;例如&#…

抖音怎么设置自动点赞视频和评论呢?

先来看实操成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 你是否曾被抖音那令人眼花缭乱的短视频所吸引&#xff0c;却苦于无法自动点赞和评论而错过那些精彩的瞬间&#xff1f;现在&#xff0c;让我们一起揭开抖音自动点赞和评论的神秘面…

centos卸载mysql库全流程

&#xff08;1&#xff09;暂停服务 systemctl stop mysqld &#xff08;2&#xff09;查看所有的安装包&#xff0c;将其卸载 rpm -qa |grep mysql rpm -q ( or --query) options -a 查询所有安装的软件包 &#xff08;3&#xff09;使用yum卸载安装的mysql [rootbo /…

数据结构之优先级队列(堆)及top-k问题讲解

&#x1f495;"哪里会有人喜欢孤独&#xff0c;不过是不喜欢失望。"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;数据结构之优先级队列(堆) 一.优先级队列 1.概念 我们已经学习过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff…

Flask维护者:李辉

Flask维护者&#xff1a;李辉&#xff0c; 最近看b站的flask相关&#xff0c;发现了这个视频&#xff1a;[PyCon China 2023] 濒危 Flask 扩展拯救计划 - 李辉_哔哩哔哩_bilibili 李辉讲他在维护flask之余&#xff0c;开发了apiflask这个依托flask的框架。GitHub - apiflask/a…

电商淘宝爬虫API与淘宝官方开放平台API的区别以及如何选择适合自己的API接口

随着数字化时代的到来&#xff0c;数据已经成为企业竞争力的重要因素。为了获取数据&#xff0c;企业或个人常常需要使用API接口。常见的API接口包括爬虫API和官方开放平台API。本文将详细介绍这两种API接口的区别以及如何选择适合自己的API接口。 一、爬虫API与官方开放平台A…