tlogging.py - pism - [fork] customized build of PISM, the parallel ice sheet model (tillflux branch)
 (HTM) git clone git://src.adamsgaard.dk/pism
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       tlogging.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")