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")