4.11.4. rootpy.logger Package

4.11.4.1. rootpy‘s logging subsystem

rootpy overrides the default logging class, inserting a check that there exists a default logging handler. If there is not, it adds one.

In additon, this can be used to intercept ROOT’s log messages and redirect them through python’s logging subsystem

Example use:

# Disable colored logging (not needed if writing into a file, this is automatic).
# Must be done before :py:mod:`rootpy` logs any messages.
import logging; logging.basicConfig(level=logging.DEBUG)

from rootpy import log; log = log["/myapp"]
log.debug("Hello") # Results in "DEBUG:myapp] Hello"

# Suppress all myapp debug and info messages
log.setLevel(log.WARNING)
log.debug("Hello") # No effect

mymod = log["mymod"]
mymod.warning("Hello") # Results in "WARNING:myapp.mymod] Hello"

# Suppress all rootpy debug and info messages
log["/rootpy"].setLevel(log.WARNING)

# Suppress messages coming from TCanvas like
# INFO:ROOT.TCanvas.Print] png file /path/to/file.png has been created
log["/ROOT.TCanvas.Print"].setLevel(log.WARNING)

# Suppress warning messages coming the ``TClass`` constructor:
log["/ROOT.TClass.TClass"].setLevel(log.ERROR)

# Precisely remove messages containing the text "no dictionary for class"
# (doesn't work when attached to parent logger)
import logging
class NoDictMessagesFilter(logging.Filter):
    def filter(self, record):
        return "no dictionary for class" not in record.msg
log["/ROOT.TClass.TClass"].addFilter(NoDictMessagesFilter())

# Turn ROOT errors into exceptions
from rootpy.logger.magic import DANGER
DANGER.enable = True

import ROOT
ROOT.Error("test", "Test fatal")
# Result:
# ERROR:ROOT.test] Test fatal
# Traceback (most recent call last):
#   File "test.py", line 36, in <module>
#     ROOT.Fatal("test", "Test fatal")
#   File "test.py", line 36, in <module>
#     ROOT.Fatal("test", "Test fatal")
#   File "rootpy/logger/roothandler.py", line 40, in python_logging_error_handler
#     raise ROOTError(level, location, msg)
# rootpy.ROOTError: level=6000, loc='test', msg='Test fatal'

# Primitive function tracing:
@log.trace()
def salut():
    return

@log.trace()
def hello(what):
    salut()
    return "42"

hello("world")
# Result:
#   DEBUG:myapp.trace.hello] > ('world',) {}
#   DEBUG:myapp.trace.salut]  > () {}
#   DEBUG:myapp.trace.salut]  < return None [0.00 sec]
#   DEBUG:myapp.trace.hello] < return 42 [0.00 sec]
rootpy.logger.log_trace(logger, level=10, show_enter=True, show_exit=True)[source]

log a statement on function entry and exit

4.11.4.2. rootpy.logger.color Package

Provides a CustomFormatter and CustomColoredFormatter which are enable to insert ANSI color codes.

rootpy.logger.color.default_log_handler(level=10, singleton={})[source]

Instantiates a default log handler, with colour if we’re connected to a terminal.

4.11.4.3. rootpy.logger.extended_logger Package

class rootpy.logger.extended_logger.ExtendedLogger(name)[source]

Bases: logging.Logger

A logger class which provides a few niceties, including automatically enabling logging if no handlers are available.

basic_config_colorized()[source]

Configure logging with a coloured output

getChild(suffix)[source]

Taken from CPython 2.7, modified to remove duplicate prefix and suffixes

trace(level=10, show_enter=True, show_exit=True)[source]

Show function entry and exit with values, defaults to debug log level.

4.11.4.4. rootpy.logger.multilogging Package

An example script showing how to use logging with multiprocessing.

The basic strategy is to set up a listener process which can have any logging configuration you want - in this example, writing to rotated log files. Because only the listener process writes to the log files, you don’t have file corruption caused by multiple processes trying to write to the file.

The listener process is initialised with a queue, and waits for logging events (LogRecords) to appear in the queue. When they do, they are processed according to whatever logging configuration is in effect for the listener process.

Other processes can delegate all logging to the listener process. They can have a much simpler logging configuration: just one handler, a QueueHandler, needs to be added to the root logger. Other loggers in the configuration can be set up with levels and filters to achieve the logging verbosity you need.

A QueueHandler processes events by sending them to the multiprocessing queue that it’s initialised with.

In this demo, there are some worker processes which just log some test messages and then exit.

This script was tested on Ubuntu Jaunty and Windows 7.

Copyright (C) 2010 Vinay Sajip. All Rights Reserved.

class rootpy.logger.multilogging.QueueHandler(queue)[source]

Bases: logging.Handler

This is a logging handler which sends events to a multiprocessing queue.

The plan is to add it to Python 3.2, but this can be copy pasted into user code for use with earlier Python versions.

emit(record)[source]

Emit a record.

Writes the LogRecord to the queue.

rootpy.logger.multilogging.random() → x in the interval [0, 1).

4.11.4.5. Scary magic in rootpy.logger.magic

Here be dragons.

This module contains hackery to bend the CPython interpreter to our will.

It’s necessary because it’s not possible to throw an exception from within a ctypes callback. Instead, the exception is thrown from a line tracer which we forcably insert into the appropriate frame. Then we make that frame’s next opcode a JUMP_ABSOLUTE to the last line of code. Yes.

This is a bad idea and should never be used anywhere important where reliability is a concern. Also, if you like your sanity. This thing will break backtraces when you least expect it, leading to you looking at the wrong thing.

What lies within is the product of a sick mind and should never be exposed to humanity.

rootpy.logger.magic.get_frame_pointers(frame=None)[source]

Obtain writable pointers to frame.f_trace and frame.f_lineno.

Very dangerous. Unlikely to be portable between python implementations.

This is hard in general because the PyFrameObject can have a variable size depending on the build configuration. We can get it reliably because we can determine the offset to f_tstate by searching for the value of that pointer.

rootpy.logger.magic.get_seh()[source]

Makes a function which can be used to set the ROOT error handler with a python function and returns the existing error handler.

rootpy.logger.magic.get_threadstate_idx()[source]

How many pointers into PyFrame is the f_tstate variable?

rootpy.logger.magic.re_execute_with_exception(frame, exception, traceback)[source]

Dark magic. Causes frame to raise an exception at the current location with traceback appended to it.

Note that since the line tracer is raising an exception, the interpreter disables the global trace, so it’s not possible to restore the previous tracing conditions.

rootpy.logger.magic.set_linetrace_on_frame(f, localtrace=None)[source]

Non-portable function to modify linetracing.

Remember to enable global tracing with sys.settrace(), otherwise no effect!