You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为C语言自定义日志库添加可变参数格式化打印功能

改造方案

要实现格式化可变参数打印,我们需要借助C语言标准库的<stdarg.h>处理可变参数,同时修改日志宏支持可变参数传递。

1. 修改 cd_log.h

#ifndef CONQUER_DOCKER_CD_LOG_H
#define CONQUER_DOCKER_CD_LOG_H

#include <stdarg.h>

#define CD_CURRENT_LOG_LEVEL CD_LOG_INFO_LEVEL

// 宏改造支持可变参数,##__VA_ARGS__兼容无额外参数的场景
#define CD_LOG_DEBUG(fmt, ...) cd_log(&CD_LOG_DEBUG_LEVEL, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define CD_LOG_INFO(fmt, ...) cd_log(&CD_LOG_INFO_LEVEL, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define CD_LOG_WARNING(fmt, ...) cd_log(&CD_LOG_WARNING_LEVEL, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define CD_LOG_ERROR(fmt, ...) cd_log(&CD_LOG_ERROR_LEVEL, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)

/**
 * log level
 */
typedef struct CDLogLevel {
    const char level;
    /**
     * level name
     */
    const char *name;
} CDLogLevel;

/**
 * log level
 */
extern const CDLogLevel CD_LOG_DEBUG_LEVEL;
extern const CDLogLevel CD_LOG_INFO_LEVEL;
extern const CDLogLevel CD_LOG_WARNING_LEVEL;
extern const CDLogLevel CD_LOG_ERROR_LEVEL;

/**
 * print log
 */
void cd_log(
        const CDLogLevel *logLevel, const char *fileName, const char *functionName, int line,
        const char *fmt, ...
);

#endif //CONQUER_DOCKER_CD_LOG_H

2. 修改 cd_log.c

#include "include/cd_log.h"
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

const CDLogLevel CD_LOG_DEBUG_LEVEL = {0, "DEBUG"};
const CDLogLevel CD_LOG_INFO_LEVEL = {1, "INFO"};
const CDLogLevel CD_LOG_WARNING_LEVEL = {2, "WARNING"};
const CDLogLevel CD_LOG_ERROR_LEVEL = {3, "ERROR"};

static const char *coloring(const CDLogLevel *logLevel) {
    size_t strLength = strlen(logLevel->name);
    size_t newStrLen = strLength + 16;
    char *newStr = malloc(newStrLen);
    memset(newStr, 0, newStrLen);

    if (logLevel->level == CD_LOG_DEBUG_LEVEL.level) {
        snprintf(newStr, newStrLen, "\033[34m%s\033[0m", logLevel->name);
    } else if (logLevel->level == CD_LOG_INFO_LEVEL.level) {
        snprintf(newStr, newStrLen, "\033[32m%s\033[0m", logLevel->name);
    } else if (logLevel->level == CD_LOG_WARNING_LEVEL.level) {
        snprintf(newStr, newStrLen, "\033[33m%s\033[0m", logLevel->name);
    } else if (logLevel->level == CD_LOG_ERROR_LEVEL.level) {
        snprintf(newStr, newStrLen, "\033[31m%s\033[0m", logLevel->name);
    } else {
        free(newStr);
        return logLevel->name;
    }
    return newStr;
}

void cd_log(const CDLogLevel *logLevel, const char *fileName, const char *functionName, int line, const char *fmt, ...) {
    if (logLevel->level < CD_CURRENT_LOG_LEVEL.level) {
        return;
    }
    // 处理可变参数,动态计算格式化后字符串长度避免溢出
    va_list ap;
    va_start(ap, fmt);
    int str_len = vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);
    if (str_len < 0) return;

    char *message_buf = malloc(str_len + 1);
    if (!message_buf) return;

    va_start(ap, fmt);
    vsnprintf(message_buf, str_len + 1, fmt, ap);
    va_end(ap);

    time_t currentTime = time(NULL);
    char timerStr[20];
    strftime(timerStr, 20, "%Y-%m-%d %H:%M:%S", localtime(&currentTime));

    if (isatty(STDOUT_FILENO)) {
        const char *levelStr = coloring(logLevel);
        fprintf(stdout, "%s [%-15s] %-80s %s %5d: %s\n",
                timerStr, levelStr, fileName, functionName, line, message_buf);
        free((void *) levelStr);
    } else {
        fprintf(stdout, "%s [%-7s] %-80s %s %5d: %s\n",
                timerStr, logLevel->name, fileName, functionName, line, message_buf);
    }
    free(message_buf);
}

3. 调用示例

直接按你期望的方式调用即可:

// 无额外参数场景兼容
CD_LOG_INFO("Hello World");
// 多参数格式化场景
CD_LOG_INFO("value01: %s, value02: %s, value03: %d" , "demo01", "demo2", 100);

关键说明

  • 宏定义中添加的##__VA_ARGS__是GCC/Clang等主流编译器支持的语法,用于处理无额外参数时多余的逗号问题,兼容单字符串打印场景
  • 采用两次vsnprintf的方式动态计算字符串长度,避免固定缓冲区溢出风险
  • 动态分配的内存都会在打印完成后释放,不会产生内存泄漏

内容的提问来源于stack exchange,提问作者aszswaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 15:15:00