diff --git a/README.md b/README.md index d118e78f..2e9eb1ee 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ about this topic. * [pyo3-built](https://github.com/PyO3/pyo3-built) _Simple macro to expose metadata obtained with the [`built`](https://crates.io/crates/built) crate as a [`PyDict`](https://pyo3.github.io/pyo3/pyo3/struct.PyDict.html)_ * [rust-numpy](https://github.com/PyO3/rust-numpy) _Rust binding of NumPy C-API_ * [dict-derive](https://github.com/gperinazzo/dict-derive) _Derive FromPyObject to automatically transform Python dicts into Rust structs_ + * [pyo3-log](https://github.com/vorner/pyo3-log) _Bridge from Rust to Python logging_ ## Examples diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index f4bdea66..8af59a04 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -16,3 +16,4 @@ - [Appendix A: PyO3 and rust-cpython](rust_cpython.md) - [Appendix B: Migration Guide](migration.md) - [Appendix C: Trait bounds](trait_bounds.md) +- [Appendix D: Logging](logging.md) diff --git a/guide/src/logging.md b/guide/src/logging.md new file mode 100644 index 00000000..a9078b5d --- /dev/null +++ b/guide/src/logging.md @@ -0,0 +1,68 @@ +# Logging + +It is desirable if both the Python and Rust parts of the application end up +logging using the same configuration into the same place. + +This section of the guide briefly discusses how to connect the two languages' +logging ecosystems together. The recommended way for Python extension modules is +to configure Rust's logger to send log messages to Python using the `pyo3-log` +crate. For users who want to do the opposite and send Python log messages to +Rust, see the note at the end of this guide. + +## Using `pyo3-log` to send Rust log messages to Python + +The [pyo3-log] crate allows sending the messages from the Rust side to Python's +[logging] system. This is mostly suitable for writing native extensions for +Python programs. + +Use [`pyo3_log::init`][init] to install the logger in its default configuration. +It's also possible to tweak its configuration (mostly to tune its performance). + +```rust +use log::info; +use pyo3::prelude::*; +use pyo3::wrap_pyfunction; + +#[pyfunction] +fn log_something() { + // This will use the logger installed in `my_module` to send the `info` + // message to the Python logging facilities. + info!("Something!"); +} + +#[pymodule] +fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> { + // A good place to install the Rust -> Python logger. + pyo3_log::init(); + + m.add_wrapped(wrap_pyfunction!(log_something))?; + Ok(()) +} +``` + +Then it is up to the Python side to actually output the messages somewhere. + +```python +import logging +import my_module + +FORMAT = '%(levelname)s %(name)s %(asctime)-15s %(filename)s:%(lineno)d %(message)s' +logging.basicConfig(format=FORMAT) +logging.getLogger().setLevel(logging.INFO) +my_module.log_something() +``` + +It is important to initialize the Python loggers first, before calling any Rust +functions that may log. This limitation can be worked around if it is not +possible to satisfy, read the documentation about [caching]. + +## The Python to Rust direction + +To best of our knowledge nobody implemented the reverse direction yet, though it +should be possible. If interested, the `pyo3` community would be happy to +provide guidance. + +[logging]: https://docs.python.org/3/library/logging.html +[pyo3-log]: https://crates.io/crates/pyo3-log +[init]: https://docs.rs/pyo3-log/*/pyo3_log/fn.init.html +[caching]: https://docs.rs/pyo3-log/*/pyo3_log/#performance-filtering-and-caching