当前位置: 首页 > news >正文

【高并发服务器】六、日志宏的实现 - 指南

文章目录

  • 日志宏封装

在这里插入图片描述

日志宏封装

​ 目的就是为了实现一些宏函数,让它们辅助我们进行日志信息的打印,我们想要的格式像下面这样子:

[2023/6/21 21:28:30 main.c:28] 文件打开失败

​ 也就是这样子:

[时间 出现错误的文件:行号] 错误信息

​ 对于出现错误的文件和行号来说其实不难,因为 C 语言本身就给我们提供了对应的宏,分别是 __FILE____LINE__

​ 而对于时间来说,这得用几个函数来帮忙:

// 获取系统时间戳
time_t time(NULL);
// 通过系统时间戳参数来获取本地时间的结构体tm
struct tm* localtime(time_t* t);
// 将结构体tm通过format形式根据max大小存放到buf中去
char* strftime(char* buf, int max, char* format, struct tm* tm);
// 通过format格式将可变参数写到文件指针所指的文件中去
int fprintf(FILE* fp, char* format, ...);

​ 我们的日志宏和服务器头文件 server.hpp 放在一起。因为我们要用到宏,那么就得用 <cstdio> 头文件,所以要将其包含进来。

​ 接着我们写一个简单宏定义:

#ifndef __MY_LOG_H__
#define __MY_LOG_H__
#include <cstdio>#define LOG(format, ...) fprintf(stdout, "[%s:%d] " format, __FILE__, __LINE__, __VA_ARGS__)#endif

​ 这里的 LOG 就是一个日志宏,它的第一个参数是一个格式化字符串 format,用于指定输出信息的格式,注意 format 只是我们在宏定义常常起的参数名,并不是一个关键字或预定义标识符

LOG 的第二个参数 ... 是 可变参数列表的一种表示,而 fprintf 函数中的 __VA_ARGS__C 语言中的一个预处理器宏,也是用于表示一个可变参数列表,当我们调用 LOG 宏的时候,... 中的多个可变参数都会在预处理阶段传递给 __VA_ARGS__

​ 比如说下面的例子:

LOG("%s: %d", "liren", 10);
最后在预处理阶段会被替换为如下形式:
fprintf(stdout, "[%s:%d] %s: %d", __FILE, __LINE, "liren", 10);

​ 至于具体 __VA_ARGS__ ... 的区别可以看下面这段理解!

__VA_ARGS__ ... 的区别:

​ 它们用来表示可变参数列表的语法元素,但它们的使用方式和作用范围有所不同。

... 是 C99 标准引入的 语法,用于表示函数或宏定义中的可变参数列表。在函数定义或宏定义中,... 必须放在参数列表的最后一个位置,用来表示后面还有一些可变数量的参数。例如,下面是一个使用 ... 表示可变参数的函数定义:

void my_printf(const char* format, ...);

​ 在函数调用时,可以使用类似于 printf 函数的方式传递可变数量的参数,例如:

my_printf("The value of x is %d\n", x);
my_printf("Hello, %s!\n", name);

​ 在这种情况下,编译器会将可变参数列表转换为一个类型为 va_list 的对象,然后可以使用 stdarg.h 中定义的函数和宏如 va_start()vsnprintf() 等来访问和处理这些参数。

__VA_ARGS__ 则是一个预处理器宏,用于表示宏定义中的可变参数列表。在宏定义中,__VA_ARGS__ 可以出现在参数列表的任意位置,用来表示可变数量的参数。例如,下面是一个使用 __VA_ARGS__ 表示可变参数的宏定义:

#define LOG(format, ...) printf(format, __VA_ARGS__)

​ 在这种情况下,预处理器会将 __VA_ARGS__ 展开为一系列逗号分隔的参数,然后将它们传递给宏定义中的 printf 函数进行输出。但是一般防止不传可变列表参数报错,我们会 __VA_ARGS__ 前面加上 ## 表示展开后的可变参数列表,如下所示:

#define LOG(format, ...) printf(format, ##__VA_ARGS__)

​ 并且 上面的 printf 中使用的时候是不能用 ... 的,只能使用 __VA_ARGS__ 来表示接收到的可变参数列表

​ 总的来说,...__VA_ARGS__ 都是用来表示可变参数列表的语法元素,但是 前者用于函数定义和函数调用中后者只能用于宏定义中。它们的作用和使用方式有所不同,但都可以方便地处理可变数量的参数。

​ 接下来我们再来加入时间等信息,让宏日志更完善一点!一般我们如果想要在宏定义的时候写多行代码,都会使用 do while(0) 语句来配合,涉及到换行的话要使用反斜杠 \ 在语句最后面,下面给出结合打印时间的代码的完善日志宏:

#include <cstdio>#include <time.h>#define LOG(format, ...) do{\char timebuffer[128];\time_t timestamp = time(NULL);\struct tm* timeinfo = localtime(&timestamp);\strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", timeinfo);\fprintf(stdout, "[%s %s:%d] " format, timebuffer, __FILE__, __LINE__, __VA_ARGS__);\}while(0)

​ 调用的结果如下:

LOG("%s-%d\n", "liren", 100);
结果:
[2023-06-21 23:17:03 gobang.cc:5] liren-100

​ 这样子就结束了吗❓❓❓

​ 当然不是,因为还有 bug,因为如果我们 使用 LOG 宏的时候不传可变参数的话,那么预处理时候就会报错,如下所示:

LOG("liren");
编译时候会报错:
[liren@VM-8-7-centos source]$ make
g++ -ogobang gobang.cc logger.hpp
In file included from gobang.cc:1:0:
gobang.cc: In function ‘int main():
logger.hpp:11:86: error: expected primary-expression before ‘)’ token
intf(stdout, "[%s %s:%d] " format, timebuffer, __FILE__, __LINE__, __VA_ARGS__);\
^
gobang.cc:5:5: note: in expansion of macro ‘LOG’
LOG("liren");
^~~
make: *** [makefile:2: gobang] Error 1

​ 解决这个问题很简单,只需要使用 ##__VA_ARGS__ 来表示展开后的参数列表。这个语法中的 ## 表示将 __VA_ARGS__ 前面的逗号去掉,避免在展开后出现语法错误!需要注意的是,## 的使用在不同的编译器和平台上可能有所不同。在使用 ## 时需要注意平台兼容性和语法规则。

​ 所以修改完代码如下:

#include <cstdio>#include <time.h>#define LOG(format, ...) do{\char timebuffer[128] = {0};\time_t timestamp = time(NULL);\struct tm* timeinfo = localtime(&timestamp);\strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", timeinfo);\fprintf(stdout, "[%s %s:%d] " format "\n", timebuffer, __FILE__, __LINE__, ##__VA_ARGS__);\}while(0)

​ 这样子就结束了吗❓❓❓还是没结束,因为我们到时候项目中会打印很多日志,如果我们不对日志分等级的话,那么可能会导致日志比较乱,下面我们定义一些等级的宏:

#define INF 0    // 提示型等级
#define DEBUG 1  // 调试型等级
#define ERROR 2  // 错误型等级
#define DEFAULT_LOG_LEVEL DEBUG  // 默认的日志等级

​ 然后我们再将这些等级宏和我们刚才写的日志宏封装起来:

#define ILOG(format, ...) LOG(INF, format, ##__VA_ARGS__)
#define DLOG(format, ...) LOG(DEBUG, format, ##__VA_ARGS__)
#define ELOG(format, ...) LOG(ERROR, format, ##__VA_ARGS__)

​ 此时看到 LOG 宏的第一个参数传入的是对应的等级,那我们想要去改一下原来的日志宏的参数,多加一个参数 level,并且我们判断一下当前的等级也就是 DEFAULT_LOG_LEVEL是否小于传入进来的等级,是的话我们就不需要去打印,因为我们此时程序说明不需要上升到这种级别的日志打印!

#define LOG(level, format, ...) do{\
if(DEFAULT_LOG_LEVEL > level) break\
char timebuffer[128] = {0};\
time_t timestamp = time(NULL);\
struct tm* timeinfo = localtime(&timestamp);\
strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", timeinfo);\
fprintf(stdout, "[%s %s:%d] " format "\n", timebuffer, __FILE__, __LINE__, ##__VA_ARGS__);\
}while(0)

​ 所以完整的代码是这样子的:

#include <cstdio>#include <time.h>#define INF 0    // 提示型等级#define DEBUG 1  // 调试型等级#define ERROR 2  // 错误型等级#define DEFAULT_LOG_LEVEL DEBUG  // 默认的日志等级#define LOG(level, format, ...) do{\if(DEFAULT_LOG_LEVEL < level) break;\char timebuffer[128] = {0};\time_t timestamp = time(NULL);\struct tm* timeinfo = localtime(&timestamp);\strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", timeinfo);\fprintf(stdout, "[%s %s:%d] " format "\n", timebuffer, __FILE__, __LINE__, ##__VA_ARGS__);\}while(0)// 将等级和日志打印封装起来#define ILOG(format, ...) LOG(INF, format, ##__VA_ARGS__)#define DLOG(format, ...) LOG(DEBUG, format, ##__VA_ARGS__)#define ELOG(format, ...) LOG(ERROR, format, ##__VA_ARGS__)

​ 下面我们测试一下:

ILOG("this is INF");
DLOG("this is DEBUG");
ELOG("this is ERROR");
// 运行结果:
[liren@VM-8-7-centos source]$ ./gobang
[2023-06-21 23:41:55 gobang.cc:5] this is INF
[2023-06-21 23:41:55 gobang.cc:6] this is DEBUG

在这里插入图片描述

http://www.gsyq.cn/news/50265.html

相关文章:

  • 2025年白色珍珠棉卷材生产商权威推荐榜单:珍珠棉袋子/EPE珍珠棉板材/白色珍珠棉板材源头厂家精选
  • 2025年热门的防腐耐磨涂层厂家最新权威实力榜
  • 零售场景下的数智店商:解决盗损问题,化解隐性成本痛点
  • 2025年11月北京高考辅导机构权威榜:五强对比与客观排行一览
  • 2025年热门的地面瓷砖胶厂家推荐及选择建议
  • 2025年聚合氯化铝直销厂家权威推荐榜:聚合氯化铝喷雾/30聚合氯化铝/碱式聚合氯化铝源头厂家精选
  • 2025年知名的覆膜吨袋厂家最新TOP实力排行
  • 2025年11月载冷剂厂家评测榜:五强资质与服务对比
  • 2025年11月北京全屋定制厂家评测榜:鲸准家居领衔五强对比
  • 一对一视频app开发,基于Redis+Lua实现分布式限流 - 云豹科技
  • 获取cookie的方法不止一种
  • 2025年11月牛初乳品牌口碑榜:五强实测评价与排行指南
  • 完整教程:Vue3与Cesium:轻量版3D地理可视化实践
  • 如何禁用XFCE的屏幕锁
  • 2025年11月消音室厂家推荐榜:五强对比与性能全解析
  • 2025年11月消音室厂家推荐排名榜:五强性能数据与认证资质一览
  • 2025年隔音室厂家联系方式推荐:精选推荐与使用指南
  • Why are monarchies so good
  • 完整教程:算法---位运算
  • 钢管拉弯加工工厂、管桁架加工生产工厂推荐、相贯线切割加工公司排名、钢管弯弧加工厂家、钢管喷漆加工厂家排行榜、钢构件加工工厂哪家好
  • 锅炉风帽厂家/不锈钢锅炉风帽工厂/高温锅炉风帽源头工厂排名/燃煤锅炉风帽厂家推荐/耐热锅炉风帽公司怎么选择/铸铁风帽公司排名/聊城不锈钢风帽厂家排行山东亿诚达金属材料
  • 2025年重庆24小时电脑回收公司权威推荐榜单:闲置电脑回收/i7电脑回收/高价电脑回收源头公司精选
  • 2025年精密仪器搬运公司口碑排行
  • 考研408--计算机网络--day1-概念组成功能三种交换技术分类 - 实践
  • 2025 最新连接器厂家推荐排行榜:涵盖 MDC/ZMDM/J30J/HJ30J 连接器等全系列产品,助力企业精准选型J63A微矩形电连接器,HJ30J系列⾼速传输微矩形电连接器厂家推荐
  • 2025年比较好的工业设备输送线厂家推荐及采购参考
  • 2025年靠谱的自动化输送线厂家推荐及采购指南
  • 当AI遇见塔罗:现代生活中的自我探索新方式
  • 2025年评价高的密集型母线槽厂家最新推荐权威榜
  • 2025年口碑好的衬氟安全阀最新TOP厂家排名