Skip to content

Commit 8bcc267

Browse files
authored
feat: better logger stream allocation (#1006)
# Motivation <!-- Why is this change necessary? --> # Content <!-- Please include a summary of the change --> # Testing <!-- How was the change tested? --> # Please check the following before marking your PR as ready for review - [ ] I have added tests for my changes - [ ] I have updated the documentation or added new documentation as needed
1 parent 60df161 commit 8bcc267

File tree

1 file changed

+58
-23
lines changed

1 file changed

+58
-23
lines changed
Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,76 @@
11
import logging
2+
import sys
23

34
import colorlog
45

5-
6-
def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
7-
# Force configure the root logger with a NullHandler to prevent duplicate logs
8-
logging.basicConfig(handlers=[logging.NullHandler()], force=True)
9-
10-
formatter = colorlog.ColoredFormatter(
11-
"%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s",
12-
log_colors={
6+
formatter = colorlog.ColoredFormatter(
7+
"%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s",
8+
log_colors={
9+
"DEBUG": "white",
10+
"INFO": "green",
11+
"WARNING": "yellow",
12+
"ERROR": "red",
13+
"CRITICAL": "red,bg_white",
14+
},
15+
secondary_log_colors={
16+
"message": {
1317
"DEBUG": "cyan",
14-
"INFO": "green",
18+
"INFO": "white",
1519
"WARNING": "yellow",
1620
"ERROR": "red",
1721
"CRITICAL": "red,bg_white",
18-
},
19-
secondary_log_colors={
20-
"message": {
21-
"DEBUG": "cyan",
22-
"INFO": "blue",
23-
"WARNING": "yellow",
24-
"ERROR": "red",
25-
"CRITICAL": "red,bg_white",
26-
}
27-
},
28-
)
22+
}
23+
},
24+
)
25+
26+
27+
class StdOutFilter(logging.Filter):
28+
def filter(self, record):
29+
return record.levelno < logging.ERROR
30+
31+
32+
class StdErrFilter(logging.Filter):
33+
def filter(self, record):
34+
return record.levelno >= logging.ERROR
35+
36+
37+
# Create handlers
38+
stdout_handler = logging.StreamHandler(sys.stdout) # Logs to stdout
39+
stdout_handler.setFormatter(formatter)
40+
stdout_handler.addFilter(StdOutFilter())
41+
42+
stderr_handler = logging.StreamHandler(sys.stderr) # Logs to stderr
43+
stderr_handler.setFormatter(formatter)
44+
stderr_handler.addFilter(StdErrFilter())
45+
46+
47+
def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
48+
logger = _setup_logger(name, level)
49+
_setup_exception_logging(logger)
50+
return logger
51+
52+
53+
def _setup_logger(name: str, level: int = logging.INFO) -> logging.Logger:
54+
# Force configure the root logger with a NullHandler to prevent duplicate logs
55+
logging.basicConfig(handlers=[logging.NullHandler()], force=True)
2956
logger = logging.getLogger(name)
3057
if logger.hasHandlers():
3158
for h in logger.handlers:
3259
logger.removeHandler(h)
3360

34-
handler = colorlog.StreamHandler()
35-
handler.setFormatter(formatter)
36-
logger.addHandler(handler)
61+
logger.addHandler(stdout_handler)
62+
logger.addHandler(stderr_handler)
63+
3764
# Ensure the logger propagates to the root logger
3865
logger.propagate = True
3966
# Set the level on the logger itself
4067
logger.setLevel(level)
4168
return logger
69+
70+
71+
def _setup_exception_logging(logger: logging.Logger) -> None:
72+
def log_exception(exc_type, exc_value, exc_traceback):
73+
logger.exception("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
74+
75+
# Set the log_exception function as the exception hook
76+
sys.excepthook = log_exception

0 commit comments

Comments
 (0)