11import logging
2+ import logging .config
23import logging .handlers
34
45import structlog
@@ -24,36 +25,37 @@ def _extract_gunicorn_access_log_event(
2425 return event_dict
2526
2627
27- def setup_logging (settings : LoggingSettings ) -> None :
28- """
29- Configure stdlib logger to use structlog processors and formatters so that
30- uvicorn and application logs are consistent.
31- """
32- is_generic_format = settings .log_format is LogFormat .GENERIC
28+ def _drop_color_message (
29+ record : logging .LogRecord ,
30+ name : str ,
31+ event_dict : structlog .types .EventDict ,
32+ ) -> structlog .types .EventDict :
33+ # Uvicorn logs the message a second time in the extra `color_message`, but we don't
34+ # need it. This processor drops the key from the event dict if it exists.
35+ event_dict .pop ("color_message" , None )
36+ return event_dict
3337
34- processors : list [structlog .types .Processor ] = [
35- structlog .contextvars .merge_contextvars ,
36- structlog .stdlib .add_logger_name ,
37- structlog .stdlib .add_log_level ,
38- _extract_gunicorn_access_log_event ,
39- structlog .stdlib .PositionalArgumentsFormatter (),
40- structlog .stdlib .ExtraAdder (),
41- structlog .processors .StackInfoRenderer (),
42- structlog .processors .TimeStamper (fmt = "iso" ),
43- ]
4438
45- if is_generic_format :
46- # For `generic` format, set `exc_info` on the log event if the log method is
47- # `exception` and `exc_info` is not already set.
48- #
49- # Rendering of `exc_info` is handled by ConsoleRenderer.
50- processors .append (structlog .dev .set_exc_info )
51- else :
52- # For `json` format `exc_info` must be set explicitly when
53- # needed, and is translated into a formatted `exception` field.
54- processors .append (structlog .processors .format_exc_info )
39+ COMMON_PROCESSORS : list [structlog .types .Processor ] = [
40+ structlog .contextvars .merge_contextvars ,
41+ structlog .stdlib .add_logger_name ,
42+ structlog .stdlib .add_log_level ,
43+ _extract_gunicorn_access_log_event ,
44+ structlog .stdlib .PositionalArgumentsFormatter (),
45+ structlog .stdlib .ExtraAdder (),
46+ _drop_color_message ,
47+ structlog .processors .StackInfoRenderer (),
48+ structlog .processors .TimeStamper (fmt = "iso" ),
49+ ]
5550
56- processors .append (structlog .processors .EventRenamer (settings .log_event_field_name ))
51+
52+ def setup_logging (settings : LoggingSettings ) -> None :
53+ processors = [
54+ * COMMON_PROCESSORS ,
55+ structlog .processors .EventRenamer (settings .log_event_field_name ),
56+ structlog .dev .set_exc_info ,
57+ structlog .processors .format_exc_info ,
58+ ]
5759
5860 structlog .configure (
5961 processors = processors
@@ -62,34 +64,60 @@ def setup_logging(settings: LoggingSettings) -> None:
6264 cache_logger_on_first_use = True ,
6365 )
6466
65- if is_generic_format :
66- log_renderer = structlog .dev .ConsoleRenderer (
67- event_key = settings .log_event_field_name
68- )
69- else :
70- log_renderer = structlog .processors .JSONRenderer ()
71-
72- formatter = structlog .stdlib .ProcessorFormatter (
73- use_get_message = False ,
74- pass_foreign_args = True ,
75- foreign_pre_chain = processors ,
76- processors = [
77- structlog .stdlib .ProcessorFormatter .remove_processors_meta ,
78- log_renderer ,
79- ],
80- )
81-
82- handler = logging .StreamHandler ()
83- handler .setFormatter (formatter )
84-
85- root = logging .getLogger ()
86- root .addHandler (handler )
87- root .setLevel (settings .log_level .to_logging_log_level ())
88-
8967 # Propagate uvicorn logs instead of letting uvicorn configure the format
9068 for name in ["uvicorn" , "uvicorn.error" ]:
9169 logging .getLogger (name ).handlers .clear ()
9270 logging .getLogger (name ).propagate = True
9371
9472 logging .getLogger ("uvicorn.access" ).handlers .clear ()
9573 logging .getLogger ("uvicorn.access" ).propagate = settings .enable_access_log
74+
75+ override = settings .override
76+ logging .config .dictConfig (
77+ {
78+ "version" : 1 ,
79+ "disable_existing_loggers" : False ,
80+ "formatters" : {
81+ LogFormat .GENERIC .value : {
82+ "()" : structlog .stdlib .ProcessorFormatter ,
83+ "use_get_message" : False ,
84+ "pass_foreign_args" : True ,
85+ "processors" : [
86+ structlog .stdlib .ProcessorFormatter .remove_processors_meta ,
87+ structlog .dev .ConsoleRenderer (
88+ event_key = settings .log_event_field_name ,
89+ colors = settings .colours ,
90+ ),
91+ ],
92+ "foreign_pre_chain" : processors ,
93+ },
94+ LogFormat .JSON .value : {
95+ "()" : structlog .stdlib .ProcessorFormatter ,
96+ "use_get_message" : False ,
97+ "pass_foreign_args" : True ,
98+ "processors" : [
99+ structlog .stdlib .ProcessorFormatter .remove_processors_meta ,
100+ structlog .processors .JSONRenderer (),
101+ ],
102+ "foreign_pre_chain" : processors ,
103+ },
104+ ** (override .get ("formatters" ) or {}),
105+ },
106+ "handlers" : {
107+ "default" : {
108+ "level" : settings .log_level .to_logging_log_level (),
109+ "class" : "logging.StreamHandler" ,
110+ "formatter" : settings .log_format .value ,
111+ },
112+ ** (override .get ("handlers" ) or {}),
113+ },
114+ "loggers" : {
115+ "" : {
116+ "handlers" : ["default" ],
117+ "level" : settings .log_level .to_logging_log_level (),
118+ "propagate" : True ,
119+ },
120+ ** (override .get ("loggers" ) or {}),
121+ },
122+ }
123+ )
0 commit comments