I am trying to separate the log levels into separate files (one for each level). At the moment I have defined a file for each level but with my current configuration the upper levels are propagated to the lower levels.
My log configuration is:
version: 1
formatters:
standard:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
error:
format: "%(levelname)s <PID %(process)d:%(processName)s> %(name)s.%(funcName)s(): %(message)s"
handlers:
console:
class: logging.StreamHandler
formatter: standard
level: DEBUG
debug_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: standard
level: DEBUG
filename: logs/debug.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
info_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: standard
level: INFO
filename: logs/info.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
warning_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: standard
level: WARNING
filename: logs/warning.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
error_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: error
level: ERROR
filename: logs/error.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
critical_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: error
level: CRITICAL
filename: logs/critical.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
loggers:
development:
handlers: [ console, debug_file_handler ]
propagate: false
production:
handlers: [ info_file_handler, warning_file_handler, error_file_handler, critical_file_handler ]
propagate: false
root:
handlers: [ debug_file_handler, info_file_handler, warning_file_handler, error_file_handler, critical_file_handler ]
And I load the configuration and set the logger like this:
with open(path_log_config_file, 'r') as config_file:
config = yaml.safe_load(config_file.read())
logging.config.dictConfig(config)
logger = logging.getLogger(LOGS_MODE)
logger.setLevel(LOGS_LEVEL)
Where LOGS_MODE
and LOGS_LEVEL
are defined in a configuration file in my project:
# Available loggers: development, production
LOGS_MODE = 'production'
# Available levels: CRITICAL = 50, ERROR = 40, WARNING = 30, INFO = 20, DEBUG = 10
LOGS_LEVEL = 20
And when I want to use the logger I do:
from src.logger import logger
I have found these answers where they mention to use filters: #1 #2 but both of them say to use different handlers and specify the level for each one but with this approach I'll have to import different loggers in some cases instead of only one. Is this the only way to achieve it?
Regards.
UPDATE 1:
As I am using a YAML file to load the logger configuration I found this answer #3:
So I have defined the filters in my file logger.py
:
with open(path_log_config_file, 'rt') as config_file:
config = yaml.safe_load(config_file.read())
logging.config.dictConfig(config)
class InfoFilter(logging.Filter):
def __init__(self):
super().__init__()
def filter(self, record):
return record.levelno == logging.INFO
class WarningFilter(logging.Filter):
def __init__(self):
super().__init__()
def filter(self, record):
return record.levelno == logging.WARNING
class ErrorFilter(logging.Filter):
def __init__(self):
super().__init__()
def filter(self, record):
return record.levelno == logging.ERROR
class CriticalFilter(logging.Filter):
def __init__(self):
super().__init__()
def filter(self, record):
return record.levelno == logging.CRITICAL
logger = logging.getLogger(LOGS_MODE)
logger.setLevel(LOGS_LEVEL)
And in the YAML file:
filters:
info_filter:
(): src.logger.InfoFilter
warning_filter:
(): src.logger.WarningFilter
error_filter:
(): src.logger.ErrorFilter
critical_filter:
(): src.logger.CriticalFilter
handlers:
console:
class: logging.StreamHandler
formatter: standard
level: DEBUG
debug_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: standard
level: DEBUG
filename: logs/debug.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
info_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: standard
level: INFO
filename: logs/info.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
filters: [ info_filter ]
warning_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: standard
level: WARNING
filename: logs/warning.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
filters: [ warning_filter ]
error_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: error
level: ERROR
filename: logs/error.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
filters: [ error_filter ]
critical_file_handler:
class: logging.handlers.RotatingFileHandler
formatter: error
level: CRITICAL
filename: logs/critical.log
encoding: utf8
mode: "w"
maxBytes: 10485760 # 10MB
backupCount: 1
filters: [ critical_filter ]
My problem now is in the filter section. I don't know how to specify the name of each class. In the response #3 he uses __main__.
because he is running the script directly, not as a module and doesn't says how to do it if you use a module.
Reading the User-defined objects doc reference I've tried to use ext://
as it's said in the Access to external objects section but I get the same error as when trying to specify the hierarchy with src.logger.InfoFilter
.
logging.config.dictConfig(config)
File "/usr/lib/python3.8/logging/config.py", line 808, in dictConfig
dictConfigClass(config).configure()
File "/usr/lib/python3.8/logging/config.py", line 553, in configure
raise ValueError('Unable to configure '
ValueError: Unable to configure filter 'info_filter'
python-BaseException
My project tree is (only the important part is shown):
.
├── resources
│ ├── log.yaml
│ └── properties.py
├── src
│ ├── main.py
│ └── logger.py
└── ...