JR_
JR_

Reputation: 193

Python logging: log only to handlers not to root

I have the following class:

class Log(object):

# class new
#new is used instead of init because __new__ is able to return (where __init__ can't)
def __new__(self, name, consolelevel, filelevel):

    formatter = logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s')

    #Create consolehandler and set formatting (level is set in the ROOT)
    consolehandler = StreamHandler()
    consolehandler.setFormatter(formatter)

    #Create filehandler, set level and set formatting
    filehandler = FileHandler(name + '.log')
    filehandler.setLevel(filelevel)
    filehandler.setFormatter(formatter)

    #Create the root logger, add console and file logger. Set the rootlevel == consolelevel.
    self.root = logging.getLogger(name)

    #causing me problems....
    self.root.setLevel(consolelevel)

    self.root.addHandler(consolehandler)
    self.root.addHandler(filehandler)
    self.root.propagate = True

    return self.root

# Close the logger object
def close():
    # to be implemented
    pass

I use this class to log to the console and to a file (depending on the set level). The problem is that the root level seems to be leading for the addhandlers. Is there a way to disable this? Now I set the rootlevel to the same level as the consolelevel but this does not work...

Any advice?

Thanks in advance and with best regards,

JR

Upvotes: 0

Views: 923

Answers (2)

JR_
JR_

Reputation: 193

This is a simplified version of the module I am making. The module contains a few classes that all need a logging functionality. Each class logs to a different file and and it should also be possible to change the file handler levels between classes (e.g. gamepad class: console.debug and filehandler.info and MQTT class: console info and filehandler.debug).

Therefor I thought that setting up a log class would be the easiest way. Please bear in mind that I usually do electronics but now combined with python. So my skills are pretty basic....

#!/bin/env python2.7

from future import division from operator import * import logging from logging import FileHandler from logging import StreamHandler import pygame import threading from pygame.locals import * import mosquitto import time from time import sleep import sys

class ConsoleFileLogger(object):

# class constructor
def __init__(self, filename, loggername, rootlevel, consolelevel, filelevel):

    # logger levels: DEBUG, INFO, WARNING, ERROR, CRITICAL

    # define a root logger and set its ROOT logging level
    logger = logging.getLogger(loggername)
    logger.setLevel(rootlevel)

    # define a Handler which writes messages or higher to the sys.stderr (Console)
    self.console = logging.StreamHandler()
    # set the logging level
    self.console.setLevel(consolelevel)

    # define a Handler which writes messages to a logfile
    self.logfile = logging.FileHandler(filename + '.log')
    # set the logging level
    self.logfile.setLevel(filelevel)

    # create formatter and add it to the handlers
    formatter = logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s')
    self.console.setFormatter(formatter)
    self.logfile.setFormatter(formatter)

    # add the handlers to the root logger
    logger.addHandler(self.console)
    logger.addHandler(self.logfile)

    self._logger = logger

# set a net instance of the logger
def set(self):
    return self._logger

# Stop and remove the ConsoleFileLogger object
def remove(self):
    self._logger.removeHandler(self.console)
    self._logger.removeHandler(self.logfile)
    self._logfile.FileHandler().close()

class Gamepad():

# class constructor
def __init__(self, mqttgamepad):
    self.logger = ConsoleFileLogger('BaseLogFiles/Gamepad', 'Gamepad', logging.INFO, logging.INFO, logging.INFO).set()

    if joystickcount == 0:
        self.logger.error('No gamepad connected')
    elif joystickcount == 1:
        self.gamepad = pygame.joystick.Joystick(0)
        self.gamepad.init()
        self.logger.debug('Joystick name %s', self.gamepad.get_name())
        self.logger.debug('nb of axes = %s', self.gamepad.get_numaxes())
        self.logger.debug('nb of balls = %s', self.gamepad.get_numballs())
        self.logger.debug('nb of buttons = %s', self.gamepad.get_numbuttons())
        self.logger.debug('nb of mini joysticks = %s', self.gamepad.get_numhats())
    elif joystickcount > 1:
        self.logger.error('only one gamepad is allowed')

def run(self):
    self.logger.debug('gamepad running')

class MQTTClient():

def __init__(self, clientname):
    self.logger = ConsoleFileLogger('BaseLogFiles/MQTT/Pub', clientname, logging.DEBUG, logging.DEBUG, logging.DEBUG).set()
    self.logger.debug('test')

def run(self):
       self.logger.info('Connection MQTT Sub OK')

def main(): logger = ConsoleFileLogger('BaseLogFiles/logControlCenterMain', 'ControlCenterMain', logging.DEBUG, logging.DEBUG, logging.DEBUG).set()

mqttclient = MQTTClient("MQTTClient")
mqttclient.connect()

gamepad = Gamepad(mqttclient)

if gamepad.initialized():
    gamepadthread = threading.Thread(target=gamepad.run)
    gamepadthread.start()

    mqtttpubhread = threading.Thread(target=mqttclient.run)
    mqtttpubhread.start()

logger.info('BaseMain started')

# Monitor the running program for a KeyboardInterrupt exception
# If this is the case all threads and other methods can be closed the correct way :)
while 1:
    try:
        sleep(1)
    except KeyboardInterrupt:
        logger.info('Ctrl-C pressed')
        gamepad.stop()
        mqttclient.stop()
        logger.info('BaseMain stopped')
        sys.exit(0)

if name == 'main': main()

Upvotes: 0

Bakuriu
Bakuriu

Reputation: 101959

A problem that I can see in your code is that it will add more handlers whenever you instantiate the Log class. You probably do not want this.

Keep in mind that getLogger returns always the same instance when called with the same argument, and basically it implements the singleton pattern. Hence when you later call addHandler it will add a new handler everytime.

The way to deal with logging is to create a logger at the module level and use it.

Also I'd avoid using __new__. In your case you can use a simple function. And note that your Log.close method wont work, because your __new__ method does not return a Log instance, and thus the returned logger doesn't have that method.

Regarding the level of the logger, I don't understand why you do not set the level on the consolehandler but on the whole logger.

Upvotes: 1

Related Questions