【Linux】日志函数

news2024/12/24 20:23:56

欢迎来到 破晓的历程的 博客

⛺️不负时光,不负己✈️

文章目录

    • 引言
    • 日志内容
      • 日志等级
      • 日志函数的编写
      • 函数原型
      • 参数说明
      • 功能描述
      • 使用场景
      • 示例代码

引言

日志在程序设计中扮演着至关重要的角色,它不仅是程序运行情况的记录者,还是问题诊断、性能优化、安全审计以及用户行为分析的重要工具。本篇博客我们就设计一个日志函数,并在过程中学习一些知识。

日志内容

一个完整的日志信息应该包括:日志等级、时间、问题描述、文件、行数等等。

日志等级

日志级别是对日志信息进行分类的一种方式。通过为日志信息分配不同的级别,开发者可以更精细地控制日志的生成和输出,从而在不同的场景下获取最有价值的信息。常见的日志级别包括:

  • DEBUG:调试级别,用于输出详细的调试信息,通常在开发和测试阶段使用。
  • NORMAL:信息级别,用于输出一般性的信息,表示系统正常运行。
  • WARNING:警告级别,用于输出可能的问题或异常情况,但不会影响系统的正常运行。
  • ERROR:错误级别,用于输出严重的错误信息,可能会影响系统的正常运行。
  • FATEL:严重错误级别,用于输出非常严重的错误信息,通常会导致系统崩溃或无法继续运行。
#define DEBUG 1
#define NORMAL 2
#define WARNING 3
#define ERROR 4
#define FATEL 5

日志函数的编写

完整代码为

#include <iostream>
#include <unistd.h>
#include<sys/types.h>
#include <stdarg.h>
#define DEBUG 1
#define NORMAL 2
#define WARNING 3
#define ERROR 4
#define FATEL 5

char *to_level(int level)
{
    switch(level)  
    {
        case 1:
            return "DEBUG";
        break;
        case 2:
            return "NORMAL";
        break;
        case 3:
            return "WARNING";
        break;
        case 4:
            return "ERROR";
        break;
        case 5:
            return "FATEL";
        break;
        default:
            return nullptr;
        break;

    }
}
void LogMessge(int level,const char *format,...)//"..."代表的是可变参数
{
    //先将固定的部分格式化到logprefix中
    char logprefix[1024];
    snprintf(logprefix,sizeof(logprefix),"[%s][%ld][%d]",to_level(level),time(nullptr),getpid());

    //然后将部分格式化到logcontent中
    char logcontent[1024];
    va_list argv;
    va_start(argv,format);
    vsnprintf(logcontent,sizeof(logcontent),format,argv);

    std::cout<<logprefix<<logcontent<<std::endl;
}

接下来,我们对代码讲解一下:
可变参数列表
“…”代表是可变参数列表。
可变参数列表,顾名思义,就是函数的参数个数不是固定的,可以根据需要传入任意数量的参数(但通常至少需要一个固定参数来指示后续可变参数的类型或数量)。
例如:

   int printf(const char *format, ...);

特性:

  • 至少需要一个固定参数,这是为了函数能够识别和处理后续的可变参数。
  • 可变参数部分在声明时使用省略号(…)来表示。
  • 可变参数的类型和数量在编译时无法确定,通常需要在运行时通过特定机制来访问和处理

如何实现可变参数列表呢?

在C语言中,可变参数列表的实现依赖于stdarg.h头文件中的宏和类型定义。这些宏和类型允许开发者在运行时访问和处理可变参数。

  • va_list:这是一个类型定义,用于声明一个指向可变参数列表的指针。
  • va_start:这是一个宏,用于初始化va_list类型的变量,以便它可以指向函数的第一个可变参数。
  • va_arg:这是一个宏,用于从va_list指向的位置提取下一个可变参数,并更新va_list的指向,以便下一次提取。
  • va_end:这是一个宏,用于结束对可变参数列表的访问,并将va_list变量设置为无效状态(通常是将其设置为NULL)。

vsnprintf

vsnprintf函数是C语言标准库中的一个函数,它的作用是将格式化的数据写入一个字符串缓冲区中,同时允许指定缓冲区的大小,以防止缓冲区溢出。这个函数在C99及以后的版本中得到了广泛的支持,也在C++11及以后的版本中可用。

函数原型

vsnprintf函数的原型如下:

int vsnprintf(char *str, size_t size, const char *format, va_list ap);

参数说明

  • str:指向字符缓冲区的指针,用于存储格式化后的字符串。
  • size:指定缓冲区的大小,即最多可以存储多少个字符(包括终止的空字符’\0’)。这有助于防止缓冲区溢出。
  • format:格式字符串,用于指定后续参数如何被格式化和插入到输出字符串中。这个字符串可以包含普通的字符和格式说明符(如%d%s等)。
  • ap:一个va_list类型的参数,它代表了一个可变参数列表。这个列表包含了要被格式化的实际参数。

功能描述

vsnprintf函数会读取格式字符串format,并根据格式说明符从可变参数列表ap中检索相应的参数进行格式化。格式化后的字符串(或其中的一部分,如果它太长而无法完全适应缓冲区)会被写入到str指向的缓冲区中。如果生成的字符串长度小于size,则会在字符串末尾添加一个空字符’\0’作为结束符。如果生成的字符串长度大于或等于size,则只将size-1个字符写入缓冲区(不包括空字符),并且不会在缓冲区末尾添加空字符。此外,函数会返回一个整数,表示如果不考虑缓冲区大小限制,格式化后的字符串应该包含的字符数(不包括空字符)。

使用场景

vsnprintf函数特别适用于那些需要严格控制输出缓冲区大小的情况,比如嵌入式系统编程、网络编程等。在这些场景下,缓冲区溢出可能会导致严重的后果,如程序崩溃、数据损坏或安全漏洞。通过使用vsnprintf函数,开发者可以确保即使在最坏的情况下,也不会发生缓冲区溢出。

示例代码

以下是一个使用vsnprintf函数的示例代码:

#include <stdio.h>
#include <stdarg.h>

void MyPrintF(char *buffer, size_t bufferSize, const char *format, ...) {
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, bufferSize, format, args);
    va_end(args);
    // 注意:如果bufferSize足够大,并且需要字符串以'\0'结尾,
    // 则可能需要在调用vsnprintf后手动添加'\0',
    // 但在这个例子中,由于我们使用了bufferSize作为限制,
    // 如果字符串被截断,则不需要添加'\0'(因为buffer的其余部分未定义)。
    // 如果需要确保buffer以'\0'结尾,并且确信bufferSize足够大,
    // 可以在调用vsnprintf之前将buffer[0]设置为'\0',
    // 或者在调用后(如果确定没有溢出)将buffer[bufferSize-1]设置为'\0'。
    // 但在这个简单的示例中,我们假设调用者会正确处理buffer。
    printf("%s\n", buffer);
}

int main() {
    char buffer[100];
    MyPrintF(buffer, sizeof(buffer), "Hello, %s! You are %d years old.", "Alice", 30);
    return 0;
}

在这个示例中,我们定义了一个MyPrintF函数,它接受一个缓冲区、缓冲区的大小、一个格式字符串和可变数量的参数。然后,它使用vsnprintf函数将这些参数格式化并写入缓冲区,并通过printf函数打印出来。注意,在实际应用中,我们可能需要更仔细地处理缓冲区的大小和终止的空字符。


接下来,我们模拟实现printf函数

#include<stdarg.h>
#include<cstdio>
#include<iostream>
void my_printf(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    char buffer[1024];
    vsnprintf(buffer, sizeof(buffer), format, args);
    std::cout << buffer << std::endl;
}
int main()
{
    int cnt = 12;
    my_printf("%d", cnt);
}


但是这个日志函数太复杂,有没有简单一点的?

有而且一个宏就可以搞定。
具体请看这篇博客:日志函数的简单方法

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

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

相关文章

【机器学习】智驭未来:机器学习如何重塑现代城市管理新生态

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f50d;1. 引言&#xff1a;迈向智能城市的新时代&#x1f4d2;2. 智驭交通&#xff1a;机器学习在智能交通管理中的应用&#x1…

仿Muduo库实现高并发服务器——LoopThreadPool模块

这个模块需要具备那些基础知识。 线程创建相关操作&#xff0c;锁&#xff0c;条件变量。 设置线程数量&#xff1a; _thread_count 是线程池中&#xff0c;记录线程数量的成员。 创建线程池&#xff1a; 上图就是线程池的创建&#xff0c;将线程与EventLoop对象 通过数组下…

关于嘉立创eda中同一个项目下多个原理图是否独立

嘉立创项目底下&#xff0c;如果你新建了多张原理图&#xff0c;如下 我发现&#xff0c;多张原理图是互相连接的&#xff0c;所以命名是不能重复的 多页原理图 | 嘉立创EDA标准版用户指南https://docs.lceda.cn/cn/Schematic/Multi-Sheet/index.html 上面是嘉立创原文介绍 综…

豆瓣评分7.9!世界级讲师耗时5年整理出的Python学习手册!

Python是一门流行的开源编程语言&#xff0c;广泛用于各个领域的独立程序与脚本化应用中。它不仅免费、可移植、功能强大&#xff0c;同时相对简单&#xff0c;而且使用起来充满乐趣。从软件业界的任意一角到来的程序员&#xff0c;都会发现Python着眼于开发者的生产效率以及软…

编程仙尊——深入理解指针(2)

目录 4.const修饰指针 4.1const修饰变量 5.指针运算 5.1指针-整数 5.2指针-指针 5.3指针的关系运算 6.assert断言 4.const修饰指针 4.1const修饰变量 在编程中&#xff0c;为了防止代码在运行过程中变量的内容意外改变&#xff0c;可以使用const函数&#xff0c;对变量…

介绍python的回归模型原理知识

一.回归 1.什么是回归 回归&#xff08;Regression&#xff09;最早是英国生物统计学家高尔顿和他的学生皮尔逊在研究父母和子女的身高遗传特性时提出的。1855年&#xff0c;他们在《遗传的身高向平均数方向的回归》中这样描述“子女的身高趋向于高于父母的身高的平均值&…

Linux云计算 |【第二阶段】SHELL-DAY2

主要内容&#xff1a; 条件测试&#xff08;字符串比较、整数比较、文件状态&#xff09;、IF选择结构&#xff08;单分支、双分支、多分支&#xff09;、For循环结构、While循环结构 一、表达式比较评估 test 命令是 Unix 和 Linux 系统中用于评估条件表达式的命令。它通常用…

小乌龟运动控制-1 小乌龟划圆圈

目录 第一章 小乌龟划圆圈 第二章 小乌龟走方形 文章目录 目录前言一、准备工作步骤一&#xff1a;创建ROS工作空间步骤二&#xff1a;创建ROS包和节点步骤三&#xff1a;编写Python代码步骤四&#xff1a;运行ROS节点总结 前言 本教程将教会你如何使用Python编写ROS小海龟节…

【SpringCloud】(一文通)优雅实现远程调用-OpenFeign

目 录 一. RestTemplate存在问题二. OpenFeign介绍三. 快速上手3.1 引入依赖3.2 添加注解3.3 编写 OpenFeign 的客户端3.4 远程调用3.5 测试 四. OpenFeign 参数传递4.1 传递单个参数4.2 传递多个参数4.3 传递对象4.4 传递JSON 五. 最佳实践5.1 Feign 继承方式5.1.1 创建⼀个Mo…

马克思发生器有什么用_马克思发生器工作原理

马克思发生器&#xff08;Marx Generator&#xff09;是一种电气装置&#xff0c;用于产生高压脉冲电压。它由多个电容器组成&#xff0c;这些电容器依次连接在一系列开关之后。首先&#xff0c;每个电容器被并联充电至较低的电压。然后&#xff0c;这些电容器被开关依次串联&a…

C++过生日(我给我自己做的生日礼物)

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &#x1f680;感谢大家点赞&#x1f44d;收藏⭐评论✍ 引言…

电源自动测试系统:测试柜的组成与功能

为了提高电源测试的效率和安全性&#xff0c;电源自动化测试柜是电源ATE自动测试系统的重要设备&#xff0c;不仅对示波器、万用表等测试仪器起保护作用&#xff0c;更是在测试过程中降低了安全风险&#xff0c;方便了电源产品的自动化测试。 电源自动测试系统机柜 电源自动化测…

C++初学(15补充)

15.1、嵌套循环和二维数组 下面讨论如何使用嵌套for循环来处理二维数组。到目前为止&#xff0c;我们一直学的是一维数组&#xff0c;因为每一个数组都可以看作是一行数据。二维数组更像是一个表格——既有数据行也有数据列。C并没有提供二维数组类型&#xff0c;但是用户可以…

电池的入门

目录 化学电池主要参数电池种类常用电池 物理电池太阳能电池 化学电池 主要参数 1.容量 2.标称电压 3.内阻 4.充电终止电压 5.放点终止电压 电池种类 按能否充电分&#xff1a; 原电池&#xff08;Primary Cell&#xff09;&#xff1a;只能放电不能充电的电池&#xff0c…

FastGPT如何增减用户

背景 开源版本的FastGPT默认只有一个超级用户root&#xff0c;为了更好地管理应用和知识库&#xff0c;可以通过操作MongoDB数据库来增加新的用户和团队。 所需环境 已安装并运行的FastGPT实例MongoDB客户端工具&#xff08;如Mongo Shell或Robo 3T等&#xff09; 操作步骤…

一文带你了解React Hooks

目录 一、useState 二、useRef 三、useEffect 四、自定义Hook 五、Hooks使用规则 Hooks原意是“挂钩”&#xff0c;指将类组件中的部分功能直接可以挂钩到函数组件中&#xff0c;例如state、生命周期方法、副作用等功能。 为什么使用Hooks&#xff1f; 封装代码&#xff…

Harmony鸿蒙应用开发:解决Web组件加载本地资源跨域

鸿蒙开发文档中有一节 加载本地页面 提到了可以通过 $rawfile 方法加载本地 HTML 网页&#xff1a; Index.ets 1Web({ src: $rawfile("local.html"), controller: this.webviewController })但是如果在 local.html 中需要引用一些静态资源&#xff0c;例如图片、JS、…

STM32——TIM定时器的输入捕获功能

一、什么是输出比较与输入捕获&#xff1f; 可以看到&#xff1a; 输出比较OC是用于输出一定频率和占空比的PWM波形&#xff0c;可用于电机驱动进行调速等&#xff1b;而输入捕获IC是用于测量PWM波形的频率以及占空比等参数&#xff1b;和他们的名字相反&#xff0c;一个是比…

Datawhale AI夏令营第四期魔搭- AIGC文生图方向 task01笔记

目录 分任务1&#xff1a;跑通baseline 第一步——搭建代码环境 第二步——报名赛事 第三步——在魔搭社区创建PAI实例 分任务2&#xff1a;相关知识学习以及赛题理解 赛题理解&#xff1a; 文生图基本认识&#xff1a; 1. Diffusion Model(扩散模型) 2. LDMs&#x…

嵌入式Linux学习笔记

1.文件操作命令 2.VI编辑器的部分命令 3.Uboot命令设置环境变量 4. uboot 的顶层 Makefile的重点是“make xxx_defconfig”和“make”这两个命令 &#xff0c;分别如下&#xff1a; 5.在串口SecureCRT中利用uboot启动Linux内核的两种方式 6.Linux内核移植到开发板上也可以反…