1
- # Copyright 2001-2019 by Vinay Sajip. All Rights Reserved.
1
+ # Copyright 2001-2022 by Vinay Sajip. All Rights Reserved.
2
2
#
3
3
# Permission to use, copy, modify, and distribute this software and its
4
4
# documentation for any purpose and without fee is hereby granted,
19
19
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
20
20
by Apache's log4j system.
21
21
22
- Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved.
22
+ Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved.
23
23
24
24
To use, simply 'import logging' and log away!
25
25
"""
26
26
27
27
import errno
28
+ import functools
28
29
import io
29
30
import logging
30
31
import logging .handlers
32
+ import queue
31
33
import re
32
34
import struct
33
35
import threading
@@ -563,7 +565,7 @@ def configure(self):
563
565
handler .name = name
564
566
handlers [name ] = handler
565
567
except Exception as e :
566
- if 'target not configured yet' in str (e .__cause__ ):
568
+ if ' not configured yet' in str (e .__cause__ ):
567
569
deferred .append (name )
568
570
else :
569
571
raise ValueError ('Unable to configure handler '
@@ -702,6 +704,21 @@ def add_filters(self, filterer, filters):
702
704
except Exception as e :
703
705
raise ValueError ('Unable to add filter %r' % f ) from e
704
706
707
+ def _configure_queue_handler (self , klass , ** kwargs ):
708
+ if 'queue' in kwargs :
709
+ q = kwargs ['queue' ]
710
+ else :
711
+ q = queue .Queue () # unbounded
712
+ rhl = kwargs .get ('respect_handler_level' , False )
713
+ if 'listener' in kwargs :
714
+ lklass = kwargs ['listener' ]
715
+ else :
716
+ lklass = logging .handlers .QueueListener
717
+ listener = lklass (q , * kwargs ['handlers' ], respect_handler_level = rhl )
718
+ handler = klass (q )
719
+ handler .listener = listener
720
+ return handler
721
+
705
722
def configure_handler (self , config ):
706
723
"""Configure a handler from a dictionary."""
707
724
config_copy = dict (config ) # for restoring in case of error
@@ -721,26 +738,83 @@ def configure_handler(self, config):
721
738
factory = c
722
739
else :
723
740
cname = config .pop ('class' )
724
- klass = self .resolve (cname )
725
- #Special case for handler which refers to another handler
741
+ if callable (cname ):
742
+ klass = cname
743
+ else :
744
+ klass = self .resolve (cname )
726
745
if issubclass (klass , logging .handlers .MemoryHandler ) and \
727
746
'target' in config :
747
+ # Special case for handler which refers to another handler
728
748
try :
729
- th = self .config ['handlers' ][config ['target' ]]
749
+ tn = config ['target' ]
750
+ th = self .config ['handlers' ][tn ]
730
751
if not isinstance (th , logging .Handler ):
731
752
config .update (config_copy ) # restore for deferred cfg
732
753
raise TypeError ('target not configured yet' )
733
754
config ['target' ] = th
734
755
except Exception as e :
735
- raise ValueError ('Unable to set target handler '
736
- '%r' % config ['target' ]) from e
756
+ raise ValueError ('Unable to set target handler %r' % tn ) from e
757
+ elif issubclass (klass , logging .handlers .QueueHandler ):
758
+ # Another special case for handler which refers to other handlers
759
+ if 'handlers' not in config :
760
+ raise ValueError ('No handlers specified for a QueueHandler' )
761
+ if 'queue' in config :
762
+ qspec = config ['queue' ]
763
+ if not isinstance (qspec , queue .Queue ):
764
+ if isinstance (qspec , str ):
765
+ q = self .resolve (qspec )
766
+ if not callable (q ):
767
+ raise TypeError ('Invalid queue specifier %r' % qspec )
768
+ q = q ()
769
+ elif isinstance (qspec , dict ):
770
+ if '()' not in qspec :
771
+ raise TypeError ('Invalid queue specifier %r' % qspec )
772
+ q = self .configure_custom (dict (qspec ))
773
+ else :
774
+ raise TypeError ('Invalid queue specifier %r' % qspec )
775
+ config ['queue' ] = q
776
+ if 'listener' in config :
777
+ lspec = config ['listener' ]
778
+ if isinstance (lspec , type ):
779
+ if not issubclass (lspec , logging .handlers .QueueListener ):
780
+ raise TypeError ('Invalid listener specifier %r' % lspec )
781
+ else :
782
+ if isinstance (lspec , str ):
783
+ listener = self .resolve (lspec )
784
+ if isinstance (listener , type ) and \
785
+ not issubclass (listener , logging .handlers .QueueListener ):
786
+ raise TypeError ('Invalid listener specifier %r' % lspec )
787
+ elif isinstance (lspec , dict ):
788
+ if '()' not in lspec :
789
+ raise TypeError ('Invalid listener specifier %r' % lspec )
790
+ listener = self .configure_custom (dict (lspec ))
791
+ else :
792
+ raise TypeError ('Invalid listener specifier %r' % lspec )
793
+ if not callable (listener ):
794
+ raise TypeError ('Invalid listener specifier %r' % lspec )
795
+ config ['listener' ] = listener
796
+ hlist = []
797
+ try :
798
+ for hn in config ['handlers' ]:
799
+ h = self .config ['handlers' ][hn ]
800
+ if not isinstance (h , logging .Handler ):
801
+ config .update (config_copy ) # restore for deferred cfg
802
+ raise TypeError ('Required handler %r '
803
+ 'is not configured yet' % hn )
804
+ hlist .append (h )
805
+ except Exception as e :
806
+ raise ValueError ('Unable to set required handler %r' % hn ) from e
807
+ config ['handlers' ] = hlist
737
808
elif issubclass (klass , logging .handlers .SMTPHandler ) and \
738
809
'mailhost' in config :
739
810
config ['mailhost' ] = self .as_tuple (config ['mailhost' ])
740
811
elif issubclass (klass , logging .handlers .SysLogHandler ) and \
741
812
'address' in config :
742
813
config ['address' ] = self .as_tuple (config ['address' ])
743
- factory = klass
814
+ if issubclass (klass , logging .handlers .QueueHandler ):
815
+ factory = functools .partial (self ._configure_queue_handler , klass )
816
+ else :
817
+ factory = klass
744
818
props = config .pop ('.' , None )
745
819
kwargs = {k : config [k ] for k in config if valid_ident (k )}
746
820
try :
0 commit comments