# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8
#
# MDAnalysis --- http://www.MDAnalysis.org
# Copyright (c) 2006-2015 Naveen Michaud-Agrawal, Elizabeth J. Denning, Oliver Beckstein
# and contributors (see AUTHORS for the full list)
#
# Released under the GNU Public Licence, v2 or any higher version
#
# Please cite your use of MDAnalysis in published work:
#
# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein.
# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations.
# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787
#
"""
Setting up logging --- :mod:`MDAnalysis.lib.log`
====================================================
Configure logging for MDAnalysis. Import this module if logging is
desired in application code.
Logging to a file and the console is set up by default as described
under `logging to multiple destinations`_.
The top level logger of the library is named *MDAnalysis* by
convention; a simple logger that writes to the console and logfile can
be created with the :func:`create` function. This only has to be done
*once*. For convenience, the default MDAnalysis logger can be created
with :func:`MDAnalysis.start_logging`::
import MDAnalysis
MDAnalysis.start_logging()
Once this has been done, MDAnalysis will write messages to the logfile
(named `MDAnalysis.log` by default but this can be changed with the
optional argument to :func:`~MDAnalysis.start_logging`).
Any code can log to the MDAnalysis logger by using ::
import logging
logger = logging.getLogger('MDAnalysis.MODULENAME')
# use the logger, for example at info level:
logger.info("Starting task ...")
The important point is that the name of the logger begins with
"MDAnalysis.".
.. _logging to multiple destinations:
http://docs.python.org/library/logging.html?#logging-to-multiple-destinations
.. SeeAlso:: The :mod:`logging` module in the standard library contains
in depth documentation about using logging.
Convenience functions
---------------------
Two convenience functions at the top level make it easy to start and
stop the default *MDAnalysis* logger.
.. autofunction:: MDAnalysis.start_logging
.. autofunction:: MDAnalysis.stop_logging
Other functions and classes for logging purposes
------------------------------------------------
.. autogenerated, see Online Docs
"""
from __future__ import division
import sys
import logging
from .. import version
[docs]def start_logging(logfile="MDAnalysis.log", version=version.__version__):
"""Start logging of messages to file and console.
The default logfile is named `MDAnalysis.log` and messages are
logged with the tag *MDAnalysis*.
"""
create("MDAnalysis", logfile=logfile)
logging.getLogger("MDAnalysis").info(
"MDAnalysis %s STARTED logging to %r", version, logfile)
[docs]def stop_logging():
"""Stop logging to logfile and console."""
logger = logging.getLogger("MDAnalysis")
logger.info("MDAnalysis STOPPED logging")
clear_handlers(logger) # this _should_ do the job...
[docs]def create(logger_name="MDAnalysis", logfile="MDAnalysis.log"):
"""Create a top level logger.
- The file logger logs everything (including DEBUG).
- The console logger only logs INFO and above.
Logging to a file and the console as described under `logging to
multiple destinations`_.
The top level logger of MDAnalysis is named *MDAnalysis*. Note
that we are configuring this logger with console output. If a root
logger also does this then we will get two output lines to the
console.
.. _logging to multiple destinations:
http://docs.python.org/library/logging.html?#logging-to-multiple-destinations
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)
# handler that writes to logfile
logfile_handler = logging.FileHandler(logfile)
logfile_formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
logfile_handler.setFormatter(logfile_formatter)
logger.addHandler(logfile_handler)
# define a Handler which writes INFO messages or higher to the sys.stderr
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# set a format which is simpler for console use
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
return logger
[docs]def clear_handlers(logger):
"""clean out handlers in the library top level logger
(only important for reload/debug cycles...)
"""
for h in logger.handlers:
logger.removeHandler(h)
[docs]class NullHandler(logging.Handler):
"""Silent Handler.
Useful as a default::
h = NullHandler()
logging.getLogger("MDAnalysis").addHandler(h)
del h
see the advice on logging and libraries in
http://docs.python.org/library/logging.html?#configuring-logging-for-a-library
"""
def emit(self, record):
pass
[docs]def echo(s=''):
"""Simple string output that immediately prints to the console."""
sys.stderr.write(s)
sys.stderr.flush()
[docs]class ProgressMeter(object):
"""Simple progress meter
Usage::
u = Universe(PSF, DCD)
pm = ProgressMeter(u.trajectory.n_frames, interval=100)
for ts in u.trajectory:
pm.echo(ts.frame)
...
For a trajectory with 10000 frames this will produce output such
as ::
Step 100/10000 [ 1.0%]
Step 200/10000 [ 2.0%]
...
Because the default *format* string ::
"Step %(step)5d/%(numsteps)d [%(percentage)5.1f%%]\\r"
ends with a carriage return ``\\r`` and not a newline ``\\n``, the
lines will be printed on top of each other.
It is possible to embed (almost) arbitrary additional data in the
format string, for example a current RMSD value:
pm = ProgressMeter(u.trajectory.n_frames, interval=100,
"RMSD %(rmsd)5.2f at %(step)5d/%(numsteps)d [%(percentage)5.1f%%]\\r")
for ts in u.trajectory:
pm.echo(ts.frame, rmsd=current_rmsd)
...
This will print something like
RMSD 1.02 at 100/10000 [ 1.0%]
RMSD 1.89 at 200/10000 [ 2.0%]
...
"""
def __init__(self, numsteps, format=None, interval=10, offset=0, quiet=False):
"""Set up the ProgressMeter
:Arguments:
*numsteps*
total number of steps
:Keywords:
*interval*
only calculate progress every *interval* steps [10]
*format*
a format string with Python variable interpolation. Allowed
values:
* *step*: current step
* *numsteps*: total number of steps as supplied in *numsteps*
* *percentage*: percentage of total
The last call to :meth:`ProgressMeter.print` will automatically
issue a newline ``\\n`` if the last character is the carriage
return ``\\r``.
If *format* is ``None`` then the default is used.
["Step %(step)5d/%(numsteps)d [%(percentage)5.1f%%]\\r"]
*offset*
number to add to *step*; e.g. if *step* is 0-based then one would
set *offset* = 1 [0]
*quiet*
If ``True``, disable all output, ``False`` print all messages as
specified, [``False``]
.. versionchanged:: 0.8
Keyword argument *quiet* was added.
"""
self.numsteps = numsteps
self.interval = int(interval)
self.offset = int(offset)
self.numouts = -1
self.quiet = quiet
if format is None:
format = "Step %(step)5d/%(numsteps)d [%(percentage)5.1f%%]\r"
self.format = format
if self.format.endswith('\r'):
self.last_newline = '\n'
else:
self.last_newline = None
self.step = 0
self.percentage = 0.0
assert numsteps > 0, "numsteps step must be >0"
assert interval > 0, "interval step must be >0"
[docs] def update(self, step, **kwargs):
"""Update the state of the ProgressMeter.
*kwargs* are additional attributes that can be references in
the format string.
"""
self.step = step + self.offset
self.percentage = 100. * self.step / self.numsteps
for k, v in kwargs.items():
setattr(self, k, v)
self.numouts += 1
[docs] def echo(self, step, **kwargs):
"""Print the state to stderr, but only every *interval* steps.
1) calls :meth:`~ProgressMeter.update`
2) writes step and percentage to stderr with :func:`echo`,
using the format string (in :attr:`ProgressMeter.format`)
The last step is always shown, even if not on an *interval*, and a
carriage return is replaced with a new line for a cleaner display.
*kwargs* are additional attributes that can be references in
the format string.
.. Note:: If *quiet* = ``True`` has been set in the
constructor or if :attr:`ProgressMeter.quiet` has
been set to ``True`` the no messages will be
printed.
"""
if self.quiet:
return
self.update(step, **kwargs)
format = self.format
if self.step == self.numsteps:
if self.last_newline:
format = self.format[:-1] + self.last_newline
elif self.numouts % self.interval == 0:
pass
else:
return
echo(format % vars(self))