欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_rust_cargo

本文讲解鸿蒙 PC 端 Rust 开发环境搭建,鸿蒙基于 musl 库、强制二进制签名,无法直接使用通用 Linux 编译产物。需借助鸿蒙专属包管理器 Harmonybrew,提供两套编译方案:方案一安装 llvm-gcc-compat,零配置开箱即用;方案二仅安装 ohos-sdk,需手动配置 Cargo 链接器,二者都依托 ohos-sdk 完成自动签名编译。

可以来参考一下这个文章搭建环境OpenHarmony 鸿蒙 PC + CodeArts IDE 实现 Rust开发完整开发环境搭建指南


一、两个库分别是什么、作用

1. tracing

Rust 官方推荐结构化日志/追踪框架,替代老旧 log 库。

  • 核心概念:span(调用区间)、event(日志事件)、metadata(日志级别/分类)
  • 支持分级日志:trace < debug < info < warn < error
  • 天然支持链路追踪:记录函数执行区间、嵌套调用耗时(接口全链路打印起止、耗时)
  • 结构化日志:可以附加自定义KV字段(用户ID、订单号、请求ID),不是单纯纯文本打印
  • 异步友好,兼容 Tokio/axum/rocket 所有异步框架

2. tracing-subscriber

tracing 本身只定义日志规范,不实现输出逻辑,必须搭配此库才能打印日志到控制台/文件:

  • 控制台彩色格式化输出
  • 过滤日志级别(开发打全量,生产只打 warn/error)
  • 日志持久化写入文件、滚动分割日志
  • 兼容老 log 库(如果项目混用旧log宏)
  • 时间格式自定义、字段过滤、层级span展示

核心业务用途

  1. Web服务(Axum/Tokio)打印请求日志、链路耗时、请求ID、用户参数
  2. 后台脚本/CLI分级日志,区分调试/正常/警告/错误
  3. 链路追踪:自动打印函数起止、嵌套调用层次,定位慢函数
  4. 生产环境输出JSON结构化日志,对接ELK/日志平台检索
  5. 异步任务日志不混乱,多线程日志有序打印

二、安装依赖

# 一次性安装两个核心包
cargo add tracing
cargo add tracing-subscriber --features env-filter,local-time

Cargo.toml 自动生成:

[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"

请添加图片描述

三、完整无报错 main.rs 示例

覆盖:彩色控制台日志、日志级别过滤、自定义KV结构化字段、嵌套Span链路追踪、不同日志级别、格式化时间。

use tracing::{debug, error, info, span, trace, warn, Level};
use tracing_subscriber::{fmt, EnvFilter};

fn main() {
    // 1. 全局注册日志输出器(程序入口只初始化一次)
    tracing_subscriber::registry()
        // 控制台格式化输出
        .with(fmt::layer()
            // 开启彩色文字
            .with_ansi(true)
            // 打印日志产生的文件、行号
            .with_target(true)
            // 自定义时间格式
            .with_timer(fmt::time::LocalTime::rfc_3339())
        )
        // 日志级别过滤:读取环境变量 RUST_LOG,默认INFO及以上
        .with(EnvFilter::from_default_env())
        // 注册全局,整个程序生效
        .init();

    trace!("最细粒度调试日志,默认不会打印");
    debug!("调试日志:变量打印、内部流程");
    info!("普通业务信息:服务启动、请求正常完成");
    warn!("警告日志:非致命异常,如库存不足");
    error!("错误日志:程序异常、数据库失败");

    // ===================== 结构化日志:附加自定义KV参数 =====================
    let user_id = 10086;
    let order_no = "ORD20260618001";
    info!(
        user_id,
        order_no,
        pay_money = 99.99,
        "用户下单成功,携带自定义业务字段"
    );

    // ===================== Span 链路追踪(核心特色:记录一段代码执行区间) =====================
    // 创建顶层span,记录函数区间,附加业务标识
    let root_span = span!(Level::INFO, "process_order", order_no = "ORD20260618001");
    // enter() 进入区间,离开自动drop打印结束、计算耗时
    let _enter = root_span.enter();

    info!("开始校验订单参数");
    inner_logic(); // 嵌套span演示
    info!("订单处理完成");

    // 离开root_span,自动打印区间耗时
}

/// 嵌套子Span,模拟多层业务调用链路
fn inner_logic() {
    let sub_span = span!(Level::DEBUG, "pay_check", pay_channel = "alipay");
    let _enter = sub_span.enter();

    debug!("校验支付渠道合法性");
    warn!("支付渠道延迟,响应缓慢");
}

请添加图片描述

四、运行方式

方式1:默认只打印 info/warn/error

cargo run

方式2:开启全部日志(trace/debug都输出,开发调试)

# Linux/macOS
RUST_LOG=trace cargo run

# Windows PowerShell
$env:RUST_LOG="trace"; cargo run

方式3:只打印错误日志(生产环境)

RUST_LOG=error cargo run

tracing 是 Rust 官方推荐的现代化日志/链路追踪库,替代老旧 log 库,核心优势:分级日志、结构化KV日志、Span区间链路追踪、多输出层、环境变量动态控级别,广泛用于后端服务、中间件。

一、Cargo 依赖前置说明

[dependencies]
# 核心日志/span定义
tracing = "0.1"
# 日志输出、格式化、环境过滤、注册器
tracing-subscriber = "0.3"

导入包作用拆分

use tracing::{debug, error, info, span, trace, warn, Level};
  • trace/debug/info/warn/error:5个级别日志宏,直接打印日志
  • span!:创建追踪区间(链路追踪核心)
  • Level:日志级别枚举
use tracing_subscriber::{fmt, EnvFilter};
  • fmt:控制台格式化输出层(彩色、时间、文件行号)
  • EnvFilter:通过环境变量 RUST_LOG 动态控制日志打印级别

二、初始化流程详解(main 第一大块)

tracing_subscriber::registry()
    .with(fmt::layer()
        .with_ansi(true)
        .with_target(true)
        .with_timer(fmt::time::LocalTime::rfc_3339())
    )
    .with(EnvFilter::from_default_env())
    .init();

tracing 架构核心:Registry(注册器)+ Layer(输出层),支持同时多输出(控制台+文件+Jaeger链路追踪)。

1. registry() 全局注册中心

全局单例管理器,所有日志、Span都会交给注册器分发到各个Layer,程序只能调用一次 .init(),多次调用会panic。

2. fmt::layer() 控制台输出层配置

fmt::layer()
    .with_ansi(true)        // 开启终端彩色
    .with_target(true)      // 打印日志所属模块/文件名
    .with_timer(LocalTime::rfc_3339()) // 时间格式
  • .with_ansi(true):不同级别日志区分颜色(ERROR红、WARN黄、INFO白、DEBUG灰),Windows旧终端可关闭
  • .with_target(true):日志行末尾打印 src/main.rs:30 代码位置,线上排错必备
  • rfc_3339:标准ISO时间格式 2026-06-18T16:30:20.123+08:00,带时区,比简易时间更规范

3. EnvFilter::from_default_env() 动态级别过滤

从系统环境变量 RUST_LOG 读取日志过滤规则,无需改代码、重启即可调整日志粒度
默认规则:未设置 RUST_LOG 时,只打印 INFO/WARN/ERRORtrace/debug 屏蔽。

常用环境变量配置示例
# 只打印INFO及以上(默认)
export RUST_LOG=info
# 全局开启debug
export RUST_LOG=debug
# 全局最细粒度trace
export RUST_LOG=trace
# 单独只开启当前包debug,第三方库仅info
export RUST_LOG=my_crate=debug,info
# 屏蔽某个模块错误
export RUST_LOG=info,sqlx=warn

4. .init() 全局生效

完成注册,整个进程所有日志宏、Span都会走上面配置的输出规则。


三、五大日志分级:trace / debug / info / warn / error

trace!("最细粒度调试日志,默认不会打印");
debug!("调试日志:变量打印、内部流程");
info!("普通业务信息:服务启动、请求正常完成");
warn!("警告日志:非致命异常,如库存不足");
error!("错误日志:程序异常、数据库失败");

级别优先级(从低到高):
Trace < Debug < Info < Warn < Error
过滤规则:设置级别后,等于及更高级别的日志才输出

  1. Trace 追踪:最细,循环、底层函数入参,生产环境默认关闭
  2. Debug 调试:开发调试用,打印变量、中间流程,线上一般关闭
  3. Info 业务正常:服务启动、接口请求成功、下单成功,线上默认开启
  4. Warn 警告:业务可自愈异常,不中断流程(超时、库存紧张)
  5. Error 错误:功能失败,数据库报错、接口调用失败,必须打印

四、结构化KV日志(业务埋点核心)

let user_id = 10086;
let order_no = "ORD20260618001";
info!(
    user_id,
    order_no,
    pay_money = 99.99,
    "用户下单成功,携带自定义业务字段"
);

传统log库缺陷

只能打印拼接字符串,字段无法结构化检索;
tracing 支持独立键值对,输出JSON日志时会单独解析字段,日志系统(ELK/Loki)可直接按 user_id、order_no 检索。

两种传参写法

  1. 已有变量直接写变量名:user_id, order_no(自动key=变量名)
  2. 字面量自定义键值:pay_money = 99.99

输出效果示例

2026-06-18T16:35:00+08:00  INFO src/main.rs:40 user_id=10086 order_no="ORD20260618001" pay_money=99.99 用户下单成功,携带自定义业务字段

Span 代表一段有起止时间的代码区间,用于记录调用链路、自动统计执行耗时,模拟微服务链路追踪。
完整示例分为顶层Span + 嵌套子Span

1. 顶层订单根Span

let root_span = span!(Level::INFO, "process_order", order_no = "ORD20260618001");
let _enter = root_span.enter();

span! 宏参数拆解

span!(日志级别, span名称, 自定义KV业务字段...)
span!(Level::INFO, "process_order", order_no = "xxx")
  • process_order:Span标识名,代表「处理订单」整个流程
  • order_no:绑定整个区间的全局字段,区间内所有日志自动携带该字段

.enter() 进入区间机制

  • 调用 .enter() 返回守卫变量 _enter变量生命周期控制Span区间
  • 守卫离开作用域(函数结束、变量销毁)时自动关闭Span,打印区间总耗时
  • 下划线 _enter 标记“不使用该变量,但不能丢弃”,丢弃会立刻关闭Span

区间内日志自动继承Span字段

info!("开始校验订单参数");
inner_logic();
info!("订单处理完成");

这两行日志会自动带上根Span的 order_no,无需重复传参,链路上下文自动透传。

2. 嵌套子Span inner_logic

fn inner_logic() {
    let sub_span = span!(Level::DEBUG, "pay_check", pay_channel = "alipay");
    let _enter = sub_span.enter();

    debug!("校验支付渠道合法性");
    warn!("支付渠道延迟,响应缓慢");
}
  • 子Span pay_check 嵌套在根Span内部,形成层级链路:process_order -> pay_check
  • 子Span拥有自己独立字段 pay_channel="alipay"
  • 子Span内日志同时携带父Span字段 + 自身Span字段
  • 函数执行完毕,_enter 销毁,自动打印支付校验这段逻辑耗时

3. Span自动输出内容

当守卫变量销毁时,会自动打印一条Span结束日志,包含:

  1. Span名称、所有绑定KV
  2. 该代码块执行耗时
  3. 父子Span层级关系

真实输出效果示意

INFO process_order{order_no="ORD20260618001"}: src/main.rs:48: 开始校验订单参数
DEBUG process_order{order_no="ORD20260618001"}:pay_check{pay_channel="alipay"}: src/main.rs:58: 校验支付渠道合法性
WARN process_order{order_no="ORD20260618001"}:pay_check{pay_channel="alipay"}: src/main.rs:59: 支付渠道延迟,响应缓慢
INFO process_order{order_no="ORD20260618001"}: src/main.rs:51: 订单处理完成
INFO process_order{order_no="ORD20260618001"}: finished; time=1.2ms

Span 业务使用场景

  1. 单函数耗时统计:自动打印代码块执行时间,不用手动 Instant 计时
  2. 接口全链路追踪:HTTP请求创建顶层Span,数据库、缓存、第三方调用创建子Span
  3. 日志上下文透传:整个请求链路自动携带 request_id、user_id,每条日志不用重复传参
  4. 微服务链路对接:搭配 tracing-opentelemetry 上报Jaeger、SkyWalking分布式追踪

六、核心优势总结(对比传统 log 库)

  1. 分级可控:环境变量动态调整日志级别,无需改代码
  2. 结构化日志:原生支持KV字段,适配日志检索平台
  3. Span链路追踪:自动记录代码区间、耗时、调用层级,log库无法实现
  4. 多Layer扩展:可同时输出控制台、文件、OpenTelemetry链路系统
  5. 上下文自动透传:Span内所有日志自动继承绑定的业务参数,减少重复代码

七、生产环境优化拓展

  1. 输出JSON日志:fmt::layer().json(),便于日志采集器解析
  2. 文件落盘:搭配 tracing-appender 分割日志文件
  3. 分布式追踪:引入 tracing-opentelemetry 上报链路数据
  4. 过滤精细化:按模块单独控制日志级别,关闭第三方库冗余debug
  5. 异步日志:使用非阻塞Layer,避免打印日志阻塞业务逻辑

五、核心API详细说明

1. 五级日志宏(由低到高)

  • trace!():最细,函数入参、循环细节,线上默认关闭
  • debug!():开发调试,中间变量、流程分支
  • info!():正常业务流程,服务启动、下单、请求成功
  • warn!():可恢复异常,资源紧张、即将过期
  • error!():不可恢复错误,数据库崩溃、接口500

2. 结构化KV字段语法

// 格式:字段名 = 值,放在消息前面
info!(user_id = 123, "操作完成");
// 变量简写:直接写变量名
let user_id = 123;
info!(user_id, "操作完成");

日志会附带结构化数据,日志平台可直接按 user_id 检索。

3. Span 链路追踪(tracing 最大优势)

// span!(级别, span名称, 自定义kv...)
let sp = span!(Level::INFO, "handle_request", req_id = "xxxx");
let _guard = sp.enter(); // 进入区间

// 区间内所有日志都会归属这个span
info!("处理请求中");

// _guard 离开作用域自动销毁,自动打印:区间起止时间、耗时

多层嵌套自动展示缩进,一眼看清调用链路。

4. tracing-subscriber 配置项

  1. .with_ansi(true):控制台彩色日志,Windows/macOS/Linux兼容
  2. .with_target(true):打印日志所在文件、模块
  3. .with_timer(...):自定义时间格式(本地时间/UTC)
  4. EnvFilter::from_default_env():读取环境变量 RUST_LOG 动态控制日志级别,不用改代码重启服务。

六、拓展常用场景

场景1:输出JSON结构化日志(生产对接ELK)

修改初始化代码:

tracing_subscriber::registry()
    .with(fmt::layer()
        .json() // 开启JSON格式
        .with_target(true)
    )
    .with(EnvFilter::from_default_env())
    .init();

输出标准JSON,包含时间、日志级别、span、自定义字段,可直接日志系统解析。

场景2:日志写入文件持久化

需要额外 fmt::writer::MakeWriter,用于线上保存日志文件,支持按大小分割。

场景3:Axum Web框架全局请求追踪

在中间件为每个请求生成唯一 request_id span,所有接口日志自动带上请求ID,排查线上请求全链路。

七、对比旧log库优势

  1. log 只有纯文本打印,无链路追踪、无span、无结构化KV
  2. tracing 天然支持异步,多线程日志不乱序
  3. 分层过滤更灵活,可单独关闭某个模块日志
  4. 支持链路耗时自动统计,无需手动写时间戳计算耗时

更多推荐