loggingTools: tools for interfacing with the Python logging package

class fontTools.misc.loggingTools.CapturingLogHandler(logger, level)[source]

Acquire the I/O thread lock.


Add the specified filter to this handler.

assertRegex(regexp, msg=None)[source]

Tidy up any resources used by the handler.

This version removes the handler from an internal map of handlers, _handlers, which is used for handler lookup by name. Subclasses should ensure that this gets called from overridden close() methods.


Acquire a thread lock for serializing access to the underlying I/O.


Do whatever it takes to actually log the specified logging record.

This version is intended to be implemented by subclasses and so raises a NotImplementedError.


Determine if a record is loggable by consulting all the filters.

The default is to allow the record to be logged; any filter can veto this and the record is then dropped. Returns a zero value if a record is to be dropped, else non-zero.

Changed in version 3.2: Allow filters to be just callables.


Ensure all logging output has been flushed.

This version does nothing and is intended to be implemented by subclasses.


Format the specified record.

If a formatter is set, use it. Otherwise, use the default formatter for the module.


Conditionally emit the specified logging record.

Emission depends on filters which may have been added to the handler. Wrap the actual emission of the record with acquisition/release of the I/O thread lock. Returns whether the filter passed the record for emission.


Handle errors which occur during an emit() call.

This method should be called from handlers when an exception is encountered during an emit() call. If raiseExceptions is false, exceptions get silently ignored. This is what is mostly wanted for a logging system - most users will not care about errors in the logging system, they are more interested in application errors. You could, however, replace this with a custom handler if you wish. The record which was being processed is passed in to this method.

property name

Release the I/O thread lock.


Remove the specified filter from this handler.


Set the formatter for this handler.


Set the logging level of this handler. level must be an int or a str.

class fontTools.misc.loggingTools.ChannelsFilter(*names)[source]

Provides a hierarchical filter for log entries based on channel names.

Filters out records emitted from a list of enabled channel names, including their children. It works the same as the logging.Filter class, but allows the user to specify multiple channel names.

>>> import sys
>>> handler = logging.StreamHandler(sys.stdout)
>>> handler.setFormatter(logging.Formatter("%(message)s"))
>>> filter = ChannelsFilter("A.B", "C.D")
>>> handler.addFilter(filter)
>>> root = logging.getLogger()
>>> root.addHandler(handler)
>>> root.setLevel(level=logging.DEBUG)
>>> logging.getLogger('A.B').debug('this record passes through')
this record passes through
>>> logging.getLogger('A.B.C').debug('records from children also pass')
records from children also pass
>>> logging.getLogger('C.D').debug('this one as well')
this one as well
>>> logging.getLogger('A.B.').debug('also this one')
also this one
>>> logging.getLogger('A.F').debug('but this one does not!')
>>> logging.getLogger('C.DE').debug('neither this one!')

Determine if the specified record is to be logged.

Is the specified record to be logged? Returns 0 for no, nonzero for yes. If deemed appropriate, the record may be modified in-place.

class fontTools.misc.loggingTools.LevelFormatter(fmt=None, datefmt=None, style='%')[source]

Log formatter with level-specific formatting.

Formatter class which optionally takes a dict of logging levels to format strings, allowing to customise the log records appearance for specific levels.


A dictionary mapping logging levels to format strings. The * key identifies the default format string.


As per py:class:logging.Formatter


As per py:class:logging.Formatter

>>> import sys
>>> handler = logging.StreamHandler(sys.stdout)
>>> formatter = LevelFormatter(
...     fmt={
...         '*':     '[%(levelname)s] %(message)s',
...         'DEBUG': '%(name)s [%(levelname)s] %(message)s',
...         'INFO':  '%(message)s',
...     })
>>> handler.setFormatter(formatter)
>>> log = logging.getLogger('test')
>>> log.setLevel(logging.DEBUG)
>>> log.addHandler(handler)
>>> log.debug('this uses a custom format string')
test [DEBUG] this uses a custom format string
>>> log.info('this also uses a custom format string')
this also uses a custom format string
>>> log.warning("this one uses the default format string")
[WARNING] this one uses the default format string
localtime([seconds]) -> (tm_year,tm_mon,tm_mday,tm_hour,tm_min,


Convert seconds since the Epoch to a time tuple expressing local time. When ‘seconds’ is not passed in, convert the current time instead.

default_msec_format = '%s,%03d'
default_time_format = '%Y-%m-%d %H:%M:%S'

Format the specified record as text.

The record’s attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.


Format and return the specified exception information as a string.

This default implementation just uses traceback.print_exception()


This method is provided as an extension point for specialized formatting of stack information.

The input data is a string as returned from a call to traceback.print_stack(), but with the last trailing newline removed.

The base implementation just returns the value passed in.

formatTime(record, datefmt=None)

Return the creation time of the specified LogRecord as formatted text.

This method should be called from format() by a formatter which wants to make use of a formatted time. This method can be overridden in formatters to provide for any specific requirement, but the basic behaviour is as follows: if datefmt (a string) is specified, it is used with time.strftime() to format the creation time of the record. Otherwise, an ISO8601-like (or RFC 3339-like) format is used. The resulting string is returned. This function uses a user-configurable function to convert the creation time to a tuple. By default, time.localtime() is used; to change this for a particular formatter instance, set the ‘converter’ attribute to a function with the same signature as time.localtime() or time.gmtime(). To change it for all formatters, for example if you want all logging times to be shown in GMT, set the ‘converter’ attribute in the Formatter class.


Check if the format uses the creation time of the record.

class fontTools.misc.loggingTools.LogMixin[source]

Mixin class that adds logging functionality to another class.

You can define a new class that subclasses from LogMixin as well as other base classes through multiple inheritance. All instances of that class will have a log property that returns a logging.Logger named after their respective <module>.<class>.

For example:

>>> class BaseClass(object):
...     pass
>>> class MyClass(LogMixin, BaseClass):
...     pass
>>> a = MyClass()
>>> isinstance(a.log, logging.Logger)
>>> print(a.log.name)
>>> class AnotherClass(MyClass):
...     pass
>>> b = AnotherClass()
>>> isinstance(b.log, logging.Logger)
>>> print(b.log.name)
property log
class fontTools.misc.loggingTools.Timer(logger=None, msg=None, level=None, start=None)[source]

Keeps track of overall time and split/lap times.

>>> import time
>>> timer = Timer()
>>> time.sleep(0.01)
>>> print("First lap:", timer.split())
First lap: ...
>>> time.sleep(0.02)
>>> print("Second lap:", timer.split())
Second lap: ...
>>> print("Overall time:", timer.time())
Overall time: ...

Can be used as a context manager inside with-statements.

>>> with Timer() as t:
...     time.sleep(0.01)
>>> print("%0.3f seconds" % t.elapsed)
0... seconds

If initialised with a logger, it can log the elapsed time automatically upon exiting the with-statement.

>>> import logging
>>> log = logging.getLogger("my-fancy-timer-logger")
>>> configLogger(logger=log, level="DEBUG", format="%(message)s", stream=sys.stdout)
>>> with Timer(log, 'do something'):
...     time.sleep(0.01)
Took ... to do something

The same Timer instance, holding a reference to a logger, can be reused in multiple with-statements, optionally with different messages or levels.

>>> timer = Timer(log)
>>> with timer():
...     time.sleep(0.01)
elapsed time: ...s
>>> with timer('redo it', level=logging.INFO):
...     time.sleep(0.02)
Took ... to redo it

It can also be used as a function decorator to log the time elapsed to run the decorated function.

>>> @timer()
... def test1():
...    time.sleep(0.01)
>>> @timer('run test 2', level=logging.INFO)
... def test2():
...    time.sleep(0.02)
>>> test1()
Took ... to run 'test1'
>>> test2()
Took ... to run test 2
default_format = 'Took %(time).3fs to %(msg)s'
default_msg = 'elapsed time: %(time).3fs'
formatTime(msg, time)[source]

Format ‘time’ value in ‘msg’ and return formatted string. If ‘msg’ contains a ‘%(time)’ format string, try to use that. Otherwise, use the predefined ‘default_format’. If ‘msg’ is empty or None, fall back to ‘default_msg’.


Reset timer to ‘start_time’ or the current time.


Split and return the lap time (in seconds) in between splits.


Return the overall time (in seconds) since the timer started.


A more sophisticated logging system configuation manager.

This is more or less the same as logging.basicConfig(), with some additional options and defaults.

The default behaviour is to create a StreamHandler which writes to sys.stderr, set a formatter using the DEFAULT_FORMATS strings, and add the handler to the top-level library logger (“fontTools”).

A number of optional keyword arguments may be specified, which can alter the default behaviour.

  • logger – Specifies the logger name or a Logger instance to be configured. (Defaults to “fontTools” logger). Unlike basicConfig, this function can be called multiple times to reconfigure a logger. If the logger or any of its children already exists before the call is made, they will be reset before the new configuration is applied.

  • filename – Specifies that a FileHandler be created, using the specified filename, rather than a StreamHandler.

  • filemode – Specifies the mode to open the file, if filename is specified. (If filemode is unspecified, it defaults to a).

  • format – Use the specified format string for the handler. This argument also accepts a dictionary of format strings keyed by level name, to allow customising the records appearance for specific levels. The special '*' key is for ‘any other’ level.

  • datefmt – Use the specified date/time format.

  • level – Set the logger level to the specified level.

  • stream – Use the specified stream to initialize the StreamHandler. Note that this argument is incompatible with filename - if both are present, stream is ignored.

  • handlers – If specified, this should be an iterable of already created handlers, which will be added to the logger. Any handler in the list which does not have a formatter assigned will be assigned the formatter created in this function.

  • filters – If specified, this should be an iterable of already created filters. If the handlers do not already have filters assigned, these filters will be added to them.

  • propagate – All loggers have a propagate attribute which determines whether to continue searching for handlers up the logging hierarchy. If not provided, the “propagate” attribute will be set to False.

fontTools.misc.loggingTools.deprecateArgument(name, msg, category=<class 'UserWarning'>)[source]

Raise a warning about deprecated function argument ‘name’.

fontTools.misc.loggingTools.deprecateFunction(msg, category=<class 'UserWarning'>)[source]

Decorator to raise a warning when a deprecated function is called.