22
33import json
44import logging
5+ import sys
56import textwrap
6- import traceback
7- from typing import Any
7+ from typing import TYPE_CHECKING , Any
88
99from colorama import Fore , Style , just_fix_windows_console
10+ from typing_extensions import assert_never
11+
12+ if TYPE_CHECKING :
13+ from crawlee .configuration import Configuration
1014
1115just_fix_windows_console ()
1216
3135_LOG_MESSAGE_INDENT = ' ' * 6
3236
3337
38+ def get_configured_log_level (configuration : Configuration ) -> int :
39+ verbose_logging_requested = 'verbose_log' in configuration .model_fields_set and configuration .verbose_log
40+
41+ if 'log_level' in configuration .model_fields_set :
42+ if configuration .log_level == 'DEBUG' :
43+ return logging .DEBUG
44+ if configuration .log_level == 'INFO' :
45+ return logging .INFO
46+ if configuration .log_level == 'WARNING' :
47+ return logging .WARNING
48+ if configuration .log_level == 'ERROR' :
49+ return logging .ERROR
50+ if configuration .log_level == 'CRITICAL' :
51+ return logging .CRITICAL
52+
53+ assert_never (configuration .log_level )
54+
55+ if sys .flags .dev_mode or verbose_logging_requested :
56+ return logging .DEBUG
57+
58+ return logging .INFO
59+
60+
61+ def configure_logger (
62+ logger : logging .Logger ,
63+ configuration : Configuration ,
64+ * ,
65+ remove_old_handlers : bool = False ,
66+ ) -> None :
67+ handler = logging .StreamHandler ()
68+ handler .setFormatter (CrawleeLogFormatter ())
69+
70+ if remove_old_handlers :
71+ for old_handler in logger .handlers [:]:
72+ logger .removeHandler (old_handler )
73+
74+ logger .addHandler (handler )
75+ logger .setLevel (get_configured_log_level (configuration ))
76+
77+
3478class CrawleeLogFormatter (logging .Formatter ):
3579 """Log formatter that prints out the log message nicely formatted, with colored level and stringified extra fields.
3680
@@ -87,15 +131,6 @@ def format(self, record: logging.LogRecord) -> str:
87131 level_short_alias = _LOG_LEVEL_SHORT_ALIAS .get (record .levelno , record .levelname )
88132 level_string = f'{ level_color_code } { level_short_alias } { Style .RESET_ALL } '
89133
90- # Format the exception, if there is some
91- # Basically just print the traceback and indent it a bit
92- exception_string = ''
93- if record .exc_info :
94- exc_info = record .exc_info
95- record .exc_info = None
96- exception_string = '' .join (traceback .format_exception (* exc_info )).rstrip ()
97- exception_string = '\n ' + textwrap .indent (exception_string , _LOG_MESSAGE_INDENT )
98-
99134 # Format the extra log record fields, if there were some
100135 # Just stringify them to JSON and color them gray
101136 extra_string = ''
@@ -105,8 +140,19 @@ def format(self, record: logging.LogRecord) -> str:
105140 f' { Fore .LIGHTBLACK_EX } ({ json .dumps (extra , ensure_ascii = False , default = str )} ){ Style .RESET_ALL } '
106141 )
107142
143+ # Call the parent method so that it populates missing fields in the record
144+ super ().format (record )
145+
108146 # Format the actual log message
109- log_string = super ().format (record )
147+ log_string = self .formatMessage (record )
148+
149+ # Format the exception, if there is some
150+ # Basically just print the traceback and indent it a bit
151+ exception_string = ''
152+ if record .exc_text :
153+ exception_string = '\n ' + textwrap .indent (record .exc_text .rstrip (), _LOG_MESSAGE_INDENT )
154+ else :
155+ exception_string = ''
110156
111157 if self .include_logger_name :
112158 # Include logger name at the beginning of the log line
0 commit comments