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

Arduino自定义printf风格日志函数前缀格式异常问题求助

Arduino DebugSystem 日志格式修复:处理开头换行符问题

问题重现

当前DebugSystem的print方法会先输出日志前缀,再输出格式化后的消息。当用户传入的消息开头包含换行符(或其他空白/控制字符)时,会出现格式混乱:

调用代码:

debug.print(DEBUG_INFO,"Test message: %s\n","TEST");
debug.print(DEBUG_INFO,"\nTest message: %s\n","TEST");

实际输出:

[INFO] Test message: TEST
[INFO]
      Test message: TEST

期望输出:

[INFO] Test message: TEST

[INFO] Test message: TEST

解决方案

核心思路是先输出消息开头的所有空白/控制字符,再输出前缀和剩余的有效内容。这样既保留了用户想要的换行效果,又不会让前缀和有效内容分离。具体步骤:

  1. 先完成消息的格式化,得到完整的缓冲区内容
  2. 遍历缓冲区,提取开头所有的空白/控制字符(包括\n、\r、\t、空格等),先输出这些字符
  3. 如果还有剩余的非空白内容,先输出对应级别的前缀,再输出剩余内容
  4. 如果消息全是空白字符,则只输出这些空白,不输出前缀

代码实现

修改DebugSystem::print方法,替换原有的逻辑(注意需要包含<ctype.h>头文件以使用isspace函数):

#include <ctype.h>

void DebugSystem::print(byte level, const char *message, ...){
    if( level > debugLevel || level == NO_DEBUG ){ return; }
    char buffer[PRINT_BUFFER_SIZE];
    
    // 先完成消息格式化
    va_list args;
    va_start(args, message);
    vsprintf(buffer, message, args);
    va_end(args);

    // 处理开头的空白/控制字符
    char* ptr = buffer;
    // 输出所有开头的空白/控制字符
    while (*ptr != '\0' && isspace((unsigned char)*ptr)) {
        Serial.print(*ptr);
        ptr++;
    }

    // 如果还有剩余内容,输出前缀+剩余内容
    if (*ptr != '\0') {
        if (levelPrefix && level != NO_PREFIX) {
            const char* prefix = level == DEBUG_ERROR ? DEBUG_ERROR_PREFIX
                : level == DEBUG_WARN    ? DEBUG_WARN_PREFIX
                : level == DEBUG_SUCCESS ? DEBUG_SUCCESS_PREFIX
                : DEBUG_INFO_PREFIX;
            Serial.print(prefix);
        }
        Serial.print(ptr);
    }
}

说明

  • 用isspace函数覆盖所有常见的空白/控制字符,不止处理换行符,兼容更多开头格式场景
  • 先格式化再处理,避免直接解析原格式字符串的复杂性
  • 保留原有levelPrefix和NO_PREFIX的控制逻辑,不破坏已有的功能

额外优化(可选)

如果需要支持多行消息中每行自动添加前缀,可以用以下逻辑,遍历整个缓冲区,遇到换行且后续还有内容时,自动在下一行开头补前缀:

#include <ctype.h>

void DebugSystem::print(byte level, const char *message, ...){
    if( level > debugLevel || level == NO_DEBUG ){ return; }
    char buffer[PRINT_BUFFER_SIZE];
    
    va_list args;
    va_start(args, message);
    vsprintf(buffer, message, args);
    va_end(args);

    const char* prefix = nullptr;
    if (levelPrefix && level != NO_PREFIX) {
        prefix = level == DEBUG_ERROR ? DEBUG_ERROR_PREFIX
            : level == DEBUG_WARN    ? DEBUG_WARN_PREFIX
            : level == DEBUG_SUCCESS ? DEBUG_SUCCESS_PREFIX
            : DEBUG_INFO_PREFIX;
    }

    char* ptr = buffer;
    bool needPrefix = true;

    while (*ptr != '\0') {
        if (needPrefix && prefix != nullptr) {
            Serial.print(prefix);
            needPrefix = false;
        }
        if (*ptr == '\n') {
            Serial.print(*ptr);
            needPrefix = true; // 下一行开头需要补前缀
        } else {
            Serial.print(*ptr);
        }
        ptr++;
    }
}

该版本输入"\nLine1\nLine2"会输出:

[INFO] Line1
[INFO] Line2

内容的提问来源于stack exchange,提问作者Dr.Random

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:45:40