source: anuga_core/source/anuga/utilities/log.py @ 7073

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

Merge from numpy branch.

  • Property svn:executable set to *
File size: 8.0 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.
20
21Note that this module uses features of the logging package that were introduced
22in python2.5.  If running on earlier versions, these features are disabled:
23    . Calling module name + line number
24'''
25
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# set new_python to True if python version 2.5 or later
62(version_major, version_minor, _, _, _) = sys.version_info
63new_python = ((version_major == 2 and version_minor >= 5) or version_major > 2)
64
65
66################################################################################
67# Module code.
68################################################################################
69
70##
71# @brief Log a message at a specified level.
72# @param level The loglevel to log with (logging.DEBUG, etc).
73# @param msg Message string to log.
74# @note First call of this method initializes the logging system.
75def log(level, msg):
76    '''Log a message at a particular loglevel.
77
78    The first call to this method (by anybody) initializes logging and
79    then logs the message.  Subsequent calls just log the message.
80    '''
81
82    global _setup, log_logging_level
83
84    # have we been setup?
85    if not _setup:
86        # sanity check the logging levels, require console >= file
87        if log_logging_level > console_logging_level:
88            log_logging_level = console_logging_level
89
90        # setup the file logging system
91        if new_python:
92            fmt = '%(asctime)s %(levelname)-8s %(mname)25s:%(lnum)-4d|%(message)s'
93        else:
94            fmt = '%(asctime)s %(levelname)-8s|%(message)s'
95        logging.basicConfig(level=log_logging_level, format=fmt,
96                            filename=log_filename, filemode='w')
97
98        # define a console handler which writes to sys.stdout
99        console = logging.StreamHandler(sys.stdout)
100        console.setLevel(console_logging_level)
101        formatter = logging.Formatter('%(message)s')
102        console.setFormatter(formatter)
103        logging.getLogger('').addHandler(console)
104
105        # catch exceptions
106        sys.excepthook = log_exception_hook
107
108        # tell the world how we are set up
109        start_msg = ("Logfile is '%s' with logging level of %s, "
110                     "console logging level is %s"
111                     % (log_filename,
112                        logging.getLevelName(log_logging_level),
113                        logging.getLevelName(console_logging_level)))
114        if new_python:
115            logging.log(logging.CRITICAL, start_msg,
116                        extra={'mname': __name__, 'lnum': 0})
117        else:
118            logging.log(logging.CRITICAL, start_msg)
119
120        # mark module as *setup*
121        _setup = True
122
123    # get caller information - look back for first module != <this module name>
124    frames = traceback.extract_stack()
125    frames.reverse()
126    try:
127        (_, mod_name) = __name__.rsplit('.', 1)
128    except ValueError:
129        mod_name = __name__
130    for (fpath, lnum, mname, _) in frames:
131        (fname, _) = os.path.basename(fpath).rsplit('.', 1)
132        if fname != mod_name:
133            break
134
135    if new_python:
136        logging.log(level, msg, extra={'mname': fname, 'lnum': lnum})
137    else:
138        logging.log(level, msg)
139
140##
141# @brief Hook function to process uncaught exceptions.
142# @param type
143# @param value
144# @param traceback
145# @note Same interface as sys.excepthook()
146def log_exception_hook(type, value, tb):
147    msg = ''.join(traceback.format_exception(type, value, tb))
148    critical(msg)
149
150   
151################################################################################
152# Shortcut routines to make for simpler user code.
153################################################################################
154
155##
156# @brief Shortcut for log(DEBUG, msg).
157# @param msg Message string to log at logging.DEBUG level.
158def debug(msg=''):
159    log(logging.DEBUG, msg)
160
161##
162# @brief Shortcut for log(INFO, msg).
163# @param msg Message string to log at logging.INFO level.
164def info(msg=''):
165    log(logging.INFO, msg)
166
167##
168# @brief Shortcut for log(WARNING, msg).
169# @param msg Message string to log at logging.WARNING level.
170def warning(msg=''):
171    log(logging.WARNING, msg)
172
173##
174# @brief Shortcut for log(ERROR, msg).
175# @param msg Message string to log at logging.ERROR level.
176def error(msg=''):
177    log(logging.ERROR, msg)
178
179##
180# @brief Shortcut for log(CRITICAL, msg).
181# @param msg Message string to log at logging.CRITICAL level.
182def critical(msg=''):
183    log(logging.CRITICAL, msg)
184
185##
186# @brief Log memory usage at time of call.
187# @param level Override the default INFO logging level.
188# @note From http://code.activestate.com/recipes/286222/.
189def resource_usage(level=logging.INFO):
190    '''Log memory usage at given log level.'''
191
192    if sys.platform != 'win32':
193        _proc_status = '/proc/%d/status' % os.getpid()
194        _scale = {'KB': 1024, 'MB': 1024*1024, 'GB': 1024*1024*1024,
195                  'kB': 1024, 'mB': 1024*1024, 'gB': 1024*1024*1024}
196
197        def _VmB(VmKey):
198            '''Get number of virtual bytes used.'''
199
200            # get pseudo file /proc/<pid>/status
201            try:
202                t = open(_proc_status)
203                v = t.read()
204                t.close()
205            except IOError:
206                return 0.0
207
208            # get VmKey line, eg: 'VmRSS: 999 kB\n ...
209            i = v.index(VmKey)
210            v = v[i:].split(None, 3)
211            if len(v) < 3:
212                return 0.0
213
214            # convert Vm value to bytes
215            return float(v[1]) * _scale[v[2]]
216
217        def memory(since=0.0):
218            '''Get virtual memory usage in bytes.'''
219
220            return _VmB('VmSize:') - since
221
222        def resident(since=0.0):
223            '''Get resident memory usage in bytes.'''
224
225            return _VmB('VmRSS:') - since
226
227        def stacksize(since=0.0):
228            '''Get stack size in bytes.'''
229
230            return _VmB('VmStk:') - since
231
232        msg = ('Resource usage: memory=%.1fMB resident=%.1fMB stacksize=%.1fMB'
233               % (memory()/_scale['MB'], resident()/_scale['MB'],
234                  stacksize()/_scale['MB']))
235        log(level, msg)
236    else:
237        msg = ('Sorry, no memory statistics for Windows (yet).')
238        log(level, msg)
239
240
241if __name__ == '__main__':
242##    critical('Testing exception capturing')
243    def test_it(num=100):
244        if num > 0:
245            test_it(num-1)
246        else:
247            resource_usage()
248
249    import numpy as num
250   
251    a = num.zeros((1000,1000), num.float)
252
253    test_it()
Note: See TracBrowser for help on using the repository browser.