The logging dilemma: keeping debug context without debug noise
blog.assad.fr
blog.assad.fr
The feature i am referring too is named loggers where different loggers have different levels. The problem with this is of course one logger may record ERROR messages but the real details in other loggers are at DEBUG which may be filtered because they also are set to something higher.
Im suggesting the solution is support for ideas of capturing all log messages within a transaction if an ERROR message is hit.
- new transaction - log DEBUG message 1 - log DEBUG message 2 - log ERROR message 3 - end transaction
In the above example my suggestion would be all 3 messages would be logged because 3 happens. In a transaction without "3" 1 and 2 would not be logged.
The twist I’m exploring is to keep debug logs buffered during normal execution and only release the relevant context when an error occurs. So you get the diagnostic value of DEBUG without continuously storing all the noise.
I built a small Go library to experiment with this approach, but I’m particularly interested in where the model breaks down in real-world systems.
Names, logger, different levels...
And thats all.
Whether it's the right idea I genuinely don't know yet. But the space is emptier than it looks, and the cost of trying something is a weekend.
I think this could be improved by moving this logic to properties files and using named loggers or named transactions to enable/disable.
The explicit Trip(ctx) is the escape hatch for failures that never produce an error log: a 4xx you return without logging, a retry that eventually succeeded but you still want the context, a request you sampled deliberately. That's the minority case.
On config: the threshold is already a knob rather than a hardcoded level, so you can set the trip at Warn, or set what gets buffered below your emit level, from wherever your config lives. What's genuinely missing is the per-logger-name granularity you're describing, log4j-style logger.foo.bar=DEBUG. Go's log/slog has no logger registry to hang names on, so there's nothing to address by name. Scopes are the unit instead, which maps to a request or transaction rather than to a package. That's a real tradeoff and not obviously the right one for every codebase.