| |
| info:cursos:pue:python-pcpp1:m5:4.1 [04/03/2024 12:07] – creat mate | info:cursos:pue:python-pcpp1:m5:4.1 [04/03/2024 13:24] (actual) – mate |
|---|
| |
| **NOTE**: Several calls to the ''getLogger'' function with the same name will always return the same object. | **NOTE**: Several calls to the ''getLogger'' function with the same name will always return the same object. |
| | |
| | == Logging levels |
| | The ''Logger'' object allows you to create logs with different levels of logging that help you to distinguish between less important logs and those reporting a serious error. By default, the following logging levels are defined: |
| | * CRITICAL: 50 |
| | * ERROR: 40 |
| | * WARNING: 30 |
| | * INFO: 20 |
| | * DEBUG: 10 |
| | * NOTSET: 0 |
| | |
| | Each level has a name and a numeric value. You can also define your own level, but those offered by the ''logging'' module are quite sufficient. The ''Logger'' object has methods that set the logging level for you. Take a look at the example in the editor. |
| | |
| | <code python> |
| | import logging |
| | |
| | logging.basicConfig() |
| | |
| | logger = logging.getLogger() |
| | |
| | logger.critical('Your CRITICAL message') |
| | logger.error('Your ERROR message') |
| | logger.warning('Your WARNING message') |
| | logger.info('Your INFO message') |
| | logger.debug('Your DEBUG message') |
| | </code> |
| | |
| | |
| | Result: |
| | <code> |
| | CRITICAL:root:Your CRITICAL message |
| | ERROR:root:Your ERROR message |
| | WARNING:root:Your WARNING message |
| | </code> |
| | |
| | All of the above methods require you to provide a message that will be visible in the logs. The default log format includes the level, the logger name and the message you’ve defined. Note that all these values are separated by a colon. Later in this course, you'll learn how to change the default formatting. |
| | |
| | You’re probably wondering why messages with ''INFO'' and ''DEBUG'' levels are not displayed. This is due to the default configuration, which we'll talk about in a moment. |
| | |
| | **NOTE**: The ''basicConfig'' method will be discussed later in the course. For now, remember that it's responsible for the basic logging configuration. |
| | |
| | == The setLevel method |
| | The root logger has the logging level set to ''WARNING''. This means that messages at the ''INFO'' or ''DEBUG'' levels aren't processed. |
| | |
| | Sometimes you may want to change this behavior, especially if you create your own logger. To do this, you need to pass a logging level to the ''setLevel'' method. See how we do this in the editor. |
| | |
| | <code python>import logging |
| | |
| | logging.basicConfig() |
| | |
| | logger = logging.getLogger() |
| | logger.setLevel(logging.DEBUG) |
| | |
| | logger.critical('Your CRITICAL message') |
| | logger.error('Your ERROR message') |
| | logger.warning('Your WARNING message') |
| | logger.info('Your INFO message') |
| | logger.debug('Your DEBUG message')</code> |
| | |
| | Result: |
| | <code>CRITICAL:root:Your CRITICAL message |
| | ERROR:root:Your ERROR message |
| | WARNING:root:Your WARNING message |
| | INFO:root:Your INFO message |
| | DEBUG:root:Your DEBUG message</code> |
| | |
| | Setting the ''DEBUG'' level causes messages with this or a higher level to be logged. It's worth mentioning that loggers created using the name argument have the ''NOTSET'' level set by default. In this case, their logging level is set based on the parent levels, starting from the closest parent to the root logger. |
| | |
| | If the closest parent has a level set to ''NOTSET'', the logger level is set based on the levels of subsequent parents in the hierarchy. Level setting ends if a parent has a level other than ''NOTSET''. If none of the visited parents has a level other than ''NOTSET'', then all messages will be processed regardless of their level. |
| | |
| | == Basic configuration (part 1) |
| | As we mentioned before, the basic logging configuration is done using the ''basicConfig'' method. Calling the ''basicConfig'' method (without specifying any arguments) creates a ''StreamHandler'' object that processes the logs and then displays them in the console. |
| | |
| | The ''StreamHandler'' object is created by the default ''Formatter'' object responsible for the log format. As a reminder, the default format consists of the level name, logger name, and defined message. Finally the newly created handler is added to the root logger. Later you'll learn how to create your own handler and formatter. |
| | |
| | In the previous examples, we called the ''basicConfig'' method without any arguments. Using the ''basicConfig'', method you can change the logging level (in the same way as using the ''setLevel'' method) and even the location of the logs. Take a look at the example in the editor. |
| | |
| | <code python>import logging |
| | |
| | logging.basicConfig(level=logging.CRITICAL, filename='prod.log', filemode='a') |
| | |
| | logger = logging.getLogger() |
| | |
| | logger.critical('Your CRITICAL message') |
| | logger.error('Your ERROR message') |
| | logger.warning('Your WARNING message') |
| | logger.info('Your INFO message') |
| | logger.debug('Your DEBUG message')</code> |
| | |
| | Result in ''prod.log'' file: |
| | <code>CRITICAL:root:Your CRITICAL message</code> |
| | |
| | In the example, the ''basicConfig'' method takes three arguments. The first one is the logging level equal to ''CRITICAL'', which means that only messages with this level will be processed. |
| | |
| | Passing a filename to the second argument creates a ''FileHandler'' object (instead of a ''StreamHandler'' object). As you’ve probably noticed, the logs no longer appear in the console. After setting the ''filename'' argument, all logs will be directed to the specified file. |
| | |
| | In addition, passing the last ''filemode'' argument with the value 'a' (this is the default mode) means that new logs will be appended to this file. If you'd like to change this mode, you can use other modes that are analogous to those used in the built-in ''open ''function. |
| | |
| | These aren't all the arguments that the ''basicConfig'' method can take. Are you ready for another dose of knowledge? Let's move on! |
| | |
| | **NOTE**: The ''basicConfig'' method changes the configuration of the root logger and its children who don't have their own handler defined. |
| | |
| | == Basic configuration (part 2) |
| | The ''basicConfig'' method presented earlier can also be used to change the default log formatting. This is done using the ''format'' argument, which can be defined using any characters or attributes of the ''LogRecord'' object. Let's explain it with the example in the editor. |
| | |
| | <code python>import logging |
| | |
| | FORMAT = '%(name)s:%(levelname)s:%(asctime)s:%(message)s' |
| | |
| | logging.basicConfig(level=logging.CRITICAL, filename='prod.log', filemode='a', format=FORMAT) |
| | |
| | logger = logging.getLogger() |
| | |
| | logger.critical('Your CRITICAL message') |
| | logger.error('Your ERROR message') |
| | logger.warning('Your WARNING message') |
| | logger.info('Your INFO message') |
| | logger.debug('Your DEBUG message') |
| | </code> |
| | |
| | Result in the ''prod.log'' file: |
| | |
| | <code>root:CRITICAL:2019-10-10 17:16:46,293:Your CRITICAL message</code> |
| | |
| | The format we define is created by combining the attributes of the ''LogRecord'' object separated by a colon. The ''LogRecord'' object is automatically created by the logger during logging. It contains many attributes, such as the name of the logger, the logging level, or even the line number in which the logging method is called. A full list of all available attributes can be found here [[[https://docs.python.org/3/library/logging.html#logrecord-attributes]]]. |
| | |
| | In our example, we use the following attributes:ç |
| | * **%(name)s** – this pattern will be replaced by the name of the logger that calls the logging method. In our case, it's the root logger; |
| | * **%(levelname)s** – this pattern will be replaced with the set login level. In our case, this is the CRITICAL level; |
| | * **%(asctime)s** – this pattern will be replaced with a human-readable date format that indicates when the LogRecord object was created. The decimal value is expressed in milliseconds; |
| | * **%(message)s** – this pattern will be replaced by the defined message. In our case, it's 'Your CRITICAL message'. |
| | |
| | In general, the scheme for using the ''LogRecord'' object argument in the format argument looks like this: |
| | |
| | <code>(LOG_RECORD_ATTRIBUTE_NAME)s</code> |
| | |
| | == Your first handler |
| | Each logger can save logs in different locations as well as in different formats. To do this, you must define your own handler and formatter. |
| | |
| | In most cases, you'll want to save your logs to a file. The logging module has the ''FileHandler'' class, which facilitates this task. When creating a ''FileHandler'' object, you must pass a filename where the logs will be saved. |
| | |
| | Additionally, you can pass a file ''mode'' with the mode argument, e.g., mode='a'. In the next step, you should set the logging level that will be processed by the handler. By default, the newly created handler is set to the ''NOTSET'' level. You can change this using the ''setLevel'' method. In the example in the editor, we've set the CRITICAL level. |
| | |
| | Finally, you need to add the created handler to your logger using the ''addHandler'' method. |
| | <code python> |
| | import logging |
| | |
| | logger = logging.getLogger(__name__) |
| | |
| | handler = logging.FileHandler('prod.log', mode='w') |
| | handler.setLevel(logging.CRITICAL) |
| | |
| | logger.addHandler(handler) |
| | |
| | logger.critical('Your CRITICAL message') |
| | logger.error('Your ERROR message') |
| | logger.warning('Your WARNING message') |
| | logger.info('Your INFO message') |
| | logger.debug('Your DEBUG message') |
| | </code> |
| | Result in the ''prod.log'' file: |
| | <code>Your CRITICAL message</code> |
| | |
| | If you check the ''prod.log'' file, you'll see that only the message is saved there. Do you know what we forgot? Your handler hasn't created a formatter. You'll learn how to do this in a moment. |
| | |
| | **NOTE**: Each logger can have several handlers added. One handler can save logs to a file, while another can send them to an external service. In order to process messages with a level lower than ''WARNING'' by added handlers, it's necessary to set this level threshold in the root logger. |
| | |
| | == Your first formatter |
| | Congratulations! You’ve just created your first handler. Only the formatter is missing, but don't worry. It's just two steps. Take a look at the example in the editor. |
| | |
| | <code python> |
| | import logging |
| | |
| | FORMAT = '%(name)s:%(levelname)s:%(asctime)s:%(message)s' |
| | |
| | logger = logging.getLogger(__name__) |
| | |
| | handler = logging.FileHandler('prod.log', mode='w') |
| | handler.setLevel(logging.CRITICAL) |
| | |
| | formatter = logging.Formatter(FORMAT) |
| | handler.setFormatter(formatter) |
| | |
| | logger.addHandler(handler) |
| | |
| | logger.critical('Your CRITICAL message') |
| | logger.error('Your ERROR message') |
| | logger.warning('Your WARNING message') |
| | logger.info('Your INFO message') |
| | logger.debug('Your DEBUG message') |
| | </code> |
| | |
| | Result in the prod.log file: |
| | <code>__main__:CRITICAL:2019-10-10 20:40:05,119:Your CRITICAL message</code> |
| | In the first step you need to create a ''Formatter'' object by passing the format you've defined to its constructor. In the example, we use the format defined in one of the previous examples. |
| | |
| | The next step is to set the formatter in the handler object. This is done using the ''setFormatter'' method. After doing this, you can analyze your logs in the ''prod.log'' file. |
| | |
| | |