source: branches/numpy/anuga/utilities/log.py @ 6833

Last change on this file since 6833 was 6833, checked in by rwilson, 16 years ago

Changes getting Patong validation to run.

  • Property svn:executable set to *
File size: 7.3 KB
Line 
1#!/usr/bin/env python
2
3'''
4A simple logging module that logs to the console and a logfile, and has a
5configurable threshold loglevel for each of console and logfile output.
6
7Use it this way:
8    import anuga.utilities.log as log
9    log.console_logging_level = log.DEBUG
10    log.debug('A message at DEBUG level')
11    log.info('Another message, INFO level')
12
13This class uses the 'borg' pattern - there is never more than one instance
14of log data.  See
15<http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html>
16for the basic idea used here: modules *are* singletons!
17
18Until the first call to log() the user is free to play with the module data
19to configure the logging.
20Note that this module uses features of the logging package that were introduced
21in python2.5.  If running on earlier versions, these features are disabled:
22    . Calling module name + line number
23'''
24
25import sys
26import os
27import sys
28import traceback
29import logging
30
31
32################################################################################
33# Module variables - only one copy of these, ever.
34#
35# The console logging level is set to a high level, like CRITICAL.  The logfile
36# logging is set lower, between DEBUG and CRITICAL.  The idea is to log least to
37# the console, but ensure that everything that goes to the console *will* also
38# appear in the log file.  There is code to ensure log <= console levels.
39################################################################################
40
41# flag variable to determine if logging set up or not
42_setup = False
43
44# logging level for the console
45console_logging_level = logging.CRITICAL
46
47# logging level for the logfile
48log_logging_level = logging.INFO
49
50# The default name of the file to log to.
51log_filename = os.path.join('.', 'anuga.log')
52
53# set module variables so users don't have to do 'import logging'.
54CRITICAL = logging.CRITICAL
55ERROR = logging.ERROR
56WARNING = logging.WARNING
57INFO = logging.INFO
58DEBUG = logging.DEBUG
59NOTSET = logging.NOTSET
60
61
62################################################################################
63# Module code.
64################################################################################
65
66##
67# @brief Log a message at a specified level.
68# @param level The loglevel to log with (logging.DEBUG, etc).
69# @param msg Message string to log.
70# @note First call of this method initializes the logging system.
71def log(level, msg):
72    '''Log a message at a particular loglevel.
73
74    The first call to this method (by anybody) initializes logging and
75    then logs the message.  Subsequent calls just log the message.
76    '''
77
78    global _setup, log_logging_level
79
80    # get running python version for later
81    (version_major, version_minor, _, _, _) = sys.version_info
82
83    # have we been setup?
84    if not _setup:
85        # sanity check the logging levels, require console >= file
86        if log_logging_level > console_logging_level:
87            log_logging_level = console_logging_level
88
89        # setup the file logging system
90        if version_major >= 2 and version_minor >= 5:
91            fmt = '%(asctime)s %(levelname)-8s %(mname)25s:%(lnum)-4d|%(message)s'
92        else:
93            fmt = '%(asctime)s %(levelname)-8s|%(message)s'
94        logging.basicConfig(level=log_logging_level, format=fmt,
95                            filename=log_filename, filemode='w')
96
97        # define a console handler which writes to sys.stdout
98        console = logging.StreamHandler(sys.stdout)
99        console.setLevel(console_logging_level)
100        formatter = logging.Formatter('%(message)s')
101        console.setFormatter(formatter)
102        logging.getLogger('').addHandler(console)
103
104        # tell the world how we are set up
105        start_msg = ("Logfile is '%s' with logging level of %s, "
106                     "console logging level is %s"
107                     % (log_filename,
108                        logging.getLevelName(log_logging_level),
109                        logging.getLevelName(console_logging_level)))
110        if version_major >= 2 and version_minor >= 5:
111            logging.log(logging.CRITICAL, start_msg,
112                        extra={'mname': __name__, 'lnum': 0})
113        else:
114            logging.log(logging.CRITICAL, start_msg)
115
116        # mark module as *setup*
117        _setup = True
118
119    # get caller information - look back for first module != <this module name>
120    frames = traceback.extract_stack()
121    frames.reverse()
122    (_, mod_name) = __name__.rsplit('.', 1)
123    for (fpath, lnum, mname, _) in frames:
124        (fname, _) = os.path.basename(fpath).rsplit('.', 1)
125        if fname != mod_name:
126            break
127
128    if version_major >= 2 and version_minor >= 5:
129        logging.log(level, msg, extra={'mname': fname, 'lnum': lnum})
130    else:
131        logging.log(level, msg)
132
133################################################################################
134# Shortcut routines to make for simpler user code.
135################################################################################
136
137##
138# @brief Shortcut for log(DEBUG, msg).
139# @param msg Message string to log at logging.DEBUG level.
140def debug(msg):
141    log(logging.DEBUG, msg)
142
143##
144# @brief Shortcut for log(INFO, msg).
145# @param msg Message string to log at logging.INFO level.
146def info(msg):
147    log(logging.INFO, msg)
148
149##
150# @brief Shortcut for log(WARNING, msg).
151# @param msg Message string to log at logging.WARNING level.
152def warning(msg):
153    log(logging.WARNING, msg)
154
155##
156# @brief Shortcut for log(ERROR, msg).
157# @param msg Message string to log at logging.ERROR level.
158def error(msg):
159    log(logging.ERROR, msg)
160
161##
162# @brief Shortcut for log(CRITICAL, msg).
163# @param msg Message string to log at logging.CRITICAL level.
164def critical(msg):
165    log(logging.CRITICAL, msg)
166
167##
168# @brief Log memory usage at time of call.
169# @param level Override the default INFO logging level.
170# @note From http://code.activestate.com/recipes/286222/.
171def resource_usage(level=logging.INFO):
172    '''Log memory usage at given log level.'''
173
174    if sys.platform != 'win32':
175        _proc_status = '/proc/%d/status' % os.getpid()
176        _scale = {'KB': 1024.0, 'MB': 1024.0*1024.0, 'GB': 1024.0*1024.0*1024.0,
177                  'kB': 1024.0, 'mB': 1024.0*1024.0, 'gB': 1024.0*1024.0*1024.0}
178
179        def _VmB(VmKey):
180            '''Get number of virtual bytes used.'''
181
182            # get pseudo file /proc/<pid>/status
183            try:
184                t = open(_proc_status)
185                v = t.read()
186                t.close()
187            except IOError:
188                return 0.0
189
190            # get VmKey line, eg: 'VmRSS: 999 kB\n ...
191            i = v.index(VmKey)
192            v = v[i:].split(None, 3)
193            if len(v) < 3:
194                return 0.0
195
196            # convert Vm value to bytes
197            return float(v[1]) * _scale[v[2]]
198
199        def memory(since=0.0):
200            '''Get virtual memory usage in bytes.'''
201
202            return _VmB('VmSize:') - since
203
204        def resident(since=0.0):
205            '''Get resident memory usage in bytes.'''
206
207            return _VmB('VmRSS:') - since
208
209        def stacksize(since=0.0):
210            '''Get stack size in bytes.'''
211
212            return _VmB('VmStk:') - since
213
214        msg = ('Resource usage: memory=%.1f resident=%.1f stacksize=%.1f'
215               % (memory()/_scale['GB'], resident()/_scale['GB'],
216                  stacksize()/_scale['GB']))
217        log(level, msg)
218    else:
219        pass
220
221if __name__ == "__main__":
222    debug('DEBUG message')
223    info('INFO message')
224    warning('WARNING message')
Note: See TracBrowser for help on using the repository browser.