4.3. Debugging Logging — Python
4.3.1. Logging levels
Critical - Critical Error, and cannot continue
Error - Error, but can continue
Warning - Warning, will do something important
Info - I will do something
Debug - This is how I am doing this
import logging logging.critical('Permission Denied, cannot continue') logging.error('File not found, will create a new one') logging.warning('Warning, will overwrite the file') logging.info('Writing to file') logging.debug('Data {DATA} will be written to file {path}')
4.3.2. Use Case - 1
import logging print('Program start') for number in range(0,3): print(f'Current number: {number}') print('Program end') # Program start # Current number: 0 # Current number: 1 # Current number: 2 # Program end
import logging logging.warning('Program start') for number in range(0,3): logging.debug(f'Current number: {number}') logging.warning('Program end') # WARNING:root:Program start # WARNING:root:Program end
4.3.3. Konfiguracja logowania
import logging logging.basicConfig(level=logging.INFO) log = logging.getLogger(__name__) log.warning('warning!') # zostanie zapisana do pliku log.debug('Debug message') # nie zostanie zapisana, bo level jest INFO, czyli powyżej DEBUG
import logging logging.basicConfig( level=logging.INFO, filename='/tmp/logging.csv', format='"%(asctime).19s", "%(levelname)s", "%(message)s"' ) log = logging.getLogger(__name__) log.warning('warning!') # zostanie zapisana do pliku log.debug('Debug message') # nie zostanie zapisana, bo level jest INFO, czyli powyżej DEBUG
import logging logging.basicConfig( level=logging.DEBUG, format='"%(asctime).19s", "%(levelname)s", "%(message)s"', filename="log.csv", ) logging.info('Loop start') i = 0 while i <= 3: logging.info(f'Computing {i}') i += 1 logging.info('Loop end')
import logging logging.basicConfig( level=logging.INFO, filename='/tmp/logging.csv', format='"%(asctime).19s", "%(levelname)s", "%(message)s"' ) log = logging.getLogger(__name__) log.warning('warning!') # zostanie zapisana do pliku log.debug('Debug message') # nie zostanie zapisana, bo level jest INFO, czyli powyżej DEBUG
import logging logging.basicConfig( level='DEBUG', datefmt='"%Y-%m-%d" "%H:%M:%S"', format='{asctime}, "{levelname}", "{message}"', style='{' )
4.3.4. Logowanie zdarzeń
import logging log = logging.getLogger(__name__) def sum(a, b): log.debug('Function `sum()` executed with: %s', locals()) value = a + b log.debug(f'Will produce "{value}" as result') return value sum(1, 2) # Function `sum()` executed with: {'b': 2, 'a': 1} # Will produce "3" as result # 3
4.3.5. Wyciszanie logowania
import logging logging.basicConfig( level=logging.DEBUG, format='[%(asctime).19s] [%(levelname)s] %(message)s') logging.getLogger('requests').setLevel(logging.WARNING) log = logging.getLogger(__name__) log.debug('Debug message')
4.3.6. Konfiguracja formatowania logów
Format |
Description |
|---|---|
args |
The tuple of arguments merged into |
|
Human-readable time when the LogRecord was created. By default this is of the form '2003-07-08 16:49:45,896' (the numbers after the comma are millisecond portion of the time). |
|
Time when the LogRecord was created (as returned by time.time). |
exc_info |
Exception tuple (à la |
|
Filename portion of |
|
Name of function containing the logging call. |
|
Text logging level for the message
( |
|
Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). |
|
Source line number where the logging call was issued (if available). |
|
Module (name portion of |
|
Millisecond portion of the time when the LogRecord was created. |
|
The logged message, computed as |
msg |
The format string passed in the original
logging call. Merged with |
|
Name of the logger used to log the call. |
|
Full pathname of the source file where the logging call was issued (if available). |
|
Process ID (if available). |
|
Process name (if available). |
|
Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded. |
stack_info |
Stack frame information (where available) from the bottom of the stack in the current thread, up to and including the stack frame of the logging call which resulted in the creation of this record. You shouldn't need to format this yourself. |
|
Thread ID (if available). |
|
Thread name (if available). |
4.3.7. DictConfig
{ 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' }, }, 'handlers': { 'default': { 'level': 'INFO', 'formatter': 'standard', 'class': 'logging.StreamHandler', }, }, 'loggers': { '': { 'handlers': ['default'], 'level': 'INFO', 'propagate': True }, 'django.request': { 'handlers': ['default'], 'level': 'WARN', 'propagate': False }, } }
Format |
Description |
|---|---|
filename |
Specifies that a FileHandler be created, using the specified filename, rather than a StreamHandler |
filemode |
If filename is specified, open the file in this mode. Defaults to 'a' |
format |
Use the specified format string for the handler |
datefmt |
Use the specified date/time format, as accepted by time.strftime() |
style |
If format is specified, use this style for the format string. One of '%', '{' or '$' for printf-style, str.format() or string.Template respectively. Defaults to '%' |
level |
Set the root logger level to the specified level |
stream |
Use the specified stream to initialize the StreamHandler. Note that this argument is incompatible with filename - if both are present, a ValueError is raised |
handlers |
If specified, this should be an iterable of already created handlers to add to the root logger. Any handlers which don't already have a formatter set will be assigned the default formatter created in this function. Note that this argument is incompatible with filename or stream - if both are present, a ValueError is raised |
4.3.8. Rotate
logging.handlers.WatchedFileHandlerlogging.handlers.RotatingFileHandlerlogging.handlers.TimedRotatingFileHandler
from logging import handlers handler = handlers.TimedRotatingFileHandler(filename, when=LOG_ROTATE) handler.setFormatter(logging.Formatter(log_format, datefmt='%Y-%m-%d %H:%M:%S')) #LOG_ROTATE = midnight #set your log format
4.3.9. Examples
import logging import os logging.basicConfig( format='"{asctime}", "{levelname}", "{message}"', filename='...', style='{' ) log = logging.getLogger(__name__) level = os.getenv('LOG_LEVEL', 'INFO') log.setLevel(level) log.critical('Critical error... finishing') log.error('Some problem but can continue') log.warning('Warning, this is important') log.info('Typical message') log.debug('Debug message with extra information') logging.getLogger('requests').setLevel('DEBUG') logging.getLogger('_tmp').setLevel('ERROR')
Decorators:
from datetime import datetime import logging logging.basicConfig( level='DEBUG', datefmt='%Y-%m-%d %H:%M:%S', format='[{levelname}] {message}', style='{' ) def timeit(func): def wrapper(*args, **kwargs): time_start = datetime.now() result = func(*args, **kwargs) time_end = datetime.now() time = time_end - time_start logging.debug(f'Time: {time}') return result return wrapper def debug(func): def wrapper(*args, **kwargs): function = func.__name__ logging.debug(f'Calling: {function=}, {args=}, {kwargs=}') result = func(*args, **kwargs) logging.debug(f'Result: {result}') return result return wrapper @timeit @debug def add_numbers(a, b): return a + b add_numbers(1, 2) # [DEBUG] Calling: function='add_numbers', args=(1, 2), kwargs={} # [DEBUG] Result: 3 # [DEBUG] Time: 0:00:00.000105 add_numbers(1, b=2) # [DEBUG] Calling: function='add_numbers', args=(1,), kwargs={'b': 2} # [DEBUG] Result: 3 # [DEBUG] Time: 0:00:00.000042 add_numbers(a=1, b=2) # [DEBUG] Calling: function='add_numbers', args=(), kwargs={'a': 1, 'b': 2} # [DEBUG] Result: 3 # [DEBUG] Time: 0:00:00.000040