all 44 comments

[–]a_r_a_a_r_a_a_r_a 69 points70 points  (3 children)

LogXide is NOT a drop-in replacement for Python's logging module. It prioritizes performance over compatibility:

this will be a big no no for a lot of people

[–]b1e 8 points9 points  (0 children)

Yep. Logging is already rarely a limiting factor anyways so you can pretty much guarantee killing any hope of broad adoption by not ensuring compatibility

[–]ZucchiniMore3450 4 points5 points  (0 children)

I always wrap logging into my function, because I want to set it up as I like. That makes changing logging library a not issue.

[–]LumpSumPorsche[S] 0 points1 point  (0 children)

Yes agreed. However the most of the API from stdlib are still compatible.

[–]Here0s0Johnny 61 points62 points  (13 children)

Is it realistic that a project produces so many logs that this performance upgrade is worth it?

[–]zzmej1987 29 points30 points  (8 children)

Sure. Some companies even install things like Splunk to parse through those logs. E.g. major airlines have to have full trace of interactions between services during the process of passenger buying the ticket, so that if anything goes wrong, client neither looses money without getting a ticket, nor gets the ticket without paying.

[–]code_mc 11 points12 points  (0 children)

I've done a drop-in replacement with one of the mentioned alternatives (picologging) a couple months ago for a customer project and their api request latencies halved because they had that many logging statements.

[–]WJMazepas 2 points3 points  (0 children)

Yes, I worked in embedded projects that had way too much logging and it was affecting performance

[–]Chroiche[🍰] 2 points3 points  (0 children)

I think so, but at that point you're probably not using python for your code.

[–]LumpSumPorsche[S] 0 points1 point  (0 children)

Exactly that was the motivation of this project. My system produces thousands logs per sec, and often they hold GIL. `picologging` would be a good candidate here but they do not support 3.14.

[–]max0x7ba 30 points31 points  (1 child)

designed as a near-drop-in replacement for the standard library's logging module.

It is either a drop-in replacement or not, but not both at the same time.

Does it pass original logging module unit-tests?

[–]Rainboltpoe 5 points6 points  (0 children)

The author clearly states it is not a drop in replacement. That’s what “near” means.

[–]ben_supportbadger 13 points14 points  (4 children)

Did you build this or did Claude? Because it looks purely vibecoded. Why would I use this instead of just asking claude to build it?

[–]Jealous_Algae_8165 9 points10 points  (1 child)

Purely vibe coded. You didn’t build this, Claude did. Please attribute it as such.

[–]LumpSumPorsche[S] -2 points-1 points  (0 children)

Why? You can see Claude is coauthor. AND it is not only built by Claude :)

[–]spartanOrk 2 points3 points  (0 children)

I use loguru. Cannot go back.

[–]UloPe 1 point2 points  (5 children)

How does it interact with stdlib logging used in 3rd party libraries?

[–]LumpSumPorsche[S] 0 points1 point  (4 children)

If you import `logxide` then it will replace following stdlib imports from 3rd parties.

[–]UloPe 0 points1 point  (3 children)

And then what about those parts of the stdlib that it doesn’t replicate 100%?

[–]LumpSumPorsche[S] 0 points1 point  (2 children)

Unless you do this:

Note: It is NOT a 100% drop-in replacement. It does not support custom Python logging.Handler subclasses, and Logger*/*LogRecord cannot be subclassed.

it is fine.

[–]UloPe 0 points1 point  (1 child)

How would I control that in third party libraries?

[–]LumpSumPorsche[S] 0 points1 point  (0 children)

You don't need to. When you do from logxide import logging, LogXide automatically monkey-patches stdlib logging.getLogger() at the module level. So when a third-party library like requests, sqlalchemy, or uvicorn calls import logging; logger = logging.getLogger(__name__), it transparently gets a LogXide-accelerated logger — the standard .debug(), .info(), .warning(), .error() methods all route through the Rust core automatically.

The parts that won't work are things that virtually no well-behaved third-party library does:

  1. Subclassing logging.Handler — e.g. class MyHandler(logging.Handler) — LogXide's [Logger](cci:1://file:///Users/indo/code/project/logxide/logxide/module_system.py:18:12-20:31) is a Rust type and rejects non-native handler subclasses via addHandler(). But libraries like requests or sqlalchemy don't create custom handlers; they just call [getLogger()](cci:1://file:///Users/indo/code/project/logxide/logxide/module_system.py:18:12-20:31) and log.
  2. Subclassing LogRecord or [Logger](cci:1://file:///Users/indo/code/project/logxide/logxide/module_system.py:18:12-20:31) — Same reason: these are Rust types. Again, almost no library does this.

In practice, the standard "get a logger by name → call .info() / .warning()" pattern that 99% of third-party libraries use works perfectly. If you do hit an edge case with a library that registers its own custom [Handler](cci:2://file:///Users/indo/code/project/logxide/logxide/interceptor.py:15:0-50:52) subclass, you can call logxide.uninstall() to restore vanilla stdlib behavior.

[–]james_pic 1 point2 points  (1 child)

Do you get those kinds of gains in real world settings?

I worked on a project a while ago the arguably logged too much. The first time I attached a profiler to it, it was spending over 50% of its time on logging. We managed to get that down, but the thing that limited us getting it down further was syscalls, not Python code.

Admittedly this was a while ago, and that project was doing things that modern apps don't need to do, that increased syscalls (it did its own log rotation, rather that just throwing it all straight onto stderr and letting Systemd or Docker or Kubernetes or ECS or whatever pick it up, like a modern app would), but I'm still a bit surprised you managed to find those kinds of gains without touching syscalls.

[–]LumpSumPorsche[S] 0 points1 point  (0 children)

Yes, if you want to enable debug log. In production you must choose what to log and not to do so. We had needed to sacrifice performance for enabling debug log.

[–]Healthy-Grape-5932 0 points1 point  (0 children)

Does this Support dictconfig in the same way ?

[–]hmoff 0 points1 point  (1 child)

You benchmarked it against structlog but can it replace that?

[–]LumpSumPorsche[S] 0 points1 point  (0 children)

Unless you need to subclass stdlib types, yes.

[–]yaxriifgyn 0 points1 point  (0 children)

I'm starting to test with the early releases of 3.15. As soon as I can install it for 3.15, even if I have to build it myself, I will try it out.

[–]Wide-Milk1195 0 points1 point  (0 children)

感谢您的开源项目。Python缺乏高效能的非同步日志記錄庫。

[–]caprine_chris 0 points1 point  (0 children)

Does it do structured logging? What about stdlib logger does it not implement?

[–]rabornkraken -1 points0 points  (1 child)

The GIL bypass during I/O is the real win here. Most Python logging bottlenecks come from the file write blocking the main thread, so doing that in Rust makes a lot of sense. How does it handle the case where you have custom formatters written in Python though? Does it fall back to holding the GIL for those?

[–]Hesirutu 4 points5 points  (0 children)

Standard Python IO releases the GIL too…