Rust日志记录库pyo3-pylogger的使用:Python与Rust无缝集成的日志记录解决方案

pyo3-pylogger

pyo3-pylogger 是一个为嵌入Python应用的pyo3项目提供日志记录的库,它使用Python的logging模块。

特性

  • 将Python的logging模块与Rust的log crate集成
  • 通过logging的extra字段支持结构化日志记录(需要kvtracing-kv特性)
  • 与Rust的tracing库集成(需要tracing特性)

使用示例

use log::{info, warn};
use pyo3::{ffi::c_str, prelude::*};

fn main() {
    // 使用Python logger注册主机处理器,提供logger目标
    pyo3_pylogger::register("example_application_py_logger");

    // 初始化logger
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
    // 展示Rust中的logger工作
    info!("Just some normal information!");
    warn!("Something spooky happened!");

    // 让pyo3设置嵌入式Python解释器
    pyo3::prepare_freeththreaded_python();
    Python::with_gil(|py| {
        // Python代码现在可以像往常一样`import logging`
        py.run(
            c_str!(
                r#"
import logging
logging.getLogger().setLevel(0)
logging.debug('DEBUG')
logging.info('INFO')
logging.warning('WARNING')
logging.error('ERROR')
logging.getLogger('foo.bar.baz').info('INFO')"#
            ),
            None,
            None,
        )
        .unwrap();
    })
}

输出

[2025-03-28T01:12:29Z INFO  helloworld] Just some normal information!
[2025-03-28T01:12:29Z WARN  helloworld] Something spooky happened!
[2025-03-28T01:12:29Z DEBUG example_application_py_logger] DEBUG
[2025-03-28T01:12:29Z INFO  example_application_py_logger] INFO
[2025-03-28T01:12:29Z WARN  example_application_py_logger] WARNING
[2025-03-28T01:12:29Z ERROR example_application_py_logger] ERROR
[2025-03-28T01:12:29Z INFO  example_application_py_logger::foo::bar::baz] INFO

结构化日志记录

要启用结构化日志记录支持,在Cargo.toml中添加kv特性:

[dependencies]
pyo3-pylogger = { version = "0.4", features = ["kv"] }

然后可以使用Python的extra参数传递结构化数据:

logging.info("Processing order", extra={"order_id": "12345", "amount": 99.99})

在Rust中使用结构化日志订阅者时,这些键值对将被正确捕获,例如:

[2025-03-28T01:12极客时间
29Z INFO  example_application_py_logger] Processing order order_id=12345 amount=99.99

Tracing支持

要启用与Rust的tracing库集成,在Cargo.toml中添加tracing特性:

[dependencies]
pyo3-pylogger = { version = "0.4", default-features = false, features = ["tracing"] }

启用tracing特性后,Python日志将被转发到活跃的tracing订阅者:

use tracing::{info, warn};
use pyo3::{ffi::c_str, prelude::*};

fn main() {
    // 使用Python logger注册tracing处理器
    pyo3_pylogger::register_tracing("example_application_py_logger");

    // 初始化tracing订阅者
    tracing_subscriber::fmt::init();

    // 来自Rust的tracing事件
    info!("Tracing information from Rust");

    // Python日志将被tracing订阅者捕获
    pyo3::prepare_freethreaded_python();
    Python::with_gil(|py| {
        py.run(
            c_str!(
                r#"
import logging
logging.getLogger().setLevel极客时间
(0)
logging.info('This will be captured by tracing')"#
            ),
            None,
            None,
        )
        .unwrap();
    })
}

使用Tracing的结构化数据

tracing特性自动支持Python的extra字段用于结构化数据。然而,KV字段被json序列化,不作为tracing属性可用。这是tracing库的限制,不是本crate特有的。

特性标志

  • kv: 通过Python的extra字段启用结构化日志记录支持。这为log crate的键值系统添加了支持。
  • tracing: 启用与Rust的tracing库的集成。
  • tracing-kv: 通过Python的extra字段启用结构化日志记录支持,并与Rust的tracing库集成。

完整示例

下面是一个结合了基本日志记录和结构化日志记录的完整示例:

use log::{info, warn};
use pyo3::{ffi::c_str, prelude::*};

fn main() {
    // 注册Python日志处理器
    pyo3_pylogger::register("my_app_logger");

    // 初始化env_logger
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();

    // Rust日志示例
    info!("Starting the application");
    warn!("This is a warning from Rust");

    // 初始化Python解释器
    pyo3::prepare_freethreaded_python();
    Python::with_gil(|py| {
        // 运行Python代码
        py.run(
            c_str!(
                r#"
import logging

# 设置日志级别
logging.basicConfig(level=logging.DEBUG)

# 普通日志
logging.debug('Debug message from Python')
logging.info('Info message from Python')
logging.warning('Warning message from Python')
logging.error('Error message from Python')

# 结构化日志
logging.info("User login", extra={"username": "john_doe", "ip": "192.168.1.1"})
logging.warning("Payment processed", extra={"amount": 99.99, "currency": "USD"})

# 模块特定日志
module_logger = logging.getLogger('my_app.module')
module_logger.info('Module specific message')
"#
            ),
            None,
            None,
        )
        .unwrap();
    });

    info!("Application finished");
}

这个示例展示了:

  1. Rust和Python之间的日志集成
  2. Python中的结构化日志记录
  3. 模块特定的日志记录
  4. 不同日志级别的使用

输出将包含来自Rust和Python的日志消息,包括结构化数据。


1 回复

Rust日志记录库pyo3-pylogger的使用:Python与Rust无缝集成的日志记录解决方案

pyo3-pylogger是一个基于PyO3的Rust日志记录库,它允许Rust代码使用Python的日志系统进行日志记录,实现了Python和Rust之间的无缝日志集成。

主要特性

  • 将Rust的日志记录转发到Python的日志系统
  • 完全兼容Python的标准日志配置
  • 支持所有标准日志级别
  • 保持日志格式和处理器的一致性

安装方法

在Cargo.toml中添加依赖:

[dependencies]
pyo3-pylogger = "0.3"
log = "0.4"
pyo3 = { version = "0.18", features = ["extension-module"] }

基本使用方法

1. 初始化日志记录器

首先需要在Rust中初始化pylogger:

use pyo3_pylogger::PyLogger;
use log::LevelFilter;

#[pyfunction]
pub fn init_logging(py: Python) -> PyResult<()> {
    PyLogger::new(py)
        .with_log_level(LevelFilter::Info)
        .install()?;
    Ok(())
}

2. 在Python中调用

import your_rust_module

# 初始化日志
your_rust_module.init_logging()

# 配置Python日志
import logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

3. 在Rust中记录日志

use log::{info, warn, error};

pub fn do_something() {
    info!("This is an info message from Rust");
    warn!("This is a warning from Rust");
    error!("This is an error from Rust");
}

高级用法

自定义日志记录器名称

PyLogger::new(py)
    .with_logger_name("my_rust_module")
    .install()?;

设置不同的日志级别

PyLogger::new(py)
    .with_log_level(LevelFilter::Debug)
    .install()?;

在Python中获取Rust日志

def test_rust_logging():
    import logging
    logger = logging.getLogger('my_rust_module')
    
    # 添加处理器
    handler = logging.StreamHandler()
    formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    
    # 调用Rust函数
    your_rust_module.do_something()

完整示例

Rust部分 (lib.rs)

use pyo3::prelude::*;
use pyo3_pylogger::PyLogger;
use log::{info, warn, error, LevelFilter};

#[pyfunction]
pub fn init_logging(py: Python) -> PyResult<()> {
    PyLogger::new(py)
        .with_logger_name("rust_component")
        .with_log_level(LevelFilter::Debug)
        .install()?;
    Ok(())
}

#[pyfunction]
pub fn perform_calculation(value: i32) -> i32 {
    info!("Starting calculation with value: {}", value);
    
    if value < 0 {
        warn!("Negative value provided, might cause issues");
    }
    
    let result = value * 2;
    info!("Calculation complete, result: {}", result);
    
    result
}

#[pymodule]
fn rust_logging(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(init_logging, m)?)?;
    m.add_function(wrap_pyfunction!(perform_calculation, m)?)?;
    Ok(())
}

Python部分

import logging
from rust_logging import init_logging, perform_calculation

# 配置日志
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# 初始化Rust日志
init_logging()

# 使用Rust函数
result = perform_calculation(42)
print(f"Result: {result}")

# 测试错误情况
perform_calculation(-5)

注意事项

  1. 确保在调用任何Rust日志记录之前初始化PyLogger
  2. Rust和Python的日志级别是独立的,需要在两边都进行适当配置
  3. 在异步上下文中使用时需要注意线程安全
  4. 大型日志消息可能会影响性能,建议在生产环境中适当调整日志级别

通过pyo3-pylogger,你可以实现Python和Rust代码之间的统一日志管理,简化了混合应用的日志记录和调试过程。

回到顶部