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

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

Fixing problems running <2.5 python.

  • Property svn:executable set to *
File size: 7.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 disables:
23    . 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
63################################################################################
64# Module code.
65################################################################################
66
67##
68# @brief Log a message at a specified level.
69# @param level The loglevel to log with (logging.DEBUG, etc).
70# @param msg Message string to log.
71# @note First call of this method initializes the logging system.
72def log(level, msg):
73    '''Log a message at a particular loglevel.
74
75    The first call to this method (by anybody) initializes logging and
76    then logs the message.  Subsequent calls just log the message.
77    '''
78
79    global _setup, log_logging_level
80
81    # get running python version for later
82    (version_major, version_minor, _, _, _) = sys.version_info
83
84    # have we been setup?
85    if not _setup:
86        # sanity check the logging levels, require console >= file
87        if console_logging_level < log_logging_level:
88            log_logging_level = console_logging_level
89
90        # setup the file logging system
91        if version_major >= 2 and version_minor >= 5:
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        # tell the world how we are set up
106        start_msg = ("Logfile is '%s' with logging level of %s, "
107                     "console logging level is %s"
108                     % (log_filename,
109                        logging.getLevelName(log_logging_level),
110                        logging.getLevelName(console_logging_level)))
111        if version_major >= 2 and version_minor >= 5:
112            logging.log(logging.CRITICAL, start_msg,
113                        extra={'mname': __name__, 'lnum': 0})
114        else:
115            logging.log(logging.CRITICAL, start_msg)
116
117        # mark module as *setup*
118        _setup = True
119
120    # get caller information - look back for first module != <this module name>
121    frames = traceback.extract_stack()
122    frames.reverse()
123    for (fpath, lnum, mname, _) in frames:
124        fname = os.path.basename(mname).rsplit('.', 1)[0]
125        if mname != __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
167def resource_usage(level=logging.CRITICAL):
168    '''Log resource usage at given log level.'''
169
170    if sys.platform != 'win32':
171        _proc_status = '/proc/%d/status' % os.getpid()
172        _scale = {'KB': 1024.0, 'MB': 1024.0*1024.0, 'GB': 1024.0*1024.0*1024.0,
173                  'kB': 1024.0, 'mB': 1024.0*1024.0, 'gB': 1024.0*1024.0*1024.0}
174
175        def _VmB(VmKey):
176            '''Get number of virtual bytes used.'''
177
178            # get pseudo file /proc/<pid>/status
179            try:
180                t = open(_proc_status)
181                v = t.read()
182                t.close()
183            except IOError:
184                return 0.0
185
186            # get VmKey line, eg: 'VmRSS: 999 kB\n ...
187            i = v.index(VmKey)
188            v = v[i:].split(None, 3)
189            if len(v) < 3:
190                return 0.0
191
192            # convert Vm value to bytes
193            return float(v[1]) * _scale[v[2]]
194
195        def memory(since=0.0):
196            '''Get virtual memory usage in bytes.'''
197
198            return _VmB('VmSize:') - since
199
200        def resident(since=0.0):
201            '''Get resident memory usage in bytes.'''
202
203            return _VmB('VmRSS:') - since
204
205        def stacksize(since=0.0):
206            '''Get stack size in bytes.'''
207
208            return _VmB('VmStk:') - since
209
210        msg = ('Resource usage: memory=%.1f resident=%.1f stacksize=%.1f'
211               % (memory()/_scale['GB'], resident()/_scale['GB'],
212                  stacksize()/_scale['GB']))
213        log(level, msg)
214    else:
215        pass
Note: See TracBrowser for help on using the repository browser.