Python3 add logging level

孤街浪徒 提交于 2020-01-10 14:32:04

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!