自定义日志打印功能--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);
}   

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于碰撞试验

主要参数&#xff1a; 冲击与碰撞试验的主要参数及调整方法 - 百度文库 碰撞试验的技术指标包括&#xff1a;峰值加速度、脉冲持续时间、速度变化量&#xff08;半正弦波&#xff09;、每方向碰撞次数。 加速度&#xff1a;冲击的强度&#xff0c;单位为g&#xff1b;一般为3…

Zygote 进程启动过程

首语 在Android系统中&#xff0c;DVM(Dalvik虚拟机)和ART、应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程创建的&#xff0c;也可以将其称之为孵化器&#xff0c;它通过fork(复制进程)的形式来创建应用程序进程和SystemServer进程。 Zygote进程是在…

记录一次chatGPT人机协同实战辅助科研——根据词库自动进行情感分析

有一个Excel中的一列&#xff0c;读取文本判断文本包含积极情感词.txt和消极情感词.txt的个数&#xff0c;分别生成两列统计数据 请将 ‘your_file.xlsx’ 替换为你的Excel文件名&#xff0c;Your Text Column’替换为包含文本的列名。 这个程序首先读取了积极和消极情感词&…

(第68天)DBCA 克隆 PDB

介绍 在前面课程我们讲过使用 DBCA 创建数据库以及搭建 DataGuard 等功能,在多租户这章节,要讲下如何使用 DBCA 克隆 PDB。 18C 开始支持使用 DBCA 在本地 CDB 中克隆 PDB19C 升级支持使用 DBCA 克隆 PDB 到远端 CDB 中19C 升级支持使用 DBCA 重定向迁移 PDB 到远端 CDB 中本…

2023/12/12作业

思维导图 作业&#xff1a; 成果图 代码 #include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { speechernew QTextToSpeech(this); ui->setupUi(this); //一直获取当前时间 idst…

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

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

AI全栈大模型工程师(二十八)如何做好算法备案

互联网信息服务算法 什么情况下要备案&#xff1f; 对于B2B业务&#xff0c;不需要备案。 但在B2C领域&#xff0c;一切要视具体情况而定。 如果我们自主训练大型模型&#xff0c;这是必要的。 但如果是基于第三方模型提供的服务&#xff0c;建议选择那些已获得备案并且具有较大…

DevOps - Spug 自动化运维平台

关于Spug 官网&#xff1a;https://spug.cc/ Spug&#xff1a;麻雀&#xff0c;麻雀虽小&#xff0c;五脏俱全。 Spug是面向中小型企业设计的轻量级无Agent的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件在线上传下载、应用发布部署、在线任…

[Angular] 笔记1:开发设置 , 双向绑定

1 设置开发环境 1.1 安装 node 下载 node&#xff0c;因为要使用 npm 工具&#xff0c;教程中使用 Angualr 14, 最新版 node 20 用不了&#xff0c;安装 node 16 就可以。 1.2 安装 Angular CLI Angular CLI 是用于创建 Angular 工程的工具集&#xff0c;使用如下命令&…

redis的深度理解

上篇博客我们说到了redis的基本概念和基本操作&#xff0c;本篇我们就更深入去了解一些redis的操作和概念&#xff0c;我们就从red的主从同步、redis哨兵模式和redis集群三个方面来了解redis数据库 一、主从同步 像MySQL一样&#xff0c;redis是支持主从同步的&#xff0c;而…

面试 JVM 八股文五问五答第二期

面试 JVM 八股文五问五答第二期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1.JVM运行时数据区有几部分?&#xff08;JVM内存布局&#xff09;虚拟机栈和本地方…

nodejs微信小程序+python+PHP社区居民信息管理及数据分析系统-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

SQL数列

SQL数列 1、数列概述2、SQL数列2.1、简单递增序列2.2、等差数列2.3、等比数列3、SQL数列的应用3.1、连续问题3.2、多维分析1、数列概述 数列是最常见的数据形式之一,实际数据开发场景中遇到的基本都是有限数列。常见的数列例如:简单递增序列、等差数列、等比数列等 如何充分…

汽车IVI中控开发入门及进阶(十一):ALSA音频

前言 汽车中控也被称为车机、车载多媒体、车载娱乐等,其中音频视频是非常重要的部分,音频比如播放各种格式的音乐文件、播放蓝牙接口的音乐、播放U盘或TF卡中的音频文件,如果有视频文件也可以放出音频,看起来很简单,在windows下音乐播放器很多,直接打开文件就能播放各…

记录 | linux安装Manim

linux 安装 Manim sudo apt update sudo apt install build-essential python3-dev libcairo2-dev libpango1.0-dev ffmpeg sudo apt install xdg-utilsconda create manim_py39 python3.9 conda activate manim_py39pip install manim安装好环境后来测试一个例程&#xff0c;…

多维时序 | MATLAB实现BWO-CNN-BiGRU-Multihead-Attention多头注意力机制多变量时间序列预测

多维时序 | MATLAB实现BWO-CNN-BiGRU-Multihead-Attention多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现BWO-CNN-BiGRU-Multihead-Attention多头注意力机制多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现BWO-CNN-B…

向ChatGPT提特殊问题,可提取原始训练数据!

随着ChatGPT等模型的参数越来越大&#xff0c;预训练数据也呈指数级增长。谷歌DeepMind、华盛顿大学、康奈尔大学等研究人员发现,无论是开源还是闭源模型&#xff0c;在训练过程中皆能记住一定数量的原始训练数据样本。 如果使用特定的恶意攻击&#xff0c;便能轻松地从模型中…

Pytorch中Group Normalization的具体实现

Group Normalization (GN) 是一种用于深度神经网络中的归一化方法&#xff0c;它将每个样本划分为小组&#xff0c;并在每个小组内进行标准化。与批归一化&#xff08;Batch Normalization&#xff09;不同&#xff0c;Group Normalization 不依赖于小批量数据&#xff0c;因此…

论文阅读——ScanQA

ScanQA: 3D Question Answering for Spatial Scene Understanding 输入&#xff1a;点云P和问题Q&#xff0c;输出&#xff1a;答案A 点云p由三维坐标点组成。本文模型使用额外的点云特征&#xff1a;点云高度、颜色、法线和多视图图像特征&#xff0c;这些特征将 2D 外观特征投…

SD-WAN架构:优化连接以提升性能

SD-WAN架构主要分为三种类型&#xff0c;分别为本地架构、支持云的架构、支持云的骨干架构。每一种架构都基于它们利用广域网&#xff08;WAN&#xff09;的方式而有其独特的优势。本文将对三种SD-WAN架构进行简要介绍。 SD-WAN本地架构 SD-WAN本地架构是在现场使用SD-WAN盒或…