问题
I have this code which works just fine for me.
import logging
import logging.handlers
logger = None
def create_logger():
global logger
logger = logging.getLogger('Logger')
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler("C:/Users/user/Desktop/info.log", maxBytes=1000000, backupCount=20)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
create_logger()
logger.info("Text info")
logger.debug("Text debug")
logger.warning("Text warning")
logger.error("Text error")
logger.critical("Text critical")
And the output looks great:
2017-12-19 15:06:43,021 - Logger - INFO - Text info
2017-12-19 15:06:43,021 - Logger - DEBUG - Text debug
2017-12-19 15:06:43,022 - Logger - WARNING - Text warning
2017-12-19 15:06:43,022 - Logger - ERROR - Text error
2017-12-19 15:06:43,022 - Logger - CRITICAL - Text critical
Well, I want to add a new logging level like this:
logger.message("Text message")
And the output should be like this
2017-12-19 15:06:43,022 - Logger - MESSAGE - Text message
Thanks
回答1:
From Logging documentation (emphasis added):
Defining your own levels is possible, but should not be necessary, as the existing levels have been chosen on the basis of practical experience. However, if you are convinced that you need custom levels, great care should be exercised when doing this, and it is possibly a very bad idea to define custom levels if you are developing a library. That’s because if multiple library authors all define their own custom levels, there is a chance that the logging output from such multiple libraries used together will be difficult for the using developer to control and/or interpret, because a given numeric value might mean different things for different libraries.
An overview of default logging levels:
But if you still want to, you can make your own log level:
In the logging
-module, _levelToName
and _nameToLevel
are mappings between logging names and levels. Instead of manually adding to them, the addLevelName()
function does this for you.
Here, a new logging level called MESSAGE is added with log level 25:
import logging
# Define MESSAGE log level
MESSAGE = 25
# "Register" new loggin level
logging.addLevelName(MESSAGE, 'MESSAGE') # addLevelName(25, 'MESSAGE')
# Verify
assert logging.getLevelName(MESSAGE) == 'MESSAGE'
If you don't want to make your own logger class but still wants to log other log levels, then you can use the Logger.log(level, msg)
-method on the traditional loggers:
logging.log(MESSAGE, 'This is a message')
EDIT: Add message directly
def message(self, msg, *args, **kwargs):
if self.isEnabledFor(MESSAGE):
self._log(MESSAGE, msg, args, **kwargs)
Make the message()
-function available in logging
:
logging.message = message
# or setattr(logging, 'message', message)
Make the message()
-function available in the logger:
logging.Logger.message = message
# or setattr(logging.Logger, 'message', message)
Make custom Logger class
You could make your own logger class to make a message(msg)
-method, to be used similarily as the others (e.g. info(msg)
, warning(msg)
, etc.)
In the following example, a new logger is made with a message(msg)
-method to log MESSAGE:
class MyLogger(logging.Logger):
def message(self, msg, *args, **kwargs):
if self.isEnabledFor(MESSAGE):
self._log(MESSAGE, msg, args, **kwargs)
Get logger
I'm not sure of what's the best way to make it work with logging.getLogger(name)
, but below are two approaches.
Ref. comments, I believe the first approach is better:
Either make the new logger the default logging class, which means new logger instances will be of the MyLogger
class instead of the default logging.Logger
class:
logging.setLoggerClass(MyLogger)
logger = logging.getLogger('A new logger name')
logger.message('This seems to work')
assert isInstance(logger, MyLogger)
Or just make an instance of the logger and add it to the loggerDict
in the active logging.Manager
instance (EDIT: not recommended, see comments):
my_logger = MyLogger('Foo')
logging.Logger.manager.loggerDict['Foo'] = my_logger
logger = logging.getLogger('Foo')
logger.message('This is the same instance as my_logger')
assert logger is my_logger
Use the new log level
# Use the new logger class
logger.warning('Custom log levels might be a bad idea')
logger.message('Here is a message')
# Log with custom log level:
logger.log(MESSAGE, 'This is a message')
This assumes that MESSAGE
is predefined as an integer numerical value representing the log level. (E.g. 25 as previously mentioned)
来源:https://stackoverflow.com/questions/47888855/python3-add-logging-level