pism

[fork] customized build of PISM, the parallel ice sheet model (tillflux branch)
git clone git://src.adamsgaard.dk/pism # fast
git clone https://src.adamsgaard.dk/pism.git # slow
Log | Files | Refs | README | LICENSE Back to index

logging.py (6701B)


      1 # Copyright (C) 2012, 2015, 2016, 2018, 2019 David Maxwell
      2 #
      3 # This file is part of PISM.
      4 #
      5 # PISM is free software; you can redistribute it and/or modify it under the
      6 # terms of the GNU General Public License as published by the Free Software
      7 # Foundation; either version 3 of the License, or (at your option) any later
      8 # version.
      9 #
     10 # PISM is distributed in the hope that it will be useful, but WITHOUT ANY
     11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     12 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
     13 # details.
     14 #
     15 # You should have received a copy of the GNU General Public License
     16 # along with PISM; if not, write to the Free Software
     17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19 """Implements a rudimentary logging system.  Messages are sent in client code via :func:`logError`, :func:`logMessage`,
     20 etc.  These messages are then forwarded to any loggers that have been previously registered with :func:`add_logger`.
     21 
     22 A logger is either a function with signature::
     23 
     24   def myLogger(message, verbosity)
     25 
     26 or a class that implements::
     27 
     28   def __call__(self, message, verbosity)
     29 
     30 The string message is passed as ``message`` and verbosity is a standard PISM verbosity (an integer between 1-5).
     31 The following aliases are available
     32 
     33   * ``PISM.logging.kError``
     34   * ``PISM.logging.kWarning``
     35   * ``PISM.logging.kMessage``
     36   * ``PISM.logging.kDebug``
     37   * ``PISM.logging.kPrattle``
     38 
     39 which are listed in increasing verbosity.  Note that ``kError`` need not signify an error message, only a message with
     40 verbosity 1 that is ensured to be printed.  Conversely, ``kPrattle`` signifies a verbosity level 5 with messages that
     41 rarely need to be displayed.
     42 
     43 The default logger, :func:`print_logger`, simply passes the message along as a call to :cpp:func:`verbPrintf`.
     44 See also the  :class:`CaptureLogger`, which saves logged messages into an attribute of an :file:`.nc` file.
     45 
     46 The logging system does not log calls to verbPrintf directly.  In particular, calls to verbPrintf from within PISM's C++
     47 code do not pass through the python-based logging system.
     48 """
     49 
     50 import PISM
     51 import time
     52 
     53 kError = 1
     54 kWarning = 2
     55 kMessage = 2
     56 kDebug = 4
     57 kPrattle = 5
     58 
     59 _loggers = []
     60 
     61 def clear_loggers():
     62     """Removes all members from the global list of loggers."""
     63     global _loggers
     64     _loggers = []
     65 
     66 
     67 def add_logger(logger):
     68     """Appends a new logger to the global list of loggers."""
     69     global _loggers
     70     _loggers.append(logger)
     71 
     72 
     73 def log(message, verbosity):
     74     """Logs a message with the specified verbosity"""
     75     for l in _loggers:
     76         l(message, verbosity)
     77 
     78 
     79 def logError(message):
     80     """Convenience function for logging a message at the level of ``kError``"""
     81     log(message, kError)
     82 
     83 
     84 def logWarning(message):
     85     """Convenience function for logging a message at the level of ``kWarning``"""
     86     log(message, kWarning)
     87 
     88 
     89 def logMessage(message):
     90     """Convenience function for logging a message at the level of ``kMessage``"""
     91     log(message, kMessage)
     92 
     93 
     94 def logDebug(message):
     95     """Convenience function for logging a message at the level of ``kDebug``"""
     96     log(message, kDebug)
     97 
     98 
     99 def logPrattle(message):
    100     """Convenience function for logging a message at the level of ``kPrattle``"""
    101     log(message, kPrattle)
    102 
    103 
    104 def print_logger(message, verbosity):
    105     """Implements a logger that forwards messages to :cpp:func:`verbPrintf`."""
    106     com = PISM.Context().com
    107     msg = str(message)
    108     PISM.verbPrintf(verbosity, com, msg)
    109 
    110 # The global list of loggers.
    111 _loggers = [print_logger]
    112 
    113 
    114 class CaptureLogger(object):
    115 
    116     """Implements a logger that appends log messages as they occur
    117     to an attribute of an :file:`.nc` file."""
    118 
    119     def __init__(self, filename, attribute='pism_log', verbosity_threshold=2):
    120         """:param filename: Name of :file:`.nc` file to save the log to.
    121         :param attribute: Attribute name to save the log as."""
    122         self.com = PISM.Context().com
    123         self.rank = PISM.Context().rank
    124         self.log = ""
    125         self.filename = filename
    126         self.attr = attribute
    127         self.verbosity_threshold = verbosity_threshold
    128 
    129     def __call__(self, message, verbosity):
    130         """Saves the message to our internal log string and writes the string out to the file."""
    131         if verbosity <= self.verbosity_threshold:
    132             timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
    133             self.log = "%s%s: %s" % (self.log, timestamp, message)
    134             d = PISM.File(PISM.Context().com, self.filename, PISM.PISM_NETCDF3, PISM.PISM_READWRITE)
    135             d.redef()
    136             d.write_attribute("PISM_GLOBAL", self.attr, self.log)
    137             d.close()
    138 
    139     def readOldLog(self):
    140         """If the :file:`.nc` file we are logging to already has a log,
    141         read it in to the log we are about to make so that we append to it rather
    142         than overwriting it."""
    143         d = PISM.File(PISM.Context().com, self.filename, PISM.PISM_NETCDF3, PISM.PISM_READONLY)
    144         self.log += d.read_text_attribute("PISM_GLOBAL", self.attr)
    145         d.close()
    146 
    147     def write(self, filename=None, attribute=None):
    148         """Save a copy of our log to the specified file and attribute."""
    149         if filename is None:
    150             filename = self.filename
    151         if attribute is None:
    152             attribute = self.attr
    153         d = PISM.File(PISM.Context().com, filename, PISM.PISM_NETCDF3, PISM.PISM_READWRITE)
    154         d.redef()
    155         d.write_attribute("PISM_GLOBAL", attribute, self.log)
    156         d.close()
    157 
    158 import termios
    159 import sys
    160 import os
    161 TERMIOS = termios
    162 
    163 
    164 def getkey():
    165     """Helper function for grabbing a single key press"""
    166     fd = sys.stdin.fileno()
    167     c = None
    168     if os.isatty(fd):
    169         old = termios.tcgetattr(fd)
    170         new = termios.tcgetattr(fd)
    171         new[3] = new[3] & ~TERMIOS.ICANON & ~TERMIOS.ECHO
    172         new[6][TERMIOS.VMIN] = 1
    173         new[6][TERMIOS.VTIME] = 0
    174         termios.tcsetattr(fd, TERMIOS.TCSANOW, new)
    175         try:
    176             c = os.read(fd, 1)
    177         finally:
    178             termios.tcsetattr(fd, TERMIOS.TCSAFLUSH, old)
    179     else:
    180         # FIXME: The following is here for multi-processor runs.
    181         # Termios is not available and I don't know a better solution.
    182         c = sys.stdin.read(1)
    183     return c
    184 
    185 
    186 def pause(message_in=None, message_out=None):
    187     """Prints a message and waits for a key press.
    188 
    189     :param message_in: Message to display before waiting.
    190     :param message_out: Message to display after waiting."""
    191     com = PISM.Context().com
    192     if not message_in is None:
    193         PISM.verbPrintf(1, com, message_in + "\n")
    194     _ = getkey()
    195     if not message_out is None:
    196         PISM.verbPrintf(1, com, message_out + "\n")