【BlueZ】 log 模块:日志系统的实现与自定义日志输出配置

在 BlueZ 5.x 的架构中,log 模块是整个蓝牙守护进程(bluetoothd)的"听诊器"。它不仅承担着传统的调试输出功能,还实现了基于 HCI 日志通道的远程日志传输,让开发者可以通过 btmon 工具实时捕获蓝牙协议栈的运行时日志。这种双路日志输出架构(syslog + HCI Logging Channel)使得 BlueZ 在嵌入式蓝牙开发场景下具有极强的可观测性。


目录

[一、log 模块整体架构](#一、log 模块整体架构)

二、日志打印接口实现

三、调试描述符机制

[四、HCI 日志传输协议](#四、HCI 日志传输协议)

五、日志初始化流程

六、调试过滤机制

七、运行时日志控制

[八、btmon 日志接收与显示](#八、btmon 日志接收与显示)

九、日志环境变量与配置

十、各业务模块日志调用

十一、自定义日志重定向

十二、调试排错实操技巧

十三、核心函数索引

十四、日志数据流完整链路

十五、总结


本文基于 BlueZ 5.x 完整源码,深度剖析 log 模块的实现机制,覆盖核心源码文件包括:

  • src/log.h --- 日志接口定义与调试描述符结构

  • src/log.c --- 日志核心实现(syslog 输出 + 调试控制)

  • src/shared/log.h --- HCI 日志通道 API

  • src/shared/log.c --- HCI 日志传输实现

  • src/main.c --- 日志初始化与运行时控制


一、 log 模块整体架构

1.1 双路日志输出架构

BlueZ 采用创新的双路日志输出架构:

cpp 复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                        bluetoothd 进程                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  业务模块 (adapter.c, device.c, agent.c 等)                          │
│       │                                                             │
│       ▼                                                             │
│  日志宏接口 (DBG/error/warn/info)                                    │
│       │                                                             │
│       ├──► syslog (本地系统日志)                                     │
│       │    └──► /var/log/syslog 或 journald                         │
│       │                                                             │
│       └──► HCI Logging Channel (远程日志传输)                        │
│            └──► btmon 实时捕获与显示                                 │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

|---------------------------------|---------------------------------|-----------|
| 输出路径 | 实现机制 | 适用场景 |
| syslog | vsyslog() 系统调用 | 系统级日志持久化 |
| HCI Logging Channel | PF_BLUETOOTH socket + sendmsg() | 实时调试、协议分析 |

1.2 日志分级体系

BlueZ 采用标准的 syslog 优先级分级:

cpp 复制代码
// syslog 标准优先级
LOG_EMERG    (0)  // 紧急:系统不可用
LOG_ALERT    (1)  // 警报:立即需要修复
LOG_CRIT     (2)  // 严重:关键错误
LOG_ERR      (3)  // 错误:需要关注
LOG_WARNING  (4)  // 警告:可能的问题
LOG_NOTICE   (5)  // 通知:正常但重要
LOG_INFO     (6)  // 信息:常规信息
LOG_DEBUG    (7)  // 调试:详细调试信息

1.3 核心数据结构 btd_debug_desc

每个调试日志点都通过 struct btd_debug_desc 描述(定义于 log.h):

cpp 复制代码
struct btd_debug_desc {
    const char *file;               // 源文件路径
#define BTD_DEBUG_FLAG_DEFAULT (0)  // 默认未启用
#define BTD_DEBUG_FLAG_PRINT   (1 << 0)  // 启用打印标志
    unsigned int flags;             // 调试标志位
} __attribute__((aligned(8)));

关键设计

  • 每个 DBG() 宏调用都会创建一个静态的 btd_debug_desc 变量

  • 使用 section("__debug") 属性将所有描述符链接到 ELF 的 __debug

  • 运行时通过遍历 __start___debug__stop___debug 来发现所有调试点

二、日志打印接口实现

2.1 核心日志函数

BlueZ 提供了六个核心日志函数(定义于 log.c):

info():通用信息日志

cpp 复制代码
// log.c:43
void info(const char *format, ...)
{
    va_list ap;

    // 输出到 syslog
    va_start(ap, format);
    vsyslog(LOG_INFO, format, ap);
    va_end(ap);

    // 输出到 HCI Logging Channel
    va_start(ap, format);
    monitor_log(HCI_DEV_NONE, LOG_INFO, format, ap);
    va_end(ap);
}

特点

  • 无控制器索引参数,使用 HCI_DEV_NONE

  • 固定优先级为 LOG_INFO

  • 适用于全局非控制器相关的日志

btd_debug():带索引的调试日志

cpp 复制代码
// log.c:108
void btd_debug(uint16_t index, const char *format, ...)
{
    va_list ap;

    va_start(ap, format);
    vsyslog(LOG_DEBUG, format, ap);
    va_end(ap);

    va_start(ap, format);
    monitor_log(index, LOG_DEBUG, format, ap);
    va_end(ap);
}

特点

  • 支持控制器索引参数,可通过 -i 选项过滤

  • 固定优先级为 LOG_DEBUG

  • 输出到 syslog 和 HCI 双通道

其他日志函数

cpp 复制代码
// 带索引的通用日志(可变优先级)
void btd_log(uint16_t index, int priority, const char *format, ...);

// 错误日志
void btd_error(uint16_t index, const char *format, ...);

// 警告日志
void btd_warn(uint16_t index, const char *format, ...);

// 信息日志
void btd_info(uint16_t index, const char *format, ...);

2.2 monitor_log():HCI 日志通道转发

所有日志函数最终都会调用 monitor_log()log.c:):

cpp 复制代码
// log.c:37
static void monitor_log(uint16_t index, int priority,
            const char *format, va_list ap)
{
    // 通过 bt_log_vprintf() 发送到 HCI Logging Channel
    bt_log_vprintf(index, LOG_IDENT, priority, format, ap);
}

其中 LOG_IDENT 定义为 "bluetoothd"log.c,作为日志标识。

三、调试描述符机制

3.1 DBG 宏:自动注册调试点

DBG 宏是 BlueZ 日志系统的核心创新(log.h):

cpp 复制代码
// log.h:49-56
#define DBG_IDX(idx, fmt, arg...) do { \
    static struct btd_debug_desc __btd_debug_desc \
    __attribute__((used, section("__debug"), aligned(8))) = { \
        .file = __FILE__, .flags = BTD_DEBUG_FLAG_DEFAULT, \
    }; \
    if (__btd_debug_desc.flags & BTD_DEBUG_FLAG_PRINT) \
        btd_debug(idx, "%s:%s() " fmt, __FILE__, __func__ , ## arg); \
} while (0)

#define DBG(fmt, arg...) DBG_IDX(0xffff, fmt, ## arg)

工作原理

  1. 静态变量创建:每个 DBG() 调用都定义一个唯一的静态 btd_debug_desc 变量

  2. 段属性定位:使用 section("__debug") 将变量放入 ELF 的特殊段

  3. 条件编译:运行时检查 BTD_DEBUG_FLAG_PRINT 标志决定是否输出

  4. 自动格式化:自动附加 FILE 和 func 信息

3.2 日志调用示例

adapter.c中可以看到典型的 DBG 使用:

cpp 复制代码
// adapter.c:392
DBG("Class: 0x%06x", dev_class);

// adapter.c:570-571
DBG("Changed settings: 0x%08x", changed_mask);
DBG("Pending settings: 0x%08x", adapter->pending_settings);

编译后,每个 DBG() 宏调用会生成:

  1. 一个静态的 btd_debug_desc 变量(存入 .debug 段)

  2. 条件性的 btd_debug() 函数调用

3.3 错误和警告宏

cpp 复制代码
// log.h:59-62
#define error(fmt, arg...) \
    btd_error(0xffff, "%s:%s() " fmt, __FILE__, __func__, ## arg)
#define warn(fmt, arg...) \
    btd_warn(0xffff, "%s:%s() " fmt, __FILE__, __func__, ## arg)

特点

  • error()warn() 总是输出(无条件判断)

  • 自动包含文件名和函数名

  • 使用固定索引 0xffff(HCI_DEV_NONE)

四、HCI 日志传输协议

4.1 日志数据包格式

HCI 日志通道使用自定义的二进制协议(定义于 src/shared/log.c):

cpp 复制代码
// src/shared/log.c:32-38
struct log_hdr {
    uint16_t opcode;    // 操作码 (固定为 0x0000)
    uint16_t index;     // 控制器索引
    uint16_t len;       // 数据长度
    uint8_t  priority;  // 日志优先级
    uint8_t  ident_len; // 标识长度
} __attribute__((packed));

数据包结构

4.2 bt_log_open():建立 HCI 日志通道

bt_log_open() 创建并绑定 HCI 日志 socket:

cpp 复制代码
// src/shared/log.c:95-128
int bt_log_open(void)
{
    struct sockaddr_hci addr;
    int fd;
    static int err;

    if (err < 0)
        return err;

    if (log_fd >= 0)
        return log_fd;

    // 创建 HCI socket
    fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
    if (fd < 0) {
        err = -errno;
        return -errno;
    }

    // 绑定到 HCI_LOGGING 通道
    memset(&addr, 0, sizeof(addr));
    addr.hci_family = AF_BLUETOOTH;
    addr.hci_dev = HCI_DEV_NONE;          // 不绑定特定控制器
    addr.hci_channel = HCI_CHANNEL_LOGGING; // 日志通道 (值为 4)

    err = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
    if (err < 0) {
        err = -errno;
        close(fd);
        return err;
    }

    log_fd = fd;
    return fd;
}

关键步骤

  1. 创建 PF_BLUETOOTH 域的原始 socket

  2. 绑定到 HCI_CHANNEL_LOGGING 通道(值为 4)

  3. 不绑定特定设备(HCI_DEV_NONE),日志对所有控制器可见

4.3 bt_log_sendmsg():发送日志数据

bt_log_sendmsg() 使用 sendmsg() 发送完整的日志数据包:

cpp 复制代码
// src/shared/log.c:47-93
int bt_log_sendmsg(uint16_t index, const char *label, int level,
            struct iovec *io, size_t io_len)
{
    struct log_hdr hdr;
    struct msghdr msg;
    struct iovec iov[5];
    size_t i;
    int err;

    if (io_len > 3)
        return -EMSGSIZE;

    log_fd = bt_log_open();
    if (log_fd < 0)
        return log_fd;

    // 填充日志头
    hdr.opcode = cpu_to_le16(0x0000);
    hdr.index = cpu_to_le16(index);
    hdr.ident_len = strlen(label) + 1;
    hdr.len = cpu_to_le16(2 + hdr.ident_len);
    hdr.priority = level;

    // 构建 scatter-gather I/O
    iov[0].iov_base = &hdr;
    iov[0].iov_len = sizeof(hdr);

    iov[1].iov_base = (void *) label;
    iov[1].iov_len = hdr.ident_len;

    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = iov;
    msg.msg_iovlen = 2;

    // 添加额外的数据块
    for (i = 0; i < io_len; i++) {
        iov[i + 2] = io[i];
        hdr.len += io[i].iov_len;
        msg.msg_iovlen++;
    }

    // 发送数据
    err = sendmsg(log_fd, &msg, 0);
    if (err < 0) {
        err = -errno;
        close(log_fd);
        log_fd = -1;
    }

    return err;
}

性能优化

  • 使用 scatter-gather I/O 减少系统调用次数

  • 失败时自动关闭连接并重置状态

  • 支持最多 3 个额外数据块(io_len <= 3

4.4 bt_log_vprintf():格式化日志

bt_log_vprintf()格式化并发送日志消息:

cpp 复制代码
// src/shared/log.c:130-157
int bt_log_vprintf(uint16_t index, const char *label, int level,
            const char *format, va_list ap)
{
    struct iovec iov;
    char *str;
    int len;

    // 格式化字符串
    len = vasprintf(&str, format, ap);
    if (len < 0)
        return errno;

    len = strlen(str);

    // 移除末尾的换行符(btmon 会自动添加)
    if (len > 1 && str[len - 1] == '\n') {
        str[len - 1] = '\0';
        len--;
    }

    // 构建 I/O 向量并发送
    iov.iov_base = str;
    iov.iov_len = len + 1;

    len = bt_log_sendmsg(index, label, level, &iov, 1);

    free(str);
    return len;
}

五、日志初始化流程

5.1 __btd_log_init():日志系统初始化

__btd_log_init() 是日志系统的入口函数:

cpp 复制代码
// log.c:163-180
void __btd_log_init(const char *debug, int detach)
{
    int option = LOG_NDELAY | LOG_PID;

    // 1. 解析调试选项
    if (debug != NULL)
        enabled = g_strsplit_set(debug, ":, ", 0);

    // 2. 启用匹配的调试点
    __btd_enable_debug(__start___debug, __stop___debug);

    // 3. 打开 HCI 日志通道
    bt_log_open();

    // 4. 设置 syslog 选项
    if (!detach)
        option |= LOG_PERROR;  // 非守护模式下同时输出到 stderr

    // 5. 打开 syslog
    openlog(LOG_IDENT, option, LOG_DAEMON);

    // 6. 输出启动日志
    info("Bluetooth daemon %s", VERSION);
}

初始化步骤

  1. 解析调试过滤字符串(支持 :, ,, 分隔符)

  2. 遍历所有调试描述符,启用匹配的调试点

  3. 打开 HCI 日志通道用于远程日志传输

  4. 根据 detach 参数决定是否同时输出到 stderr

  5. 打开 syslog,设置标识符为 "bluetoothd"

  6. 输出启动信息日志

5.2 main() 中的调用

main.c:1189 中调用日志初始化:

cpp 复制代码
// main.c:1189
__btd_log_init(option_debug, option_detach);

5.3 选项解析

option_debug:调试过滤字符串

cpp 复制代码
// main.c:1102-1111
static gboolean parse_debug(const char *key, const char *value,
            gpointer user_data, GError **error)
{
    if (value)
        option_debug = g_strdup(value);
    else
        option_debug = g_strdup("*");  // 无参数时启用所有调试

    return TRUE;
}

支持格式

  • * --- 启用所有调试日志

  • adapter.c --- 仅启用 adapter.c 的调试

  • adapter.c:device.c --- 启用多个文件的调试

  • adapter.c,device.c --- 使用逗号分隔

  • adapter.c device.c --- 使用空格分隔

option_detach:守护进程模式

cpp 复制代码
// main.c:1040
static gboolean option_detach = TRUE;  // 默认守护进程模式
  • TRUE:后台运行,日志仅输出到 syslog

  • FALSE:前台运行,日志同时输出到 stderr

六、调试过滤机制

6.1 is_enabled():匹配调试描述符

is_enabled()判断某个调试描述符是否应该启用:

cpp 复制代码
// log.c:126-139
static gboolean is_enabled(struct btd_debug_desc *desc)
{
    int i;

    if (enabled == NULL)
        return 0;  // 未设置调试过滤时不启用

    for (i = 0; enabled[i] != NULL; i++)
        // 使用 GPattern 模式匹配
        if (desc->file != NULL && g_pattern_match_simple(enabled[i],
                            desc->file) == TRUE)
            return 1;

    return 0;
}

匹配规则

  • 使用 g_pattern_match_simple() 进行 glob 模式匹配

  • * 匹配所有文件

  • adapter.c 仅匹配 adapter.c

  • adapter* 匹配所有以 adapter 开头的文件

6.2 __btd_enable_debug():批量启用调试

__btd_enable_debug()遍历指定范围的调试描述符:

cpp 复制代码
// log.c:141-153
void __btd_enable_debug(struct btd_debug_desc *start,
            struct btd_debug_desc *stop)
{
    struct btd_debug_desc *desc;

    if (start == NULL || stop == NULL)
        return;

    for (desc = start; desc < stop; desc++) {
        if (is_enabled(desc))
            desc->flags |= BTD_DEBUG_FLAG_PRINT;
    }
}

6.3 __btd_toggle_debug():强制启用所有调试

__btd_toggle_debug()强制启用所有调试点:

cpp 复制代码
// log.c:155-161
void __btd_toggle_debug(void)
{
    struct btd_debug_desc *desc;

    for (desc = __start___debug; desc < __stop___debug; desc++)
        desc->flags |= BTD_DEBUG_FLAG_PRINT;
}

用途:响应 SIGUSR2信号,在运行时动态启用所有调试日志。

七、运行时日志控制

7.1 SIGUSR2 信号处理

main.c中处理 SIGUSR2 信号:

cpp 复制代码
// main.c:1027-1031
case SIGUSR2:
    __btd_toggle_debug();  // 启用所有调试日志
    break;

使用方法

cpp 复制代码
# 发送 SIGUSR2 信号给 bluetoothd
kill -USR2 $(pidof bluetoothd)

7.2 GLib 日志处理

main.c 中设置 GLib 日志处理器:

cpp 复制代码
// main.c:1191-1193
g_log_set_handler("GLib", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL |
            G_LOG_FLAG_RECURSION,
            log_handler, NULL);

7.3 日志清理

__btd_log_cleanup()关闭日志系统:

cpp 复制代码
// log.c:182-189
void __btd_log_cleanup(void)
{
    closelog();       // 关闭 syslog
    bt_log_close();   // 关闭 HCI 日志通道
    g_strfreev(enabled);  // 释放调试过滤列表
}

八、btmon 日志接收与显示

8.1 btmon 接收日志

btmon 通过监听 HCI 日志通道接收 bluetoothd 发送的日志数据包。当收到 BTSNOOP_OPCODE_USER_LOGGING 操作码的数据包时进行解析。

8.2 数据包解析

monitor/packet.c中处理用户日志:

cpp 复制代码
// monitor/packet.c:4051-4058
case BTSNOOP_OPCODE_USER_LOGGING:
    ul = data;
    ident = ul->ident_len ? data + sizeof(*ul) : NULL;

    packet_user_logging(tv, cred, index, ul->priority, ident,
                data + sizeof(*ul) + ul->ident_len,
                size - (sizeof(*ul) + ul->ident_len));
    break;

8.3 使用 btmon 调试

cpp 复制代码
# 启动 btmon 实时捕获日志
sudo btmon

# 启动 bluetoothd 并启用调试
sudo bluetoothd -d
sudo bluetoothd -d '*'          # 所有模块调试
sudo bluetoothd -d 'adapter.c'  # 仅 adapter 模块调试

# 控制器过滤
sudo bluetoothd -d -i 0         # 仅显示 hci0 的日志

九、日志环境变量与配置

9.1 编译时调试开关

cpp 复制代码
# 编译时启用调试
./configure --enable-debug

# 运行时增加日志
export G_MESSAGES_DEBUG=all
bluetoothd

9.2 日志级别控制

cpp 复制代码
# syslog 配置
# 编辑 /etc/rsyslog.conf 或 /etc/syslog-ng/.conf

# 查看实时日志
journalctl -u bluetooth -f
journalctl -t bluetoothd -f

# 按时间过滤
journalctl --since "10 min ago" -t bluetoothd

9.3 日志过滤配置

自定义调试过滤

cpp 复制代码
# 格式: -d <文件模式>[,<文件模式>...]
bluetoothd -d 'adapter.c:device.c'   # 多个文件
bluetoothd -d 'adapter*.c'            # glob 模式
bluetoothd -d '*'                     # 所有文件

控制器过滤

cpp 复制代码
# btmon 过滤特定控制器
btmon -i 0           # 仅 hci0
btmon -i 1           # 仅 hci1
btmon -i 0 -i 1      # 多个控制器

优先级过滤

cpp 复制代码
# btmon 按优先级过滤
btmon -p ERR         # 仅显示错误
btmon -p WARN        # 错误和警告
btmon -p INFO        # 信息及以上

十、各业务模块日志调用

10.1 典型日志调用模式

适配器操作日志

cpp 复制代码
// adapter.c - 设置设备类
DBG("Class: 0x%06x", dev_class);

// adapter.c - 发送 MGMT 命令
DBG("sending set mode command for index %u", adapter->dev_id);

设备操作日志

cpp 复制代码
// device.c - 设备连接
info("Device %s connected", bdaddr_to_string(bdaddr, buf, sizeof(buf)));

// device.c - 设备断开
info("Device %s disconnected", bdaddr_to_string(bdaddr, buf, sizeof(buf)));

错误处理日志

cpp 复制代码
// 错误日志(总是输出)
error("Unable to set discoverable: %s", err->message);

// 警告日志(总是输出)
warn("Agent disconnected unexpectedly");

10.2 日志调用统计

通过搜索 DBG( 可以发现,BlueZ 源码中包含大量调试日志点:

|---------|---------------|-------------|
| 模块 | 文件 | DBG 调用数 |
| 适配器管理 | adapter.c | 100+ |
| 设备管理 | device.c | 50+ |
| 代理管理 | agent.c | 20+ |
| GATT 服务 | gatt.c | 30+ |
| 广播管理 | advertising.c | 20+ |
| 广播监控 | adv_monitor.c | 25+ |

10.3 日志格式规范

BlueZ 日志遵循统一的格式规范:

cpp 复制代码
[文件名]:[函数名] [消息内容]

示例

cpp 复制代码
adapter.c:set_discoverable() Changed settings: 0x00000001
adapter.c:set_discoverable() Pending settings: 0x00000000
adapter.c:start_discovery() sending MGMT_OP_START_SERVICE_DISCOVERY 0, 0, 0

十一、自定义日志重定向

11.1 重定向到文件

cpp 复制代码
# 方法1: 通过 journalctl 导出
journalctl -u bluetooth --no-pager > bluetooth.log

# 方法2: 直接运行时重定向
bluetoothd -d -n 2>&1 | tee bluetooth.log

# 方法3: 使用 syslog 转发
# 配置 rsyslog 将 bluetoothd 日志发送到远程服务器
if $programname == 'bluetoothd' then @logserver:514

11.2 自定义日志过滤器

cpp 复制代码
# 使用 grep 过滤
btmon | grep -E "adapter|error"

# 使用 awk 格式化
btmon | awk '{print $1, $2, $NF}'

# 按时间过滤
btmon | grep "10:30:[0-5]"

11.3 日志分析脚本

cpp 复制代码
#!/bin/bash
# analyze_bt_log.sh - 蓝牙日志分析脚本

LOG_FILE=$1

echo "=== 蓝牙日志分析 ==="

# 统计错误
echo "错误统计:"
grep -c "ERR" "$LOG_FILE"

# 统计警告
echo "警告统计:"
grep -c "WARN" "$LOG_FILE"

# 常见错误分类
echo "连接错误:"
grep "connect.*fail\|connection.*error" "$LOG_FILE" | head -5

echo "认证错误:"
grep "auth.*fail\|pair.*error" "$LOG_FILE" | head -5

echo "超时错误:"
grep "timeout\|timed.out" "$LOG_FILE" | head -5

# 导出时间线
echo "关键事件时间线:"
grep -E "connect|disconnect|pair|unpair" "$LOG_FILE" | head -20

十二、调试排错实操技巧

12.1 启用详细调试

cpp 复制代码
# 1. 启动 bluetoothd 并启用所有调试
sudo systemctl stop bluetooth
sudo bluetoothd -d -n 2>&1 | tee /tmp/bt_debug.log

# 2. 另一个终端启动 btmon
sudo btmon | tee /tmp/bt_monitor.log

# 3. 操作蓝牙并收集日志
bluetoothctl power on
bluetoothctl discoverable on
bluetoothctl scan on

12.2 运行时切换调试级别

cpp 复制代码
# 获取 bluetoothd PID
BT_PID=$(pidof bluetoothd)

# 启用所有调试
kill -USR2 $BT_PID

# 测试蓝牙操作
bluetoothctl devices

# 查看日志变化
journalctl -u bluetooth --since "1 min ago"

12.3 内核日志配合

cpp 复制代码
# 同时查看内核日志
dmesg -w | grep -i bt

# 使用 hci 工具
hciconfig hci0
hcidump -t

# 使用 btmgmt
btmgmt info
btmgmt show

12.4 常见问题排查清单

问题 1:日志无输出

排查步骤

  1. 确认 syslog 服务正在运行:systemctl status rsyslog

  2. 检查 bluetoothd 是否以调试模式启动:ps aux | grep bluetooth

  3. 验证 HCI 日志通道可用:strace -e socket bluetoothd

  4. 检查权限:ls -la /var/log/

问题 2:调试日志被过滤

排查步骤

  1. 确认 BTD_DEBUG_FLAG_PRINT 已设置

  2. 检查文件模式匹配:-d '*' 启用所有

  3. 使用 SIGUSR2 强制启用

  4. 重新编译时启用 --enable-debug

问题 3:日志重复或丢失

排查步骤

  1. 检查是否同时在 syslog 和 btmon 输出

  2. 确认日志缓冲区是否溢出

  3. 检查 HCI 日志通道连接状态

  4. 使用 logger 命令验证 syslog

问题 4:性能影响

排查步骤

  1. 减少调试模块范围:-d 'adapter.c'

  2. 调整 syslog 优先级过滤

  3. 关闭 HCI 日志通道(无 btmon 时)

  4. 生产环境避免使用 -d '*'

12.5 高级调试技巧

使用 GDB 调试日志

cpp 复制代码
# 附加到 bluetoothd
gdb -p $(pidof bluetoothd)

# 在日志函数设置断点
break btd_debug
break btd_error

# 查看日志参数
bt
info args
p format

# 查看调试描述符
print __start___debug
print __stop___debug

使用 perf 分析日志性能

cpp 复制代码
# 采集日志相关的性能数据
perf record -g -p $(pidof bluetoothd) -e syscalls:sys_enter_sendmsg

# 分析日志系统调用
perf script | grep sendmsg

十三、核心函数索引

|----------------------|------------------|----------------------|
| 函数名 | 源文件 | 功能描述 |
| info | log.c:43 | 通用信息日志(syslog + HCI) |
| btd_log | log.c:56 | 带索引的可变优先级日志 |
| btd_error | log.c:69 | 带索引的错误日志 |
| btd_warn | log.c:82 | 带索引的警告日志 |
| btd_info | log.c:95 | 带索引的信息日志 |
| btd_debug | log.c:108 | 带索引的调试日志 |
| __btd_log_init | log.c:163 | 日志系统初始化 |
| __btd_log_cleanup | log.c:182 | 日志系统清理 |
| __btd_enable_debug | log.c:141 | 批量启用调试点 |
| __btd_toggle_debug | log.c:155 | 强制启用所有调试 |
| is_enabled | log.c:126 | 检查调试点是否启用 |
| bt_log_open | shared/log.c:95 | 打开 HCI 日志通道 |
| bt_log_close | shared/log.c:172 | 关闭 HCI 日志通道 |
| bt_log_sendmsg | shared/log.c:47 | 发送日志数据包 |
| bt_log_vprintf | shared/log.c:130 | 格式化并发送日志 |
| bt_log_printf | shared/log.c:159 | varargs 版本的日志发送 |
| monitor_log | log.c:37 | HCI 日志转发入口 |

十四、日志数据流完整链路

十五、总结

BlueZ log 模块通过创新的双路日志架构(syslog + HCI Logging Channel),为蓝牙守护进程提供了强大的可观测性支持。其核心设计特点包括:

  1. 双路输出架构:同时支持本地 syslog 持久化和远程 HCI 日志传输

  2. 段属性注册:通过 ELF 段属性自动注册所有调试点,无需手动维护

  3. 运行时控制:支持 SIGUSR2 信号动态启用所有调试日志

  4. 模式匹配过滤:使用 glob 模式灵活控制各模块的日志级别

  5. HCI 协议传输:基于标准 HCI 通道的日志传输,兼容 btmon 生态

  6. 零开销抽象:未启用的调试点完全不产生运行时开销

理解 BlueZ log 模块的实现机制,对于嵌入式蓝牙开发中的问题排查、性能优化和协议分析都具有重要的实践价值。


核心源文件路径索引

|--------------------|---------------------------------|
| 文件路径 | 关键内容 |
| src/log.h | 日志接口、btd_debug_desc 结构、DBG 宏定义 |
| src/log.c | 日志核心实现、syslog 输出、调试控制 |
| src/shared/log.h | HCI 日志通道 API 声明 |
| src/shared/log.c | HCI 日志传输实现、数据包格式定义 |
| src/main.c | 日志初始化调用、信号处理、选项解析 |
| monitor/packet.c | btmon 日志数据包解析与显示 |
| lib/hci.h | HCI 通道常量定义(HCI_CHANNEL_LOGGING) |

相关推荐
AI码农小姐姐1 小时前
AI漫剧推文短视频动态镜头实现:FFmpeg zoompan 与 Ken Burns 效果实践
人工智能·音视频·ai工具·ai漫剧
技灵AI1 小时前
ChatGPT Images 2.5 深度解读:延迟减半、Sketch 草图与 Flare/Sunburst 双模型 API
人工智能·gpt·aigc·音视频·images2.5
Geek-Chow1 小时前
12. 完整重演:一句话请求的完整旅程 + 动手练习
人工智能
m0_734571761 小时前
深入理解人工智能chatGPT 外部工具与知识增强层 (Tools & RAG Layer)
人工智能·chatgpt
智能运维指南1 小时前
2026年企业自动化运维平台选型:四类架构的差异与决策逻辑
运维·人工智能·嘉为蓝鲸
探索云原生1 小时前
KubeClipper 1.7.0 发布:Operation 优化与 Kubernetes 1.37 支持
linux·docker·云原生·kubernetes·go
user_admin_god1 小时前
第 01 篇:OpenAI 兼容 API 初探 —— 用 curl 跑通第一次对话
java·人工智能·spring boot·语言模型·devops
梦帮科技1 小时前
RNS 代币架构:ERC20 五件套扩展与六钱包分配
人工智能·sql·区块链·database·合成复用原则·加密货币
林澈在路上1 小时前
游戏场景背景音乐定制AI软件哪款好 2026对比推荐
大数据·人工智能·aigc·音视频