1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
---
created: 2024-03-17
description: Python logging with ✨pretty colors🌸
---
## Logging > print
Logging is useful, because all python code can use the same standard, allowing you to centrally control:
- Which messages you wish to see
- Every debug message, for local development
- Disable debug messages, by changing 1 line e.g. for end user deployment
- How to format them
- Do you wish to see which module and line of code this message originated from
- Where they should end up
- Printed to [STDOUT](https://en.wikipedia.org/wiki/Standard_streams) (terminal)
- A log file
- A log server
- Maybe a notification should be triggered on CRITICAL errors
> A python [*"module"*](https://docs.python.org/3/tutorial/modules.html) is simply just the name for a .py file.
> e.g. the file `database_handler.py` is a module called `database_handler`
```py
import logging
class ColorFormatter(logging.Formatter):
grey = "\x1b[90;20m"
cyan = "\x1b[96;20m"
yellow = "\x1b[33;20m"
red = "\x1b[31;20m"
bold_red = "\x1b[31;1m"
reset = "\x1b[0m"
format = "%(asctime)s,%(msecs)03d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s"
FORMATS = {
logging.DEBUG: grey + format + reset,
logging.INFO: cyan + format + reset,
logging.WARNING: yellow + format + reset,
logging.ERROR: red + format + reset,
logging.CRITICAL: bold_red + format + reset
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
logger = logging.getLogger(__name__) # Instantiate a logger to be used in this module
# Display every message Change this to INFO to see INFO and above (filter out DEBUG)
# See: https://docs.python.org/3/howto/logging.html#logging-levels
logger.root.setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler() # This catches and handles log messages on the root handler
stream_handler.setFormatter(ColorFormatter())
logger.root.addHandler(stream_handler)
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
```
> Code snippet is modified, but based on:
> [stackoverflow.com - How can I color Python logging output?](https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output#answer-56944256).
> Used and credited as per [stackoverflow.com - CC BY-SA 4.0 content license](https://stackoverflow.com/help/licensing)
<pre>
$ python /tmp/1.py
<span style="color:grey">2024-03-17 14:09:06,411,411 DEBUG [1.py:37] debug message</span>
<span style="color:var(--main-color)">2024-03-17 14:09:06,411,411 INFO [1.py:38] info message</span>
<span style="color:yellow">2024-03-17 14:09:06,411,411 WARNING [1.py:39] warning message</span>
<span style="color:red">2024-03-17 14:09:06,411,411 ERROR [1.py:40] error message</span>
<span style="font-weight: bold; color:red">2024-03-17 14:09:06,411,411 CRITICAL [1.py:41] critical message</span>
</pre>
In subsequent modules, do the following:
```py
import logging
logger = logging.getLogger(__name__) # Set the logger name, to the name of the module
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
```
> Wish to change the colors?
> Color codes can be found here: [wikipedia.org - ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit)
Okay, but why do we need to do it this way?
Because logging is hieratical, these 2 loggers will be siblings under the root logger (see following tree structure).
Therefore we set a handler on the root logger which ensures all logging will hit our formatting code.
<pre>
$ root
├── <span class="devicons devicons-python"></span> main
└── <span class="devicons devicons-python"></span> module_a
</pre>
You can set a breakpoint and inspect `logger.parent` to see this structure:
```pycon
>>> logger.parent
<RootLogger root (DEBUG)>
```
Read more: [docs.python.org - Logging HOWTO](https://docs.python.org/3/howto/logging.html)
|