forked from infinition/Bjorn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logger.py
136 lines (104 loc) · 5.14 KB
/
logger.py
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#logger.py
# Description:
# This file, logger.py, is responsible for setting up a robust logging system for the Bjorn project. It defines custom logging levels and formats,
# integrates with the Rich library for enhanced console output, and ensures logs are written to rotating files for persistence.
#
# Key functionalities include:
# - Defining a custom log level "SUCCESS" to log successful operations distinctively.
# - Creating a vertical filter to exclude specific log messages based on their content.
# - Setting up a logger class (`Logger`) that initializes logging handlers for both console and file output.
# - Utilizing Rich for console logging with custom themes for different log levels, providing a more readable and visually appealing log output.
# - Ensuring log files are written to a specified directory, with file rotation to manage log file sizes and backups.
# - Providing methods to log messages at various levels (debug, info, warning, error, critical, success).
# - Allowing dynamic adjustment of log levels and the ability to disable logging entirely.
import logging
from logging.handlers import RotatingFileHandler
import os
from rich.console import Console
from rich.logging import RichHandler
from rich.theme import Theme
# Define custom log level "SUCCESS"
SUCCESS_LEVEL_NUM = 25
logging.addLevelName(SUCCESS_LEVEL_NUM, "SUCCESS")
def success(self, message, *args, **kwargs):
if self.isEnabledFor(SUCCESS_LEVEL_NUM):
self._log(SUCCESS_LEVEL_NUM, message, args, **kwargs)
logging.Logger.success = success
class VerticalFilter(logging.Filter):
def filter(self, record):
return 'Vertical' not in record.getMessage()
class Logger:
LOGS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'logs')
def __init__(self, name, level=logging.DEBUG, enable_file_logging=True):
self.logger = logging.getLogger(name)
self.logger.setLevel(level)
self.enable_file_logging = enable_file_logging
# Define custom log level styles
custom_theme = Theme({
"debug": "yellow",
"info": "blue",
"warning": "yellow",
"error": "bold red",
"critical": "bold magenta",
"success": "bold green"
})
console = Console(theme=custom_theme)
# Create console handler with rich and set level
console_handler = RichHandler(console=console, show_time=False, show_level=False, show_path=False, log_time_format="%Y-%m-%d %H:%M:%S")
console_handler.setLevel(level)
console_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
console_handler.setFormatter(console_formatter)
# Add filter to console handler
vertical_filter = VerticalFilter()
console_handler.addFilter(vertical_filter)
# Add console handler to the logger
self.logger.addHandler(console_handler)
if self.enable_file_logging:
# Ensure the log folder exists
os.makedirs(self.LOGS_DIR, exist_ok=True)
log_file_path = os.path.join(self.LOGS_DIR, f"{name}.log")
# Create file handler and set level
file_handler = RotatingFileHandler(log_file_path, maxBytes=5*1024*1024, backupCount=2)
file_handler.setLevel(level)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
file_handler.setFormatter(file_formatter)
# Add filter to file handler
file_handler.addFilter(vertical_filter)
# Add file handler to the logger
self.logger.addHandler(file_handler)
def set_level(self, level):
self.logger.setLevel(level)
for handler in self.logger.handlers:
handler.setLevel(level)
def debug(self, message):
self.logger.debug(message)
def info(self, message):
self.logger.info(message)
def warning(self, message):
self.logger.warning(message)
def error(self, message):
self.logger.error(message)
def critical(self, message):
self.logger.critical(message)
def success(self, message):
self.logger.success('\n' + message) # Add newline for better readability
def disable_logging(self):
logging.disable(logging.CRITICAL)
# Example usage
if __name__ == "__main__":
# Change enable_file_logging to False to disable file logging
log = Logger(name="MyLogger", level=logging.DEBUG, enable_file_logging=False)
log.debug("This is a debug message")
log.info("This is an info message")
log.warning("This is a warning message")
log.error("This is an error message")
log.critical("This is a critical message")
log.success("This is a success message")
# Change log level
log.set_level(logging.WARNING)
log.debug("This debug message should not appear")
log.info("This info message should not appear")
log.warning("This warning message should appear")
# Disable logging
log.disable_logging()
log.error("This error message should not appear")