20

I am using the logging module in Python to write debug and error messages.

I want to write to file all messages of logging.DEBUG or greater.

However, I only want to print to the screen messages of logging.WARNING or greater.

Is this possible using just one Logger and one FileHandler?

cssndrx
  • 773
  • 2
  • 9
  • 17

4 Answers4

14

As it has been mentioned, handlers are so easy to create and add that you're probably better off just using two handlers. If, however, for some reason you want to stick to one, the Python logging cookbook has a section describing more or less what you want to do: logging to both console and file, but at different levels (it even shows you how to do different formatting). It does it with a single StreamHandler rather than a FileHandler, though:

import logging

# set up logging to file - see previous section for more details
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M',
                    filename='/temp/myapp.log',
                    filemode='w')
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# set a format which is simpler for console use
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(console)

# Now, we can log to the root logger, or any other logger. First the root...
logging.info('Jackdaws love my big sphinx of quartz.')

# Now, define a couple of other loggers which might represent areas in your
# application:

logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')

logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')

Edit: As discussed in the comments this code still generates two handlers, but "hides" one construction through the use of basicConfig(). I would strongly encourage you to create both explicitly.

bdeniker
  • 995
  • 1
  • 9
  • 21
  • This is still two handlers. Only you "masked" the creation of the `FileHandler` with `basicConfig(...)`, I'd rather create them both explicitly. – plundra Jul 19 '11 at 09:21
  • You're right, of course. However, I assumed this (or similar) is what the asker had in mind when s/he said s/he was outputting to two places with only one logger. I do have to agree with you on your other point as well: it's a lot clearer and neater to declare both handlers explicitly IMO. – bdeniker Jul 19 '11 at 09:25
3

You can append lots of handlers to the same logger with different loglevel, but root loglevel must be as verbose as the most verbose handler. This example log messages to file with log.debug() and log.info() to console:

log = logging.getLogger("syslog2elastic")
log.setLevel(logging.DEBUG) # this must be DEBUG to allow debug messages through

console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s.%(msecs)03d - %(name)s:%(lineno)d - %(levelname)s - %(message)s", "%Y%m%d%H%M%S")
console.setFormatter(formatter)
log.addHandler(console)

fh = RotatingFileHandler(args.logfile, maxBytes=104857600, backupCount=5)
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s.%(msecs)03d - %(message)s", "%Y%m%d%H%M%S")
fh.setFormatter(formatter)
log.addHandler(fh)
MortenB
  • 2,749
  • 1
  • 31
  • 35
2

No. File and Screen output means you need two handlers.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • With one handler, it is currently writing to both file and screen. But both at the same level (logging.DEBUG). – cssndrx Jul 11 '11 at 16:25
  • @cssndrx: "one handler, it is currently writing to both file and screen." I find that hard to understand. Please **update** the question to include the logging configuration you're using to make this happen. – S.Lott Jul 11 '11 at 16:35
  • @eryksun: Many things "can be done". We need to see what they're **actually** doing. – S.Lott Jul 11 '11 at 17:36
2

You can do this in the following way, making two loggers, one for file logging, another one for console logging. Make sure to set the root logger to the most verbose of the two.

import logging

logging.getLogger().setLevel(logging.DEBUG)  # This must be as verbose as the most verbose handler

formatter = logging.Formatter(
    '%(asctime)s.%(msecs)03d %(levelname)s %(module)s - %(funcName)s [%(lineno)s]: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
)

console_logging = logging.StreamHandler()
console_logging.setLevel(logging.WARNING)
console_logging.setFormatter(formatter)
logging.getLogger().addHandler(console_logging)

file_logging = logging.FileHandler('file.log')
file_logging.setLevel(logging.DEBUG)
file_logging.setFormatter(formatter)
logging.getLogger().addHandler(file_logging)
JoDavid
  • 393
  • 2
  • 8