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

Last change on this file since 6902 was 6902, checked in by rwilson, 14 years ago

Back-merge from Numeric trunk to numpy branch.

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