Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python3 add logging level

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

like image 363
Alex Avatar asked Dec 19 '17 14:12

Alex


People also ask

How do you create a logging level in Python?

Configuring Logging Creating loggers, handlers, and formatters explicitly using Python code that calls the configuration methods listed above. Creating a logging config file and reading it using the fileConfig() function. Creating a dictionary of configuration information and passing it to the dictConfig() function.

How do I create a multiple logging level in Python?

You can set a different logging level for each logging handler but it seems you will have to set the logger's level to the "lowest". In the example below I set the logger to DEBUG, the stream handler to INFO and the TimedRotatingFileHandler to DEBUG. So the file has DEBUG entries and the stream outputs only INFO.

How do I increase my logging level?

You can change the logging level or trace level (also called the debug level) of a running process by sending a USR1 or USR2 signal to the process. Sending a USR1 signal changes the logging level and sending a USR2 signal changes the trace level.

What is level in logging in Python?

Python Logging Levels There are six log levels in Python; each level is associated with an integer that indicates the log severity: NOTSET=0, DEBUG=10, INFO=20, WARN=30, ERROR=40, and CRITICAL=50.


1 Answers

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:

enter image description here

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)

like image 153
Thomas Fauskanger Avatar answered Sep 22 '22 05:09

Thomas Fauskanger