While developing my own logging library, I studied the source code of the standard logging
module of CPython.
One of its features is that handlers are thread-safe. One can write logs to a file from multiple threads without the risk that the lines are corrupted.
I digged into the sources of the logging
module and I figured out that to ensure thread-safety, each handler uses its own Lock
that it acquire and release at each logging call. You can see this here.
What is the point of doing this rather than using a single lock surrounding the call of handlers globally? For example, why not acquire the lock in the callHandlers()
function?
I see two main advantages:
- Better performances as there is only one lock acquired and released
- Ensure that logged messages are displayed in the same order in all handlers (thinks for example of two handlers H1 and H2 and two threads logging respectively M1 and M2, this avoid the possibility of the sequence
H1.handle(M1) -> H1.handle(M2) -> H2.handle(M2) -> H2.handle(M1)
to happen)
In my personal logging library, can I safely do something like this per logger:
with self.lock:
for handler in self.handlers:
handler.handle(message) # No lock used in "handle()"
Or am I overlooking something else?