#error-logging #log-error #proc-macro #logging-tracing #match #statement #wrap

wrap-match

一个过程宏,用于将函数包装在 match 语句中,以便轻松进行错误记录

6 个稳定版本

1.0.7 2023年7月31日
1.0.5 2023年7月30日
1.0.4 2023年5月17日
1.0.3 2023年5月16日
1.0.2 2023年5月12日

#336Rust 模式


doc-sync 中使用

MIT 许可协议

25KB
52

问题

如果您想记录错误发生时以及错误原因,您可能会发现自己在每个可能出现的错误上使用 match 语句,而不是使用 ? 操作符。

这会导致代码非常冗长。编写和维护都很痛苦。

现在介绍 wrap-match!

wrap-match 是一个属性宏,可以将您的函数包装在 match 语句中。此外,它还将在所有使用 ? 操作符(即 try 表达式)的语句中附加丰富的错误信息。

wrap-match 支持日志和跟踪。它默认使用日志,但如果启用了跟踪功能,它将使用跟踪。有关更多信息,请参阅 tracing 支持

注意

wrap-match 使用 logtracing crate 来记录成功和错误消息。它不公开 logtracing crate 以供展开函数使用;您必须自己依赖它们。

此外,除非您使用日志实现或 tracing 订阅者,否则不会显示任何消息。 对于 log,我推荐 env_logger,但您可以在 此处 找到完整的列表。对于 tracing,我推荐 tracing-subscriber,但您可以在 此处 找到完整的列表。

示例

首先,将以下内容添加到您的 Cargo.toml

[dependencies]
# For log users:
wrap-match = "1"
log = "*"
# You'll also want a logging implementation, for example `env_logger`
# More info here: https://docs.rs/log/#available-logging-implementations

# For tracing users:
wrap-match = { version = "1", features = ["tracing"] }
tracing = "0.1"
# You'll also want a `tracing` subscriber, for example `tracing-subscriber`
# More info here: https://docs.rs//tracing/#related-crates

现在您可以使用 wrap_match 属性宏

#[wrap_match::wrap_match]
fn my_function() -> Result<(), CustomError> {
    Err(CustomError::Error)?; // notice the ?; when the macro is expanded, it will be modified to include line number and expression
    // If you need to return an error, just do `Err(CustomError::Error.into())`
    Ok(())
}

这将扩展为类似以下的内容(通常不包括注释,并且部分输出已简化;如果您想了解宏实际扩展的内容,请使用cargo-expand

fn my_function() -> Result<(), CustomError> {
    // This is where the original function is
    fn _wrap_match_inner_my_function() -> Result<(), WrapMatchError<CustomError>> {
        Err(CustomError::Error)
            .map_err(|e| WrapMatchError {
                // Here, line number and expression are added to the error
                line_and_expr: Some((3, "Err(CustomError::Error)")),
                inner: e.into(), // This is so you can have `Box<dyn Error>` as your error type
            })?;
        // If you need to return an error, just do `Err(CustomError::Error.into())`
        Ok(())
    }

    match _wrap_match_inner_my_function() {
        Ok(r) => {
            ::log::info!("Successfully ran my_function"); // when the tracing feature is enabled, it will use tracing macros instead
            Ok(r)
        }
        Err(e) => {
            if let Some((_line, _expr)) = e.line_and_expr {
                ::log::error!("An error occurred when running my_function (when running `{_expr}` on line {_line}): {:?}", e.inner); // when the tracing feature is enabled, it will use tracing macros instead
            } else {
                ::log::error!("An error occurred when running my_function: {:?}", e.inner); // when the tracing feature is enabled, it will use tracing macros instead
            }
            Err(e.inner)
        }
    }
}

如果我们运行此代码,它将记录以下内容

[ERROR] An error occurred when running my_function (when running `Err(CustomError::Error)` on line 3): Error

如您所见,wrap-match使错误日志非常容易,同时还能记录导致错误的信息。

tracing支持

从wrap-match 1.0.5版本开始,wrap-match支持tracing,前提是启用了tracing功能。wrap-match 不会对span进行任何操作。此外,您将无法在应用wrap-match的功能中手动创建span。这是因为span将在wrap-match记录任何内容之前被丢弃。

要将函数和wrap-match的日志放入一个span中,您必须使用tracing::instrument属性宏。属性宏的顺序很重要;它必须在wrap-match之后

示例

#[wrap_match::wrap_match(success_message = "still in span!")]
#[tracing::instrument] // IMPORTANT: after wrap-match!
fn my_function() -> Result<(), ()> {
    tracing::info!("hello from tracing!");
    Ok(())
}

自定义

wrap-match允许用户自定义成功和错误消息,以及选择是否在成功时记录任何内容。

success_message

成功时记录的消息。

可用的格式参数

  • function:原始函数名称。注意:您只能使用{function};不支持其他格式,如{function:?}
  • 函数参数

默认值:Successfully ran {function}

示例

#[wrap_match::wrap_match(success_message = "{function} ran successfully!! 🎉🎉")]
fn my_function() -> Result<(), CustomError> {
    Ok(())
}

这将记录以下内容

[INFO] my_function ran successfully!! 🎉🎉

error_message

当行和表达式信息可用时,记录错误时的消息。目前,这仅适用于try表达式(有?的语句)。

可用的格式参数

  • function:原始函数名称。注意:您只能使用{function};不支持其他格式,如{function:?}
  • line:错误发生的行。
  • expr:导致错误的表达式。
  • error:错误。
  • 函数参数

默认值:An error occurred when running {function} (caused by `{expr}` on line `{line}): `{error:?}

示例

#[wrap_match::wrap_match(error_message = "oh no, {function} failed! `{expr}` on line {line} caused the error: {error:?}")]
fn my_function() -> Result<(), CustomError> {
    Err(CustomError::Error)?;
    Ok(())
}

这将记录以下内容

[ERROR] oh no, my_function failed! `Err(CustomError::Error)` on line 3 caused the error: Error

error_message_without_info

当行和表达式信息不可用时,记录错误时的消息。这通常在您自己返回错误并使用.into()时触发。

可用的格式参数

  • function:原始函数名称。注意:您只能使用{function};不支持其他格式,如{function:?}
  • error:错误。
  • 函数参数

默认值:An error occurred when running {function}: `{error:?}

示例

#[wrap_match::wrap_match(error_message_without_info = "oh no, {function} failed with this error: {error:?}")]
fn my_function() -> Result<(), CustomError> {
    Err(CustomError::Error.into())
}

这将记录以下内容

[ERROR] oh no, my_function failed with this error: Error

log_success

如果设置为false,则在成功时不会记录任何内容。

默认值:true

示例

#[wrap_match::wrap_match(log_success = false)]
fn my_function() -> Result<(), CustomError> {
    Ok(())
}

这将不记录任何内容。

disregard_result

如果 true,则生成的函数将返回 () 并丢弃 Result 的任何内容。对于 main 函数很有用。

默认值:false

示例

#[wrap_match::wrap_match(disregard_result = true)]
fn main() -> Result<(), CustomError> {
    Ok(())
}

main 函数将被转换为以下形式

fn main() {
    fn _wrap_match_inner_main() -> Result<(), WrapMatchError<CustomError>> {
        Ok(())
    }

    match _wrap_match_inner_main() {
        // the Result would be logged like normal, but it is not returned
    }
}

在消息中使用函数参数

从 wrap-match 1.0.5 版本开始,您可以在消息中使用函数参数,只要它们没有被移动/丢弃。您应该只为引用和实现 Copy 的项使用此功能。

示例

#[wrap_match::wrap_match(success_message = "Success! input = {input}")]
fn my_function(input: i64) -> Result<i64, ()> {
    Ok(input)
}

限制

wrap-match 当前有以下限制

  1. wrap-match 无法用于接受 self 参数的函数实现。如果您需要支持此功能,请创建一个 GitHub 问题说明您的使用情况。 现在已支持!但是,这需要 wrap-match 将内部函数移出生成的函数,因此它将在实现中添加一个新方法。此方法被标记为已弃用,并设置为私有,不会在文档中显示。希望这不会引起任何问题。

  2. wrap-match 只支持 Result。如果您需要支持 Option,请创建一个 GitHub 问题说明您的使用情况。

  3. error_messageerror_message_without_info 只支持使用 DebugDisplay 格式化程序格式化 error。这是因为我们确定使用了哪些格式说明符。如果您需要支持其他格式说明符,请创建一个 GitHub 问题说明您的使用情况。 所有格式参数(除了 function)现在都支持 format! 所支持的所有基本格式(然而,诸如精度、符号、填充、对齐和宽度等特性可能永远不会得到支持)。

  4. wrap-match 无法用于 const 函数。这是因为 log crate 无法在 const 上下文中使用。

如果 wrap-match 对本列表中未列出的内容不起作用,请创建一个 GitHub 问题!

依赖项

~0.5–1MB
~24K SLoC