@@ -3712,6 +3712,71 @@ Of course, these above examples show output according to the format used by
3712
3712
:func: `~logging.basicConfig `, but you can use a different formatter when you
3713
3713
configure logging.
3714
3714
3715
+ Note that with the above scheme, you are somewhat at the mercy of buffering and
3716
+ the sequence of write calls which you are intercepting. For example, with the
3717
+ definition of ``LoggerWriter `` above, if you have the snippet
3718
+
3719
+ .. code-block :: python
3720
+
3721
+ sys.stderr = LoggerWriter(logger, logging.WARNING )
3722
+ 1 / 0
3723
+
3724
+ then running the script results in
3725
+
3726
+ .. code-block :: text
3727
+
3728
+ WARNING:demo:Traceback (most recent call last):
3729
+
3730
+ WARNING:demo: File "/home/runner/cookbook-loggerwriter/test.py", line 53, in <module>
3731
+
3732
+ WARNING:demo:
3733
+ WARNING:demo:main()
3734
+ WARNING:demo: File "/home/runner/cookbook-loggerwriter/test.py", line 49, in main
3735
+
3736
+ WARNING:demo:
3737
+ WARNING:demo:1 / 0
3738
+ WARNING:demo:ZeroDivisionError
3739
+ WARNING:demo::
3740
+ WARNING:demo:division by zero
3741
+
3742
+ As you can see, this output isn't ideal. That's because the underlying code
3743
+ which writes to ``sys.stderr `` makes mutiple writes, each of which results in a
3744
+ separate logged line (for example, the last three lines above). To get around
3745
+ this problem, you need to buffer things and only output log lines when newlines
3746
+ are seen. Let's use a slghtly better implementation of ``LoggerWriter ``:
3747
+
3748
+ .. code-block :: python
3749
+
3750
+ class BufferingLoggerWriter (LoggerWriter ):
3751
+ def __init__ (self , logger , level ):
3752
+ super ().__init__ (logger, level)
3753
+ self .buffer = ' '
3754
+
3755
+ def write (self , message ):
3756
+ if ' \n ' not in message:
3757
+ self .buffer += message
3758
+ else :
3759
+ parts = message.split(' \n ' )
3760
+ if self .buffer:
3761
+ s = self .buffer + parts.pop(0 )
3762
+ self .logger.log(self .level, s)
3763
+ self .buffer = parts.pop()
3764
+ for part in parts:
3765
+ self .logger.log(self .level, part)
3766
+
3767
+ This just buffers up stuff until a newline is seen, and then logs complete
3768
+ lines. With this approach, you get better output:
3769
+
3770
+ .. code-block :: text
3771
+
3772
+ WARNING:demo:Traceback (most recent call last):
3773
+ WARNING:demo: File "/home/runner/cookbook-loggerwriter/main.py", line 55, in <module>
3774
+ WARNING:demo: main()
3775
+ WARNING:demo: File "/home/runner/cookbook-loggerwriter/main.py", line 52, in main
3776
+ WARNING:demo: 1/0
3777
+ WARNING:demo:ZeroDivisionError: division by zero
3778
+
3779
+
3715
3780
.. patterns-to-avoid:
3716
3781
3717
3782
Patterns to avoid
0 commit comments