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

如何在Debug级别格式化SQLx输出的数据库语句?

用tracing自定义格式化处理sqlx的数据库日志

要把sqlx输出的多行带占位符的SQL日志转成紧凑的带实际参数的单行格式,可以通过自定义tracing的日志格式化逻辑实现,步骤如下:

  • 1. 拦截sqlx的目标日志事件
    借助tracing-subscriber的fmt::Layer,实现自定义FormatEvent trait,只针对sqlx::query的DEBUG级别事件做特殊处理,其余日志保持默认格式输出。

  • 2. 提取并处理核心字段
    从事件中提取db.statement(原始SQL语句)和db.params(绑定的参数值)两个关键字段:

    • 先将原始SQL中的换行、多余空格去除,压缩为单行格式
    • 把SQL中的$1、$2这类占位符,替换为对应的实际参数值(注意根据参数类型添加格式,比如字符串加单引号,数字直接保留)
  • 3. 示例代码实现
    以下是简化的可运行示例:

    use tracing_subscriber::{fmt, layer::SubscriberExt, Registry};
    use tracing::{Event, Level};
    use std::fmt::Write;
    
    struct SqlxCompactFmt;
    
    impl fmt::FormatEvent for SqlxCompactFmt {
        fn format_event(
            &self,
            ctx: &fmt::FmtContext<'_>,
            mut writer: fmt::format::Writer<'_>,
            event: &Event<'_>,
        ) -> std::fmt::Result {
            // 仅处理sqlx::query的DEBUG日志
            if event.metadata().target() == "sqlx::query" && event.metadata().level() == &Level::DEBUG {
                let mut raw_sql = String::new();
                let mut params = Vec::new();
    
                // 提取日志字段
                event.record_fields(|field| {
                    match field.name() {
                        "db.statement" => {
                            if let Some(s) = field.value().as_str() {
                                // 压缩SQL为单行
                                raw_sql = s.replace('\n', " ").trim().replace("  ", " ").to_string();
                            }
                        }
                        "db.params" => {
                            if let Some(p) = field.value().as_debug() {
                                params.push(format!("{:?}", p));
                            }
                        }
                        _ => {}
                    }
                });
    
                // 替换占位符为实际参数
                for (idx, param) in params.iter().enumerate() {
                    let placeholder = format!("${}", idx + 1);
                    raw_sql = raw_sql.replace(&placeholder, param);
                }
    
                // 输出格式化后的SQL
                writeln!(writer, "{}", raw_sql)?;
                Ok(())
            } else {
                // 其他日志用默认格式输出
                fmt::format::DefaultFormat.format_event(ctx, writer, event)
            }
        }
    }
    
    fn main() {
        // 初始化自定义日志订阅器
        let fmt_layer = fmt::layer().event_format(SqlxCompactFmt);
        let subscriber = Registry::default().with(fmt_layer);
        tracing::subscriber::set_global_default(subscriber).unwrap();
    
        // 此处编写你的sqlx数据库操作代码
    }
    
  • 4. 关键注意事项

    • 确保Cargo.toml中sqlx启用了tracing特性:sqlx = { version = "0.7", features = ["tracing", "postgres", ...] }
    • 如需更精准的参数格式化(比如日期、布尔值的特殊处理),可在替换占位符时增加类型判断逻辑
    • 若只需要输出格式化后的SQL,可删除默认日志的处理分支,仅保留自定义输出逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 16:45:40