马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
欢迎来到 破晓的历程的 博客
⛺️不负韶光,不负己✈️
弁言
日志在程序设计中扮演着至关重要的角色,它不仅是程序运行情况的记载者,照旧题目诊断、性能优化、安全审计以及用户行为分析的重要工具。本篇博客我们就设计一个日志函数,并在过程中学习一些知识。
日志内容
一个完整的日志信息应该包括:日志品级、时间、题目描述、文件、行数等等。
日志品级
日志级别是对日志信息进行分类的一种方式。通过为日志信息分配差别的级别,开发者可以更精细地控制日志的生成和输出,从而在差别的场景下获取最有代价的信息。常见的日志级别包括:
- 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);
- }
复制代码 但是这个日志函数太复杂,有没有简单一点的?
有而且一个宏就可以搞定。
详细请看这篇博客:日志函数的简单方法
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |