1 #----------------------------------------------------------------------------
3 # Purpose: Utilities to help with logging
9 # Copyright: (c) 2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
19 from activegrid
.util
.lang
import *
20 import activegrid
.util
.objutils
as objutils
21 import activegrid
.util
.sysutils
as sysutils
22 import activegrid
.util
.appdirs
as appdirs
24 LEVEL_FATAL
= logging
.FATAL
25 LEVEL_ERROR
= logging
.ERROR
26 LEVEL_WARN
= logging
.WARN
27 LEVEL_INFO
= logging
.INFO
28 LEVEL_DEBUG
= logging
.DEBUG
30 EXCEPTION_INFO
= 'exceptionInfo'
31 loggingInitialized
= False
36 def initLogging(mode
, force
=False):
37 global ag_debugLogger
, loggingInitialized
38 if (force
or not loggingInitialized
):
39 loggingInitialized
= True
41 if (mode
== LOG_MODE_IDE
):
42 configFile
= os
.getenv("AG_LOGCONFIG_IDE")
43 elif (mode
== LOG_MODE_TESTRUN
):
44 configFile
= os
.getenv("AG_LOGCONFIG_PYTESTRUN")
46 configFile
= os
.getenv("AG_LOGCONFIG_RUN")
47 if ((configFile
== None) or not os
.path
.exists(configFile
)):
48 if (mode
== LOG_MODE_IDE
):
50 elif (mode
== LOG_MODE_TESTRUN
):
51 configFile
= "TestRunLog"
54 configFile
= os
.path
.join(appdirs
.getSystemDir(appdirs
.AG_LOGS_DIR
), "py" + configFile
+ ".ini")
55 if (os
.path
.exists(configFile
)):
56 print "Using logging configuration file: %s" % configFile
57 fileConfig(configFile
)
59 print "*** Cannot find logging configuration file (%s) -- setting default logging level to WARN ***" % (configFile
)
60 defaultStream
= sys
.stderr
61 if (mode
== LOG_MODE_RUN
):
62 defaultStream
= sys
.stdout
63 handler
= logging
.StreamHandler(defaultStream
)
64 handler
.setLevel(logging
.DEBUG
)
65 handler
.setFormatter(logging
.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s"))
66 logging
.getLogger().addHandler(handler
)
67 logging
.getLogger().setLevel(logging
.WARN
)
68 ag_debugLogger
= logging
.getLogger("activegrid.debug")
69 ag_debugLogger
.setLevel(logging
.DEBUG
)
72 ag_debugLogger
= logging
.getLogger("activegrid.debug")
74 def log(logger
, level
, msg
, *params
):
76 logger
= ag_debugLogger
77 apply(logger
.log
, (level
, msg
) + params
)
79 def fatal(logger
, msg
, *params
):
80 apply(logger
.fatal
, (msg
,) + params
)
82 def error(logger
, msg
, *params
):
83 apply(logger
.error
, (msg
,) + params
)
85 def warn(logger
, msg
, *params
):
86 apply(logger
.warn
, (msg
,) + params
)
88 def info(logger
, msg
, *params
):
89 apply(logger
.info
, (msg
,) + params
)
91 def debug(logger
, msg
, *params
):
93 logger
= ag_debugLogger
94 apply(logger
.debug
, (msg
,) + params
)
96 def setLevelFatal(logger
):
97 logger
.setLevel(LEVEL_FATAL
)
99 def setLevelError(logger
):
100 logger
.setLevel(LEVEL_ERROR
)
102 def setLevelWarn(logger
):
103 logger
.setLevel(LEVEL_WARN
)
105 def setLevelInfo(logger
):
106 logger
.setLevel(LEVEL_INFO
)
108 def setLevelDebug(logger
):
109 logger
.setLevel(LEVEL_DEBUG
)
111 def isEnabledForError(logger
):
112 return logger
.isEnabledFor(LEVEL_ERROR
)
114 def isEnabledForWarn(logger
):
115 return logger
.isEnabledFor(LEVEL_WARN
)
117 def isEnabledForInfo(logger
):
118 return logger
.isEnabledFor(LEVEL_INFO
)
120 def isEnabledForDebug(logger
):
121 return logger
.isEnabledFor(LEVEL_DEBUG
)
124 TEST_MODE_DETERMINISTIC
= 1
125 TEST_MODE_NON_DETERMINISTIC
= 2
128 agTestMode
= TEST_MODE_NONE
130 def setTestMode(mode
):
138 def testMode(normalObj
, testObj
=None, nonDeterministicObj
=None):
139 testMode
= getTestMode()
140 if testMode
> TEST_MODE_NONE
:
141 if ((nonDeterministicObj
!= None) and (testMode
== TEST_MODE_NON_DETERMINISTIC
)):
142 return nonDeterministicObj
146 pythonFileRefPattern
= asString(r
'(?<=File ")[^"]*(#[^#]*")(, line )[0-9]*')
147 phpFileRefPattern
= asString(r
'( in ).*#([^#]*#[^ ]*)(?= on line )')
148 pathSepPattern
= os
.sep
149 if (pathSepPattern
== "\\"):
150 pathSepPattern
= "\\\\"
151 pythonFileRefPattern
= pythonFileRefPattern
.replace("#", pathSepPattern
)
152 pythonFileRefPattern
= re
.compile(pythonFileRefPattern
)
153 phpFileRefPattern
= phpFileRefPattern
.replace("#", pathSepPattern
)
154 phpFileRefPattern
= re
.compile(phpFileRefPattern
)
156 def removeFileRefs(str):
157 str = pythonFileRefPattern
.sub(_fileNameReplacement
, str)
158 str = phpFileRefPattern
.sub(_fileNameReplacementPHP
, str)
161 def removePHPFileRefs(str):
162 str = phpFileRefPattern
.sub(_fileNameReplacementPHP
, str)
165 def _fileNameReplacement(match
):
166 return "...%s" % match
.group(1).replace(os
.sep
, "/")
168 def _fileNameReplacementPHP(match
):
169 return "%s...%s" % (match
.group(1), match
.group(2).replace(os
.sep
, "/"))
171 def formatTraceback(tb
=None):
173 extype
, val
, tb
= sys
.exc_info()
174 tbs
= "\n" + "".join(traceback
.format_tb(tb
))
177 def formatExceptionCause(cause
, stacktrace
=False):
182 tbs
= formatTraceback()
183 return "Caused by %s.%s: %s%s" % (cause
.__module
__, cause
.__class
__.__name
__, str(cause
), tbs
)
185 def addExceptionInfo(e
, key
, value
):
186 if not hasattr(e
, EXCEPTION_INFO
):
188 setattr(e
, EXCEPTION_INFO
, {})
190 return # Make sure we still report the real exception even if we can't add the extra info
191 if not e
.exceptionInfo
.has_key(key
): # Never overwrite exception info since we assume earlier info is more specific
192 e
.exceptionInfo
[key
] = value
194 def reportException(exception
, out
=None, stacktrace
=False, diffable
=False):
195 exstr
= exceptionToString(exception
, stacktrace
, diffable
)
201 def exceptionToString(exception
, stacktrace
=False, diffable
=False):
202 extype
= objutils
.typeToString(exception
)
205 e
,v
,t
= sys
.exc_info()
207 exstr
= removeFileRefs(str(val
))
210 if hasattr(val
, EXCEPTION_INFO
):
212 for infoKey
, infoValue
in getattr(val
, EXCEPTION_INFO
).items():
214 prefix
= " EXTRA INFO:"
218 exstr
+= ("%s %s=%s" % (prefix
, infoKey
, infoValue
))
219 result
= "Got Exception = %s: %s" % (extype
, exstr
)
221 fmt
= traceback
.format_exception(extype
, val
, t
)
224 s
= removeFileRefs(s
)
225 result
= result
+ "\n" + s
228 def fileConfig(fname
, defaults
=None):
230 This is copied from logging.config so that we could fix the class lookup of
231 handlers. Previously handlers had to be defined in logging.handlers and we
232 need to be able to define our own.
234 import ConfigParser
, string
236 cp
= ConfigParser
.ConfigParser(defaults
)
237 if hasattr(cp
, 'readfp') and hasattr(fname
, 'readline'):
241 #first, do the formatters...
242 flist
= cp
.get("formatters", "keys")
244 flist
= string
.split(flist
, ",")
247 sectname
= "formatter_%s" % form
248 opts
= cp
.options(sectname
)
250 fs
= cp
.get(sectname
, "format", 1)
253 if "datefmt" in opts
:
254 dfs
= cp
.get(sectname
, "datefmt", 1)
257 f
= logging
.Formatter(fs
, dfs
)
259 #next, do the handlers...
261 logging
._acquireLock
()
264 #first, lose the existing handlers...
265 logging
._handlers
.clear()
266 #now set up the new ones...
267 hlist
= cp
.get("handlers", "keys")
269 hlist
= string
.split(hlist
, ",")
271 fixups
= [] #for inter-handler references
274 sectname
= "handler_%s" % hand
275 classname
= cp
.get(sectname
, "class")
276 opts
= cp
.options(sectname
)
277 if "formatter" in opts
:
278 fmt
= cp
.get(sectname
, "formatter")
283 klass
= eval(classname
, vars(logging
))
287 klass
= objutils
.classForName(classname
)
288 args
= cp
.get(sectname
, "args")
289 args
= eval(args
, vars(logging
))
290 h
= apply(klass
, args
)
292 level
= cp
.get(sectname
, "level")
293 h
.setLevel(logging
._levelNames
[level
])
295 h
.setFormatter(formatters
[fmt
])
296 #temporary hack for FileHandler and MemoryHandler.
297 if klass
== logging
.handlers
.MemoryHandler
:
299 target
= cp
.get(sectname
,"target")
302 if len(target
): #the target handler may not be loaded yet, so keep for later...
303 fixups
.append((h
, target
))
305 ## except Exception, e: #if an error occurs when instantiating a handler, too bad
306 ## pass #this could happen e.g. because of lack of privileges
307 #now all handlers are loaded, fixup inter-handler references...
311 h
.setTarget(handlers
[t
])
312 #at last, the loggers...first the root...
313 llist
= cp
.get("loggers", "keys")
314 llist
= string
.split(llist
, ",")
316 sectname
= "logger_root"
319 opts
= cp
.options(sectname
)
321 level
= cp
.get(sectname
, "level")
322 log
.setLevel(logging
._levelNames
[level
])
323 for h
in root
.handlers
[:]:
324 root
.removeHandler(h
)
325 hlist
= cp
.get(sectname
, "handlers")
327 hlist
= string
.split(hlist
, ",")
329 log
.addHandler(handlers
[hand
])
330 #and now the others...
331 #we don't want to lose the existing loggers,
332 #since other threads may have pointers to them.
333 #existing is set to contain all existing loggers,
334 #and as we go through the new configuration we
335 #remove any which are configured. At the end,
336 #what's left in existing is the set of loggers
337 #which were in the previous configuration but
338 #which are not in the new configuration.
339 existing
= root
.manager
.loggerDict
.keys()
340 #now set up the new ones...
342 sectname
= "logger_%s" % log
343 qn
= cp
.get(sectname
, "qualname")
344 opts
= cp
.options(sectname
)
345 if "propagate" in opts
:
346 propagate
= cp
.getint(sectname
, "propagate")
349 logger
= logging
.getLogger(qn
)
353 level
= cp
.get(sectname
, "level")
354 logger
.setLevel(logging
._levelNames
[level
])
355 for h
in logger
.handlers
[:]:
356 logger
.removeHandler(h
)
357 logger
.propagate
= propagate
359 hlist
= cp
.get(sectname
, "handlers")
361 hlist
= string
.split(hlist
, ",")
363 logger
.addHandler(handlers
[hand
])
364 #Disable any old loggers. There's no point deleting
365 #them as other threads may continue to hold references
366 #and by disabling them, you stop them doing any logging.
368 root
.manager
.loggerDict
[log
].disabled
= 1
371 ## ei = sys.exc_info()
372 ## traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
375 logging
._releaseLock
()