Rust日志集成库sentry-slog的使用:通过slog适配器实现与Sentry错误监控系统的无缝对接

Rust日志集成库sentry-slog的使用:通过slog适配器实现与Sentry错误监控系统的无缝对接

Sentry Logo

Sentry slog 集成库。

主要提供 SentryDrain,它包装另一个 slog::Drain 并可以配置为将 slog::Record 转发到 Sentry。 SentryDrain 可用于创建 slog::Logger

该集成还支持 slog::KV 键值对。它们将分别被添加到面包屑的 data 或事件的 extra 属性中。

示例

use sentry_slog::SentryDrain;

let _sentry = sentry::init(());

let drain = SentryDrain::new(slog::Discard);
let root = slog::Logger::root(draft, slog::o!("global_kv" => 1234));

slog::info!(root, "recorded as breadcrumb"; "breadcrumb_kv" => Some("breadcrumb"));
slog::warn!(root, "recorded as regular event"; "event_kv" => "event");

let breadcrumb = &captured_event.bbreadcrumbs.as_ref()[0];
assert_eq!(
    breadcrumb.message.as_deref(),
    Some("recorded as breadcrumb")
);
assert_eq!(breadcrumb.data["breadcrumb_kv"], "breadcrumb");
assert_eq!(breadcrumb.data["global_kv"], 1234);

assert_eq!(
    captured_event.message.as_deref(),
    Some("recorded as regular event")
);
assert_eq!(captured_event.extra["event_kv"], "event");
assert_eq!(captured_event.extra["global_kv"], 1234);

slog::crit!(root, "recorded as exception event");

assert_eq!(
    captured_event.message.as_deref(),
    Some("recorded as exception event")
);

Drain 还可以通过 filtermapper 进行定制:

use sentry_slog::{exception_from_record, LevelFilter, RecordMapping, SentryDrain};

let drain = SentryDrain::new(slog::Discard)
    .filter(|level| match level {
        slog::Level::Critical | slog::Level::Error => LevelFilter::Event,
        _ => LevelFilter::Ignore,
    })
    .mapper(|record, kv| match record.level() {
        slog::Level::Critical | slog::Level::Error => {
            RecordMapping::Event(exception_from_record(record, kv))
        }
        _ => RecordMapping::Ignore,
    });

当指定 mapper 时,也应提供相应的 filter

完整示例代码

use sentry_slog::{exception_from_record, LevelFilter, RecordMapping, SentryDrain};
use slog::{o, Discard, Logger};
use std::sync::Mutex;

fn main() {
    // 初始化Sentry
    let _sentry = sentry::init((
        "https://your-public-key@sentry.io/your-project-id",
        sentry::ClientOptions {
            release: sentry::release_name!(),
            ..Default::default()
        },
    ));

    // 创建带过滤器和映射器的SentryDrain
    let drain = SentryDrain::new(Discard)
        .filter(|level| match level {
            slog::Level::Critical | slog::Level::Error => LevelFilter::Event,
            slog::Level::Warning => LevelFilter::Breadcrumb,
            _ => LevelFilter::Ignore,
        })
        .mapper(|record, kv| match record.level() {
            slog::Level::Critical | slog::Level::Error => {
                RecordMapping::Event(exception_from_record(record, kv))
            }
            slog::Level::Warning => RecordMapping::Breadcrumb,
            _ => RecordMapping::Ignore,
        });

    // 创建日志记录器
    let root = Logger::root(
        Mutex::new(drain).fuse(),
        o!("version" => env!("CARGO_PKG_VERSION")),
    );

    // 记录不同级别的日志
    slog::info!(root, "This is an info message"; "key" => "value");
    slog::warn!(root, "This is a warning message"; "warning_key" => 42);
    slog::error!(root, "This is an error message"; "error_key" => true);
    slog::crit!(root, "This is a critical message"; "critical_key" => "important");

    // 确保所有事件都发送到Sentry
    sentry::flush(std::time::Duration::from_secs(2));
}

许可证: MIT


1 回复

Rust日志集成库sentry-slog的使用指南

sentry-slog是一个将Rust的slog日志系统与Sentry错误监控平台无缝集成的适配器库。它允许开发者通过现有的slog日志基础设施将错误和日志发送到Sentry。

主要特性

  • slog日志记录转发到Sentry
  • 支持Sentry的上下文信息(tags, extra data等)
  • 与现有slog日志系统无缝集成
  • 支持错误堆栈跟踪

使用方法

1. 添加依赖

首先,在Cargo.toml中添加依赖:

[dependencies]
slog = "2.7"
sentry = "0.30"
sentry-slog = "0.30"

2. 基本集成示例

use slog::{info, o, Drain, Logger};
use sentry::ClientOptions;
use sentry_slog::SentryDrain;

fn main() {
    // 初始化Sentry
    let _sentry = sentry::init(ClientOptions {
        dsn: Some("https://your-public-key@sentry.io/your-project-id".parse().unwrap()),
        ..Default::default()
    });

    // 创建slog日志记录器
    let drain = SentryDrain::new(slog::Discard);
    let logger = Logger::root(drain.fuse(), o!("version" => "1.0.0"));

    // 记录日志
    info!(logger, "Application started"; "module" => "init");
    
    // 模拟错误
    match some_operation() {
        Ok(_) => info!(logger, "Operation succeeded"),
        Err(e) => {
            error!(logger, "Operation failed"; "error" => e.to_string());
        }
    }
}

fn some_operation() -> Result<(), Box<dyn std::error::Error>> {
    Err("Something went wrong".into())
}

3. 高级配置

use sentry_slog::LevelFilter;

// 创建自定义的SentryDrain,只记录Warn及以上级别的日志
let sentry_drain = SentryDrain::new(slog::Discard)
    .filter(|record| record.level().is_at_least(slog::Level::Warning))
    .ignore_crates(&["tokio", "hyper"]); // 忽略特定crate的日志

let logger = Logger::root(sentry_drain.fuse(), o!());

4. 添加上下文信息

use sentry::configure_scope;

configure_scope(|scope| {
    scope.set_tag("environment", "production");
    scope.set_extra("hostname", "server-1".into());
});

info!(logger, "Processing request"; 
    "user_id" => 42, 
    "request_id" => "abc123");

最佳实践

  1. 日志级别过滤:建议只将Warn及以上级别的日志发送到Sentry,避免产生过多噪音
  2. 敏感信息:不要在日志中包含敏感信息,Sentry会永久存储这些数据
  3. 上下文丰富:尽量为错误添加上下文信息,便于调试
  4. 错误分类:使用tags对错误进行分类,便于在Sentry中筛选

错误处理

sentry-slog会自动将slog记录的错误转换为Sentry事件。对于Rust错误类型,它会自动捕获堆栈跟踪(如果启用了backtrace)。

error!(logger, "Database operation failed"; 
    "error" => err.to_string(), 
    "query" => query);

这将在Sentry中创建一个包含错误详情、堆栈跟踪和额外上下文信息的事件。

性能考虑

sentry-slog设计为非阻塞,日志记录不会阻塞应用程序主线程。所有Sentry通信都在后台线程中处理。

通过合理配置日志级别过滤和忽略规则,可以最小化性能影响。

完整示例demo

以下是一个完整的示例,展示了如何使用sentry-slog进行日志记录和错误监控:

use slog::{error, info, o, Drain, Logger};
use sentry::{ClientOptions, types::Dsn};
use sentry_slog::SentryDrain;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // 1. 初始化Sentry
    let dsn: Dsn = "https://your-public-key@sentry.io/your-project-id".parse()?;
    let _sentry = sentry::init(ClientOptions {
        dsn: Some(dsn),
        release: Some("myapp@1.0.0".into()),
        ..Default::default()
    });

    // 2. 配置Sentry上下文
    sentry::configure_scope(|scope| {
        scope.set_tag("environment", "production");
        scope.set_extra("hostname", "server-1".into());
    });

    // 3. 创建slog日志记录器
    let base_drain = slog::Discard; // 基础drain,可以是任何slog兼容的drain
    let sentry_drain = SentryDrain::new(base_drain)
        .filter(|record| record.level().is_at_least(slog::Level::Warning)) // 只记录Warning及以上级别
        .ignore_crates(&["tokio"]); // 忽略tokio的日志

    let logger = Logger::root(
        sentry_drain.fuse(), 
        o!("version" => "1.0.0", "service" => "myapp")
    );

    // 4. 记录不同级别的日志
    info!(logger, "Application starting"; "module" => "init");
    
    // 5. 模拟业务操作和错误处理
    match process_request(&logger, 42) {
        Ok(_) => info!(logger, "Request processed successfully"),
        Err(e) => {
            error!(logger, "Request processing failed";
                "error" => e.to_string(),
                "user_id" => 42
            );
        }
    }

    Ok(())
}

fn process_request(logger: &Logger, user_id: u64) -> Result<(), Box<dyn Error>> {
    info!(logger, "Processing request"; "user_id" => user_id);
    
    // 模拟可能失败的操作
    if user_id % 2 == 0 {
        Ok(())
    } else {
        Err("Invalid user ID".into())
    }
}

这个完整示例展示了:

  1. Sentry的初始化和配置
  2. 上下文字段的设置
  3. slog记录器的创建和配置
  4. 不同级别的日志记录
  5. 业务操作中的错误处理和日志记录
  6. 结构化日志字段的使用

注意在实际使用时,需要替换Sentry DSN为你自己的项目DSN。

回到顶部