c/c++语言的一种日志的编写办法

今日分享一下,从某源码中看到这种日志编写方式,很强。可以借鉴。

这个函数调用的日志函数是不一样的,仔细观看:
在这里插入图片描述

这几种日志输出函数,背后都调用了相同的调用。
与之对应的区别就是,函数名称的差异取决于调用函数所传入参数的不同。

PLAT_LOG_LEVEL_ERROR
PLAT_LOG_LEVEL_WARN
PLAT_LOG_LEVEL_INFO

这三个宏的不同,代表不同的日志严重等级。

#define aloge(fmt, arg...) GLOG_PRINT(PLAT_LOG_LEVEL_ERROR, fmt, ##arg)
#define alogw(fmt, arg...) GLOG_PRINT(PLAT_LOG_LEVEL_WARN, fmt, ##arg)
#define alogd(fmt, arg...) GLOG_PRINT(PLAT_LOG_LEVEL_INFO, fmt, ##arg)
#define alogv(fmt, arg...)
    #define PLAT_LOG_LEVEL_INFO   _GLOG_INFO
    #define PLAT_LOG_LEVEL_WARN   _GLOG_WARN
    #define PLAT_LOG_LEVEL_ERROR  _GLOG_ERROR
    #define PLAT_LOG_LEVEL_FATAL  _GLOG_FATAL

宏 GLOG_PRINT 是这样的:

/**
 * @brief 定义日志打印宏
 *
 * 此宏用于在代码中方便地打印不同级别的日志信息。它自动包含文件名、函数名、行号以及自定义的日志级别和格式化字符串。
 *
 * @param[in] level 日志级别,用于区分日志的重要程度,如DEBUG、INFO、WARN、ERROR等。
 * @param[in] fmt   格式化字符串,遵循printf风格,用于构造日志的具体文本内容。
 * @param[in] arg... 可变参数列表,与fmt字符串中的占位符对应,支持多个参数输入。
 *
 * @note
 * - 使用##arg确保在参数为空的情况下宏展开正确。
 * - 宏通过do-while(0)结构保证其具有块语句的特性,可直接作为单条语句使用而无需额外的大括号包裹。
 * - __FILE__、__func__、__LINE__是预处理器宏,分别表示源文件名、函数名、当前行号,提供日志的详细来源信息。
 *
 * 示例:
 * @code
 * GLOG_PRINT(DEBUG, "这是一条调试信息,变量x的值为:%d", x);
 * @endcode
 */
#define GLOG_PRINT(level, fmt, arg...) \
do { \
    log_printf(__FILE__, __func__, __LINE__, level, fmt, ##arg); \
} while (0)

定义宏 GLOG_PRINT,用于打印日志信息。
宏中 ##arg 语法是C/C++语言的变参宏技巧,处理可变参数。## 的作用是在宏中将可变长度参数的前一个参数和后一个参数连接起来,如果可变长度参数为空,则不产生任何内容。

GLOG_PRINT 宏展开后会调用 log_printf 函数,传递参数 __FILE__, __func__, __LINE__, level, fmt 以及可变数量的 arg 参数。##arg 确保在参数列表为空时,不会生成多余的逗号或者括号。

代码定义日志打印宏 GLOG_PRINT,通过变参的方式传递不定数量的参数。

_GLOG_INFO
_GLOG_WARN
_GLOG_ERROR
_GLOG_FATAL
这4个宏配置在这个文件中:

#ifndef LOG_PRINT_H_
#define LOG_PRINT_H_

#define _GLOG_INFO  0
#define _GLOG_WARN  1
#define _GLOG_ERROR 2
#define _GLOG_FATAL 3

#ifdef __cplusplus
extern "C" {
#endif
typedef struct GLogConfig
{
    //"log messages go to stderr instead of logfiles"
    int FLAGS_logtostderr;  // = false;  //--logtostderr=1, GLOG_logtostderr=1
    //"color messages logged to stderr (if supported by terminal)"
    int FLAGS_colorlogtostderr; // = true;
    //"log messages at or above this level are copied to stderr in addition to logfiles.  This flag obsoletes --alsologtostderr."
    int FLAGS_stderrthreshold; // = google::GLOG_WARNING;
    //"Messages logged at a lower level than this don't actually get logged anywhere"
    int FLAGS_minloglevel; // = google::GLOG_INFO;
    //"Buffer log messages logged at this level or lower (-1 means don't buffer; 0 means buffer INFO only;...)"
    int FLAGS_logbuflevel; // = -1;
    //"Buffer log messages for at most this many seconds"
    int FLAGS_logbufsecs; // = 0;
    //"approx. maximum log file size (in MB). A value of 0 will be silently overridden to 1."
    int FLAGS_max_log_size; // = 25;
    //"Stop attempting to log to disk if the disk is full."
    int FLAGS_stop_logging_if_full_disk; // = true;

    //e.g., "/tmp/log/LOG-"
    char LogDir[128];   //e.g., "/tmp/log"
    char InfoLogFileNameBase[128];  //e.g., "LOG-"
    char LogFileNameExtension[128];    //e.g., "SDV-"
}GLogConfig;
void log_init(const char *program, GLogConfig *pConfig);
void log_quit();
int log_printf(const char *file, const char *func, int line, const int level, const char *format, ...);

#ifdef __cplusplus
}
#endif

#endif

GLOG_PRINT带参数宏调用的函数:

/**
 * @brief 打印格式化日志,根据日志级别使用Google日志系统输出
 *
 * 此函数接收一个文件名、函数名、行号、日志级别和一个可变参数列表,用于生成和打印格式化的日志消息。
 * 它首先尝试在固定大小的缓冲区中格式化字符串,如果需要更大的空间,则动态分配内存。
 *
 * @param file 日志产生时的源代码文件名
 * @param func 调用此函数的函数名
 * @param line 产生日志的源代码行号
 * @param level 日志级别,可以是预定义的_GLOG_INFO, _GLOG_WARN, _GLOG_ERROR, _GLOG_FATAL
 * @param format 格式化字符串,类似于printf函数的格式控制符
 *
 * @return 返回0,目前此函数没有实际返回值
 *
 * @note
 * - 使用vsnprintf进行安全的格式化字符串处理。
 * - 如果格式化后的字符串长度超过固定缓冲区大小,将尝试动态分配内存以适应字符串长度。
 * - 动态分配的内存会在函数结束前释放。
 * - 根据日志级别,使用google::LogMessage流对象将日志输出到相应的日志级别。
 */

int log_printf(const char *file, const char *func, int line, const int level, const char *format, ...)
{
    int result = 0;
    char ChBuffer[128] = {0}; // 初始化为零的固定大小缓冲区
    char *buffer = NULL;
    char *p = NULL;
    int n;
    int size = sizeof(ChBuffer); // 缓冲区初始大小

    va_list args; // 变长参数列表指针

    // 初始化变长参数列表
    va_start(args, format);
    // 格式化字符串到固定大小的缓冲区
    n = vsnprintf(ChBuffer, size, format, args);
    // 结束变长参数列表
    va_end(args);

    // 如果格式化成功且字符串长度小于固定缓冲区大小,直接使用ChBuffer
    if (n > -1 && n < size)
    {
        buffer = ChBuffer;
    }
    // 否则,如果需要更多空间,尝试动态分配内存
    else if (n > -1)
    {
        size = n + 1; // 获取精确需要的内存大小
        if ((p = (char*)malloc(size)) == NULL)
        {
            // 内存分配失败,使用固定缓冲区
            printf("(f:%s, l:%d) fatal error! n=%d. use previous buffer\n", __FUNCTION__, __LINE__, n);
            buffer = ChBuffer;
        }
        else
        {
            // 重新格式化到新分配的内存
            va_start(args, format);
            n = vsnprintf(p, size, format, args);
            va_end(args);
            // 如果格式化成功,使用新分配的内存
            if (n > -1 && n < size)
            {
                buffer = p;
            }
            else
            {
                // 格式化失败,使用固定缓冲区
                printf("(f:%s, l:%d) fatal error! n=%d. use previous buffer\n", __FUNCTION__, __LINE__, n);
                buffer = ChBuffer;
            }
        }
    }
    else
    {
        // 格式化失败,使用固定缓冲区
        printf("(f:%s, l:%d) fatal error! n=%d. use previous buffer\n", __FUNCTION__, __LINE__, n);
        buffer = ChBuffer;
    }

    // 根据日志级别输出日志
    switch (level)
    {
        case _GLOG_INFO:
            google::LogMessage(file, line).stream()
                << XPOSTO(60)
                << "<" << func << "> "
                //<< XPOSTO(90)
                << buffer;
            break;
        case _GLOG_WARN:
            google::LogMessage(file, line, google::GLOG_WARNING).stream()
                << XPOSTO(60)
                << "<" << func << "> "
                //<< XPOSTO(90)
                << buffer;
            break;
        case _GLOG_ERROR:
            google::LogMessage(file, line, google::GLOG_ERROR).stream()
                << XPOSTO(60)
                << "<" << func << "> "
                //<< XPOSTO(90)
                << buffer;
            break;
        case _GLOG_FATAL:
            google::LogMessage(file, line, google::GLOG_FATAL).stream()
                << XPOSTO(60)
                << "<" << func << "> "
                //<< XPOSTO(90)
                << buffer;
            break;
        default:
            google::LogMessage(file, line).stream()
                << XPOSTO(60)
                << "<" << func << "> "
                //<< XPOSTO(90)
                << buffer;
            break;
    }

    // 释放动态分配的内存
    if (p)
    {
        free(p);
        p = NULL;
    }

    return result;
}

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

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

相关文章

小D----海量数据商用短链平台项目大课

从0-1 掌握ClickHouse新一代OLAP数据库。 Kafka接入组件封装Ip获取地理位置信息库使用。 后端工程师角度进阶数据仓库分层大数据领域技术视野 Flinkkafka短链接数据实时计算多维度数据处理。 Async异步关联查询多维度宽表扩展。 Flink多流合并DWS层整合Click House存储。

JDBC【封装工具类、SQL注入问题】

day54 JDBC 封装工具类01 创建配置文件 DBConfig.properties driverNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/qnz01?characterEncodingutf8&serverTimezoneUTC usernameroot passwordroot新建配置文件&#xff0c;不用写后缀名 创建工具类 将变…

北斗/GPS模块输出的NMEA语句详解

NMEA协议采用 ASCII 码来传递 GPS 定位信息&#xff0c;我们称之为帧。 帧格式形如&#xff1a;$aaccc,ddd,ddd,…,ddd*hh(CR)(LF) 1、“$”&#xff1a;帧命令起始位 2、aaccc&#xff1a;地址域&#xff0c;前两位为识别符&#xff08;aa&#xff09;&#xff0c;后三位为…

记一次android打包,因路由规则设置不合理而导致pom文件无法访问的错误

一、错误详情 FAILURE: Build failed with an exception.* What went wrong: Could not determine the dependencies of task :mediaplayer:compileReleaseAidl. > Could not resolve all task dependencies for configuration :mediaplayer:releaseCompileClasspath.> C…

nodejs版本升级12->18

1.把老版本删除&#xff0c;没删除升级没成功。 2.在官网下载新版本。 3.在菜单中输入cmd&#xff0c;一定要用管理员身份运行&#xff0c;切记&#xff0c;不然会出现2503/2502错误。 4.安装即可。

SCI丨5分期刊,JCR一区

SCI&#xff0c;5分&#xff0c;JCR Q1&#xff0c;中科大类3小类2区 1 基于复杂网络与xxx能源汽车节能数值分析 2 基于热能损失优化的xxx与性能管理 3 基于xxxLCA技术的绿色制造工艺优化研究 4 基于xxx入侵检测技术的物联网智能制造监控系统设计 6 基于物联网技术xxx电力系…

跨境电商自养号全攻略:TEMU、Shein、速卖通测评技巧揭秘

TEMU、Shein、速卖通等跨境平台都推出了全托管模式&#xff0c;普通平台讲究排名&#xff0c;销量&#xff0c;流量量&#xff0c;转化率等等。那么全托管为什么需要做测评呢&#xff1f;因为全托管平台讲究的是一个动销率&#xff0c;有的新品上架或许很快就出单&#xff0c;而…

邮件通知提醒邮箱设置教程及API代码示例!

邮件通知的警告功能如何配置&#xff1f;详细教程与API代码示例&#xff01; 无论是业务提醒、账户活动警告&#xff0c;还是个人事务&#xff0c;邮件通知已经成为一种重要的沟通工具。AokSend将详细介绍如何设置邮件通知提醒邮箱&#xff0c;并提供相应的API代码示例&#x…

场景管理分析平台介绍

在数字化浪潮的推动下&#xff0c;数据已成为企业决策的重要依据。特别是在智能驾驶、虚拟现实和物联网等领域&#xff0c;场景数据的高效管理和利用至关重要。在智能驾驶领域面对海量的场景数据&#xff0c;如何高效处理、精准分析&#xff0c;并将其转化为有价值的决策支持&a…

[OC]萝卜圈Python手动机器人脚本

这是给机器人设置的端口&#xff0c;对照用 代码 # #作者:溥哥’ ##机器人驱动主程序 #请在main中编写您自己的机器人驱动代码 import msvcrt def main():a"none"while True:key_input msvcrt.getch()akey_inputif abw:print(a)robot_drv.set_motors(1,40,2,40,3,…

(漏洞检查项) | 任意文件包含漏洞 file-include

(漏洞检查项)|任意文件包含漏洞 file-include 漏洞场景 1.含有动态包含语句 2.有类似于文件读取的url 漏洞描述 攻击者可以利用任意文件包含漏洞&#xff0c;读取任意文件&#xff0c;对服务器造成危害。 程序开发人员为了代码的灵活性&#xff0c;常常会将包含文件的路径…

SpringBoot怎么单独关闭某个类打印出来的日志?

application.yml文件增加以下内容&#xff1a; logging:level:org.springframework.amgp.rabbit: OFF 配置logging:level是配置的什么&#xff1f; 在application.yml文件中配置logging.level是用来设置日志级别的。这是Spring Boot应用中的一个常用配置&#xff0c;它允许您…

JeecgFlow错误事件

事件定义 错误事件可以用做一个流程的开始事件或者作为一个任务或者子流程的边界事件&#xff0c;错误事件没有提供作用中间事件的功能&#xff0c;这一点和前面介绍的定时器事件和消息事件还有区别的。在错误事件中提供了错误结束事件。 BPMN错误和Java异常并没有直接关联。BP…

tiktok数据分析应用介绍和tiktok数据分析平台分享

对于创作者、商家&#xff0c;tiktok官方有提供相应的数据分析为精细化运营给予辅助支持。 tiktok官方数据分析功能 TikTok Pro Account&#xff08;专业账户&#xff09;&#xff0c;包括CA账户&#xff08;Creator Account&#xff09;和BA&#xff08;Business Account&am…

ONLYOFFICE8.1版本桌面编辑器简单测评

ONLYOFFICE官网链接&#xff1a;在线PDF查看器和转换器 | ONLYOFFICE ONLYOFFICE介绍&#xff1a;https://www.onlyoffice.com/zh/office-suite.aspx OnlyOffice 是一款免费且开源的 Office 协作办公套件&#xff0c;支持桌面端和移动端等多平台&#xff0c;由一家领先的 IT 公…

Python深度理解系列之【排序算法——冒泡排序】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️木道寻的主页 文章目录 &#x1f525;前言&#x1f680;冒泡排序python实现算法实现图形化算法展示 ⭐️⭐️⭐️总结 &#x1f525;前…

师傅们 ~ 2024HW一手资料

各位师傅们&#xff0c;2024HW来了&#xff01; 从2026年开始&#xff0c;随着我国对网络安全的重视&#xff0c;涉及单位不断增加&#xff0c;越来越多单位和个人都加入到HW当中。 2024HW就在眼前&#xff0c; 那么还有不了解或者还没投简历面试的朋友们&#xff0c;需要注意…

职升网:中级会计师考试难度是怎样的?

中级会计师考试确实被普遍认为是具有一定难度的考试。以下是我对其难度的分析&#xff1a; 一、知识体系的广泛性 中级会计师考试覆盖的内容十分广泛&#xff0c;包括但不限于财务管理、财务会计、成本会计、税法等。这就要求考生具备扎实的基础知识和广泛的知识面&#xff0…

咨询公司在推行TPM管理中有哪些不可替代的作用?

TPM管理作为一种先进的生产维护理念&#xff0c;正逐渐成为企业追求卓越生产性能的不二之选。在这场转型升级的浪潮中&#xff0c;咨询公司扮演着不可替代的角色&#xff0c;它们如何助力企业成功推行TPM管理&#xff0c;成为了我们今天要探讨的焦点。 一、专业引领&#xff0c…

在Ubuntu 22.04 LTS 上安装 MySQL两种方式:在线方式和离线方式

Ubuntu安装MySQL 介绍&#xff1a; Ubuntu 是一款基于Linux操作系统的免费开源发行版&#xff0c;广受欢迎。它以稳定性、安全性和用户友好性而闻名&#xff0c;适用于桌面和服务器环境。Ubuntu提供了大量的软件包和应用程序&#xff0c;拥有庞大的社区支持和活跃的开发者社区…