Package Gnumed :: Package pycommon :: Module gmLog2
[frames] | no frames]

Source Code for Module Gnumed.pycommon.gmLog2

  1  """GNUmed logging framework setup. 
  2   
  3  All error logging, user notification and otherwise unhandled  
  4  exception handling should go through classes or functions of  
  5  this module. 
  6   
  7  Theory of operation: 
  8   
  9  This module tailors the standard logging framework to 
 10  the needs of GNUmed. 
 11   
 12  By importing gmLog2 into your code you'll get the root 
 13  logger send to a unicode file with messages in a format useful 
 14  for debugging. The filename is either taken from the 
 15  command line (--log-file=...) or derived from the name 
 16  of the main application. 
 17   
 18  The log file will be found in one of the following standard 
 19  locations: 
 20   
 21  1) given on the command line as "--log-file=LOGFILE" 
 22  2) ~/.<base_name>/<base_name>.log 
 23  3) /dir/of/binary/<base_name>.log               (mainly for DOS/Windows) 
 24   
 25  where <base_name> is derived from the name 
 26  of the main application. 
 27   
 28  If you want to specify just a directory for the log file you 
 29  must end the --log-file definition with a slash. 
 30   
 31  By importing "logging" and getting a logger your modules 
 32  never need to worry about the real message destination or whether 
 33  at any given time there's a valid logger available. 
 34   
 35  Your MAIN module simply imports gmLog2 and all other modules 
 36  will merrily and automagically start logging away. 
 37  """ 
 38  # TODO: 
 39  # - exception() 
 40  # - ascii_ctrl2mnemonic() 
 41  #======================================================================== 
 42  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/pycommon/gmLog2.py,v $ 
 43  # $Id: gmLog2.py,v 1.13 2009/01/02 11:37:44 ncq Exp $ 
 44  __version__ = "$Revision: 1.13 $" 
 45  __author__  = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
 46  __license__ = "GPL (details at http://www.gnu.org)" 
 47   
 48   
 49  # stdlib 
 50  import logging, sys, os, codecs, locale 
 51   
 52   
 53  _logfile_name = None 
 54  _logfile = None 
 55   
 56  _string_encoding = None 
 57   
 58  # table used for cooking non-printables 
 59  AsciiName = ['<#0-0x00-nul>', 
 60                           '<#1-0x01-soh>', 
 61                           '<#2-0x02-stx>', 
 62                           '<#3-0x03-etx>', 
 63                           '<#4-0x04-eot>', 
 64                           '<#5-0x05-enq>', 
 65                           '<#6-0x06-ack>', 
 66                           '<#7-0x07-bel>', 
 67                           '<#8-0x08-bs>', 
 68                           '<#9-0x09-ht>', 
 69                           '<#10-0x0A-lf>', 
 70                           '<#11-0x0B-vt>', 
 71                           '<#12-0x0C-ff>', 
 72                           '<#13-0x0D-cr>', 
 73                           '<#14-0x0E-so>', 
 74                           '<#15-0x0F-si>', 
 75                           '<#16-0x10-dle>', 
 76                           '<#17-0x11-dc1/xon>', 
 77                           '<#18-0x12-dc2>', 
 78                           '<#19-0x13-dc3/xoff>', 
 79                           '<#20-0x14-dc4>', 
 80                           '<#21-0x15-nak>', 
 81                           '<#22-0x16-syn>', 
 82                           '<#23-0x17-etb>', 
 83                           '<#24-0x18-can>', 
 84                           '<#25-0x19-em>', 
 85                           '<#26-0x1A-sub>', 
 86                           '<#27-0x1B-esc>', 
 87                           '<#28-0x1C-fs>', 
 88                           '<#29-0x1D-gs>', 
 89                           '<#30-0x1E-rs>', 
 90                           '<#31-0x1F-us>' 
 91                          ] 
 92   
 93  # msg = reduce(lambda x, y: x+y, (map(self.__char2AsciiName, list(tmp))), '') 
 94  # 
 95  #       def __char2AsciiName(self, aChar): 
 96  #               try: 
 97  #                       return AsciiName[ord(aChar)] 
 98  #               except IndexError: 
 99  #                       return aChar 
100  # 
101  #       def __tracestack(self): 
102  #               """extract data from the current execution stack 
103  # 
104  #               this is rather fragile, I guess 
105  #               """ 
106  #               stack = traceback.extract_stack() 
107  #               self.__modulename = stack[-4][0] 
108  #               self.__linenumber = stack[-4][1] 
109  #               self.__functionname = stack[-4][2] 
110  #               if (self.__functionname == "?"): 
111  #                       self.__functionname = "Main" 
112   
113  #=============================================================== 
114  # external API 
115  #=============================================================== 
116 -def flush():
117 logger = logging.getLogger('gm.logging') 118 logger.critical(u'-------- synced log file -------------------------------') 119 root_logger = logging.getLogger() 120 for handler in root_logger.handlers: 121 handler.flush()
122 #===============================================================
123 -def log_stack_trace(message=None):
124 125 logger = logging.getLogger('gm.logging') 126 127 tb = sys.exc_info()[2] 128 if tb is None: 129 try: 130 tb = sys.last_traceback 131 except AttributeError: 132 logger.debug(u'no stack to trace') 133 return 134 135 # recurse back to root caller 136 while 1: 137 if not tb.tb_next: 138 break 139 tb = tb.tb_next 140 # and put the frames on a stack 141 stack_of_frames = [] 142 frame = tb.tb_frame 143 while frame: 144 stack_of_frames.append(frame) 145 frame = frame.f_back 146 stack_of_frames.reverse() 147 148 if message is not None: 149 logger.debug(message) 150 logger.debug(u'stack trace follows:') 151 logger.debug(u'(locals by frame, outmost frame first)') 152 for frame in stack_of_frames: 153 logger.debug ( 154 u'>>> execution frame [%s] in [%s] at line %s <<<', 155 frame.f_code.co_name, 156 frame.f_code.co_filename, 157 frame.f_lineno 158 ) 159 for varname, value in frame.f_locals.items(): 160 if varname == u'__doc__': 161 continue 162 163 try: 164 value = unicode(value, encoding = _string_encoding, errors = 'replace') 165 except TypeError: 166 try: 167 value = unicode(value) 168 except (UnicodeDecodeError, TypeError): 169 value = '%s' % str(value) 170 value = value.decode(_string_encoding, 'replace') 171 172 logger.debug(u'%20s = %s', varname, value)
173 #===============================================================
174 -def set_string_encoding(encoding=None):
175 176 logger = logging.getLogger('gm.logging') 177 178 global _string_encoding 179 180 if encoding is not None: 181 codecs.lookup(encoding) 182 _string_encoding = encoding 183 logger.info(u'setting python.str -> python.unicode encoding to <%s> (explicit)', _string_encoding) 184 return True 185 186 enc = sys.getdefaultencoding() 187 if enc != 'ascii': 188 _string_encoding = enc 189 logger.info(u'setting python.str -> python.unicode encoding to <%s> (sys.getdefaultencoding)', _string_encoding) 190 return True 191 192 enc = locale.getlocale()[1] 193 if enc is not None: 194 _string_encoding = enc 195 logger.info(u'setting python.str -> python.unicode encoding to <%s> (locale.getlocale)', _string_encoding) 196 return True 197 198 # FIXME: or rather use utf8 ? 199 _string_encoding = locale.getpreferredencoding(do_setlocale=False) 200 logger.info(u'setting python.str -> python.unicode encoding to <%s> (locale.getpreferredencoding)', _string_encoding) 201 return True
202 #=============================================================== 203 # internal API 204 #===============================================================
205 -def __setup_logging():
206 207 set_string_encoding() 208 209 global _logfile 210 if _logfile is not None: 211 return True 212 213 if not __get_logfile_name(): 214 return False 215 216 if sys.version[:3] < '2.5': 217 fmt = u'%(asctime)s %(levelname)-8s %(name)s (%(pathname)s @ #%(lineno)d): %(message)s' 218 else: 219 fmt = u'%(asctime)s %(levelname)-8s %(name)s (%(pathname)s::%(funcName)s() #%(lineno)d): %(message)s' 220 221 _logfile = codecs.open(filename = _logfile_name, mode = 'wb', encoding = 'utf8', errors = 'replace') 222 223 logging.basicConfig ( 224 format = fmt, 225 datefmt = '%Y-%m-%d %H:%M:%S', 226 level = logging.DEBUG, 227 stream = _logfile 228 ) 229 230 logger = logging.getLogger('gm.logging') 231 logger.critical(u'-------- start of logging ------------------------------') 232 logger.info(u'log file is <%s>', _logfile_name) 233 logger.info(u'log level is [%s]', logging.getLevelName(logger.getEffectiveLevel())) 234 logger.info(u'log file encoding is <utf8>') 235 logger.info(u'initial python.str -> python.unicode encoding is <%s>', _string_encoding)
236 #---------------------------------------------------------------
237 -def __get_logfile_name():
238 239 global _logfile_name 240 if _logfile_name is not None: 241 return _logfile_name 242 243 def_log_basename = os.path.splitext(os.path.basename(sys.argv[0]))[0] 244 def_log_name = '%s-%s.log' % (def_log_basename, os.getpid()) 245 246 # given on command line ? 247 for option in sys.argv[1:]: 248 if option.startswith('--log-file='): 249 (name,value) = option.split('=') 250 (dir, name) = os.path.split(value) 251 if dir == '': 252 dir = '.' 253 if name == '': 254 name = def_log_name 255 _logfile_name = os.path.abspath(os.path.expanduser(os.path.join(dir, name))) 256 return True 257 258 # else store it in ~/.def_log_basename/def_log_name 259 dir = os.path.expanduser(os.path.join('~', '.' + def_log_basename)) 260 try: 261 os.makedirs(dir) 262 except OSError, e: 263 if (e.errno == 17) and not os.path.isdir(dir): 264 raise 265 266 _logfile_name = os.path.join(dir, def_log_name) 267 268 return True
269 #=============================================================== 270 # main 271 #--------------------------------------------------------------- 272 __setup_logging() 273 274 if __name__ == '__main__': 275 276 #-----------------------------------------------------------
277 - def test():
278 logger = logging.getLogger('gmLog2.test') 279 logger.error("I expected to see %s::test()" % __file__) 280 try: 281 int(None) 282 except: 283 logger.exception(u'unhandled exception') 284 log_stack_trace() 285 flush()
286 #----------------------------------------------------------- 287 if len(sys.argv) > 1 and sys.argv[1] == u'test': 288 test() 289 #=============================================================== 290 # $Log: gmLog2.py,v $ 291 # Revision 1.13 2009/01/02 11:37:44 ncq 292 # - improved unicoding on log_stack_trace 293 # 294 # Revision 1.12 2008/06/11 19:11:48 ncq 295 # - improved logging 296 # 297 # Revision 1.11 2008/05/31 17:44:31 ncq 298 # - defautl format defs in u'' 299 # 300 # Revision 1.10 2008/04/13 14:42:13 ncq 301 # - saviour the last bits from the old logging code 302 # 303 # Revision 1.9 2008/03/06 21:23:22 ncq 304 # - keep some interesting stuff from the old logging infrastructure 305 # 306 # Revision 1.8 2008/03/06 18:46:55 ncq 307 # - fix docs 308 # 309 # Revision 1.7 2008/03/06 18:25:22 ncq 310 # - fix docs 311 # 312 # Revision 1.6 2008/01/30 14:05:09 ncq 313 # - a bit of cleanup 314 # - TODO added 315 # 316 # Revision 1.5 2008/01/14 20:27:39 ncq 317 # - set_string_encoding() 318 # - properly encode values in log_stack_trace() 319 # - proper test suite 320 # 321 # Revision 1.4 2008/01/13 01:15:41 ncq 322 # - log_stack_trace() and test 323 # - include PID in default log file name 324 # - cleanup 325 # 326 # Revision 1.3 2008/01/07 19:48:53 ncq 327 # - add flush() 328 # 329 # Revision 1.2 2007/12/12 16:23:21 ncq 330 # - we want the default to be the default in GNUmed, 331 # no need to call it that 332 # 333 # Revision 1.1 2007/12/11 10:03:45 ncq 334 # - eventually start switching to Python standard logging 335 # 336 # 337