Issue
I have custom logger with default levels. I want to show logging.exception
"level" in logfile, but exclude it from stdout (I want to leave short error messages in stdout). It's important that I don't want to declare custom level, because I want to leave some customisation space for user (switch off my logging a.e.).
So this is the code:
import logging
import sys
import os
from pathlib import Path
from dataclasses import fields
from API.global_constants.dataclasses import LoggerTemplate
from API.global_constants.vars import LOGGER_NAME
def set_logger(**kwargs):
logger_template = LoggerTemplate()
for key, value in kwargs.items():
if key in [field.name for field in fields(logger_template)]:
setattr(logger_template, key, value)
if not isinstance(logger_template.logger, logging.Logger):
logger_template.logger = logging.getLogger(LOGGER_NAME)
else: # means logger has been overriden by user
return logger_template.logger
logger = logger_template.logger
if not logger_template.enable_logger:
logger.setLevel(logging.CRITICAL + 1)
else:
logger.setLevel(logging.DEBUG)
std_formatter = Formatter()
std_handler = logging.StreamHandler(sys.stdout)
std_handler.setLevel(logger_template.log_std_level)
std_handler.setFormatter(std_formatter)
logger.addHandler(std_handler)
if not logger_template.enable_logfile:
logger.info(f'Logging started without file.')
else:
file_formatter = logging.Formatter(f"[%(asctime)s] %(levelname)-8s [%(filename)s/%(funcName)s:%(lineno)s] %(message)s")
Path.mkdir(Path(logger_template.logfile_path), parents=True, exist_ok=True)
log_file_path = Path(logger_template.logfile_path) / Path(str(logger_template.logfile_name))
file_handler = logging.FileHandler(log_file_path)
file_handler.setLevel(logger_template.logfile_level)
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
logger.info(f'Logging started in the [{log_file_path}].')
return logger
class Formatter(logging.Formatter):
def format(self, record):
color = {
logging.CRITICAL: 31,
logging.ERROR: 31,
logging.FATAL: 31,
logging.WARNING: 33,
logging.DEBUG: 36,
logging.INFO: 32
}.get(record.levelno, 0)
self._style._fmt = f"\033[37m[%(asctime)s]\033[0m \033[{color}m%(levelname)-8s\033[0m %(message)s"
return super().format(record)
if __name__ == '__main__':
logger1 = set_logger(logfile_name='some_user_defined_name')
logger1.debug('test message')
logger1.info('test message')
logger1.warning('test message')
logger1.error('test message')
logger1.critical('test message')
logger1.exception('test message')
And the dataclass (as a logging config template) from the set_logger
function:
import logging
import sys
import os
from dataclasses import dataclass
from typing import Tuple
from datetime import datetime
from pathlib import Path
@dataclass
class LoggerTemplate:
enable_logger: bool = True
enable_logfile: bool = False
logger: logging.Logger = None
logfile_path: str = Path(f'logfiles_[{os.path.basename(sys.argv[0])}]')
logfile_name: str = Path(f"{datetime.now().strftime('__%d.%m.%Y__%H.%M.%S')}__.log")
logfile_level: int = logging.INFO
log_std_level: int = logging.DEBUG
So, how can i print logging messages, including logging.exception
messages in logfile, but exclude logging.exception
messages in stdout, replacing it with logging.error
short variants ?
Solution
I'll give you a simple example which sticks close to the core logging
APIs. See this script:
import logging
import sys
class NoExceptionFormatter(logging.Formatter):
def formatException(self, exc_info):
return ''
def main():
logger = logging.getLogger()
ch = logging.StreamHandler()
logger.addHandler(ch)
fh = logging.FileHandler('so_77873508.log', 'w')
logger.addHandler(fh)
FMT = '%(levelname)-8s %(message)s'
f1 = logging.Formatter(FMT)
f2 = NoExceptionFormatter(FMT)
ch.setFormatter(f2)
fh.setFormatter(f1)
logger.setLevel(logging.DEBUG)
try:
1 / 0
except Exception as e:
logger.exception(f'Failed: {e}')
if __name__ == '__main__':
sys.exit(main())
When run, it prints no exception to the console but does to the file:
$ python so_77873508.py
ERROR Failed: division by zero
$ more so_77873508.log
ERROR Failed: division by zero
Traceback (most recent call last):
File "/path/redacted/so_77873508.py", line 22, in main
1 / 0
ZeroDivisionError: division by zero
Answered By - Vinay Sajip
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.