litellm/litellm-rust/crates/tracing
devin-ai-integration[bot] b0ac23d385
feat(logger): dispatch Python logging through the Rust diagnostics processor (#42616)
* feat(logger): add shared Rust diagnostics and Python logging bridge

* feat(logger): dispatch diagnostic processing through Rust

* chore: regenerate Cargo.lock after rebase

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: allowlist bounded logging tree walkers in recursive detector

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* perf(logger): skip decoding plain access arguments

* test(logger): skip embedded-python logger test when litellm deps are absent

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* style: cargo fmt

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: expect NativeDiagnosticProcessor in the native public surface

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(stub): export NativeDiagnosticProcessor via __new__ in _native.pyi

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(tracing): rename logger crate and document host sink contract

* test(logger): cover exc, stack, and nested extras in the diagnostic filter

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(logger): keep rendered redacted line when template scan flags a key pattern

The blanket REDACTED for a changed msg/color template discarded lines
whose rendered form was already redacted by the same pipeline, e.g.
'password=%s' became 'REDACTED' instead of 'password=REDACTED'. Only
fall back to REDACTED when the rendered form did not change either,
which is where interpolation can mangle the key pattern the scrub
would otherwise see.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* ci(rust): install python deps so the logger bridge test runs

The end-to-end bridge test skipped silently when litellm's Python deps
were absent. uv sync --no-install-project installs them without a
maturin build, and PYTHONPATH makes them visible to the embedded
interpreter

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-22 18:44:15 -07:00
..
src feat(logger): dispatch Python logging through the Rust diagnostics processor (#42616) 2026-09-22 18:44:15 -07:00
tests feat(logger): dispatch Python logging through the Rust diagnostics processor (#42616) 2026-09-22 18:44:15 -07:00
Cargo.toml feat(logger): dispatch Python logging through the Rust diagnostics processor (#42616) 2026-09-22 18:44:15 -07:00
README.md feat(logger): dispatch Python logging through the Rust diagnostics processor (#42616) 2026-09-22 18:44:15 -07:00

Native diagnostic tracing

litellm-tracing connects standard tracing events to a host-provided Sink. It has no Python dependency and does not install a global subscriber

Use the exported debug!, info!, warn!, and error! macros in native code. A host creates a Logger with its sink, uses scope for synchronous operations, and wraps futures with instrument. Instrument spawned futures explicitly because thread-local subscribers do not automatically follow spawned work

Bindings implement litellm_tracing::Sink to connect events to their host runtime:

pub trait Sink: Send + Sync + 'static {
    fn enabled(&self, metadata: &Metadata<'_>) -> bool;
    fn emit(&self, record: &Record);
}

Pass the implementation to litellm_tracing::Logger::new(sink), then call logger.scope(|| litellm_tracing::info!(attempt = 1, "request started")). The sink owns host access, level mapping, correlation capture, and delivery failures. enabled runs before event fields are evaluated or formatted. emit borrows a record; an adapter that queues delivery must copy the data it needs into an owned value

Records retain event metadata, the message, and typed event fields. Sink filtering runs for each event so runtime level changes take effect. Logging from inside a sink is suppressed to prevent recursion

The Python bridge scopes native execution to a sink that uses LiteLLM's existing Python logger. It preserves request correlation, redacts before delivering to handlers, maps Rust trace events to Python debug, and reports handler failures through sys.unraisablehook. It accepts LiteLLM targets only, keeping dependency wire diagnostics out of the application logger

Python consumers continue using litellm._logging and its existing loggers, filters, formatters, and context setters. Catalog dispatch selects the processing backend for both Python and native diagnostics. The pure Processor takes explicit settings and never emits events

A future Node bridge can implement the same sink with runtime-specific delivery and expose the same processor through N-API. Node callback scheduling, queue limits, and shutdown belong in that bridge; this crate has no interpreter handles or output queue

This is diagnostic logging. Request lifecycle hooks and CustomLogger dispatch remain separate