1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 """
29 rrlog - a Rotating Remote LOG.
30 @author: Ruben Reifenberg
31 """
32
33 __version__="0.2.2"
34
35 import sys,os
36 import traceback
37 from rrlog.tool import traceToShortStr
38 from rrlog.logging23 import handler
39
41 """
42 Use this as errorHandler if you want log errors
43 to disappear silently (e.g.in case of a Log Server Crash).
44 This is not recommended, of course
45 But often better than an exception
46 in the log() call inside the application.
47 """
50
51
53 """
54 Use this as errorHandler if you want to see
55 Errors (in case of a Log Server Crash)
56 in standard out.
57 This is not good in case of a CGI web app, because
58 the printout may appear in the web page.
59 """
63
64
66 """
67 Use this as errorHandler if you want to see
68 Errors (in case of a Log Server Crash)
69 in standard error out.
70 """
72 sys.stderr.write("Logging Error: %s\n"%(e))
73 sys.stderr.write(traceToShortStr())
74
75
76
78 """
79 Instances of this are callable and represent the runtime interface for the application.
80 @ivar stackMax: See L{__init__}, can be modified anytime.
81 @ivar traceOffset: See L{__init__}, can be modified anytime.
82 """
83
84 - def __init__(self,
85 server,
86 on = None,
87 traceOffset=0,
88 msgCountLimit = 1000000,
89 stackMax=1,
90 errorHandler=None,
91 catsEnable=None,
92 catsDisable=None,
93 seFilesExclude=None,
94 name=None,
95 extractStack=True,
96 ):
97 """
98 @param catsEnable: None or list of cat strings, e.g.("E","W","").
99 (Remember, the empty cat "" is the default.)
100 If a list is specified, only these cats are logged, all other calls are ignored.
101 If None, all cats are enabled.
102 @param catsDisable: All cats in this list are ignored. Only one of the
103 parameters catsEnable and catsDisable can be specified, not both same time.
104 @param on: DONT USE ANYMORE. Use catsEnable=() to switch off the log.
105 @param traceOffset: Adjusts the start point of the logged stack trace.
106 Default == 0. Increase to 1 if you have wrapped the log, otherwise, all stack paths
107 will start with your wrapper function. Too high values are silently adjusted to the highest value possible.
108 @param stackMax: 0==show no stacktrace, 1==log one stack line (the line where log(...) was called),
109 2==log two stack lines, and so on.
110 Default: 1 (==log callers line and nothing else.)
111 See also: extractStack parameter.
112 @param errorHandler: Gets any Exception
113 that occurs while logging.
114 (e.g.a connection failure to the log server)
115 If handler is None: No handling, your application
116 will receive any exception occurring inside the log() call.
117 For convenience, you may provide one of these strings:
118 "stderr","stdout","silent" (case-insensitive).
119 These strings are translated
120 into the appropriate built-in "~ErrorHandler" class.
121 @param seFilesExclude: Stack Extraction-Excluded Files.
122 None (==nothing to exclude), or a callable that returns True/False when given a filename.
123 When True, the file does NOT appear in the call path.
124 None is acepted instead of False too, just to enable pragmatic ideas like
125 seFilesExclude=re.compile("foo/bar.*").search
126 (which exludes all foo/bar* files).
127 Note: The callers file name (cfn) is never excluded this way,
128 only the rest of the trace may be "censored".
129 @param name: str, defaults to class name. For individual use (to identify the log object). The log itself does not evaluate it but use it with its __repr__/__str__ method.
130 @param extractStack: If False, stack extraction is disabled. This improves performance (can be more than twice), but any stack related functionality will not work (e.g.line indention to visualize call hierarchy).
131 """
132 if name is None: name = self.__class__.__name__
133 assert on is None, "Parameter on is to be removed. Use catsEnable=() to switch off the log."
134 self._on = True
135 assert (catsEnable is None) or (catsDisable is None), "Can't use both catsEnable and catsDisable same time"
136 if catsEnable is not None:
137 assert hasattr(catsEnable,"__iter__") and (not isinstance(catsEnable,str)),"catsEnable must be None or list of str"
138 if catsDisable is not None:
139 assert hasattr(catsDisable,"__iter__") and (not isinstance(catsDisable,str)),"catsDisable must be None or list of str"
140
141 self._msgCount = 0
142 self.traceOffset = traceOffset
143 self._server = server
144 self._id = self._server.addClient()
145 self.msgCountLimit = msgCountLimit
146 self.stackMax = stackMax
147 if str(errorHandler).lower()=="stderr": errorHandler=StderrErrorHandler()
148 elif str(errorHandler).lower()=="stdout": errorHandler=StdoutErrorHandler()
149 elif str(errorHandler).lower()=="silent": errorHandler=SilentErrorHandler()
150 elif isinstance(errorHandler,str): raise TypeError("probably mistyped str value for errorHandler:%s"%(errorHandler))
151 self._errorHandler = errorHandler
152 self.catsEnable = catsEnable
153 self.catsDisable = catsDisable
154 self._seFilesExclude = seFilesExclude
155 self.name = name
156 self._extractStack = extractStack
157
158
160 """
161 Note that, contrary to the Python logging, the rrlog is not designed/tested for threading.
162 @return: a Handler for the Python >=2.3 standard logging framework.
163 """
164 return handler(self)
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
181 """
182 @return: path,cfuncname where path = ( (filename,lineno), ...), len >=0
183 cfuncname is "" if no traceback available,
184 None if no cfuncname exists (log call at module level)
185 @param depth:int,log-internal call depth when the traceback was taken.
186 """
187 if len(tb) == 0: return (),""
188
189 calldepth = -1-depth
190 try:
191 line = tb[calldepth]
192 except IndexError:
193
194
195
196
197
198 calldepth += 1
199 try:
200 line = tb[calldepth]
201 except IndexError:
202
203
204
205
206 calldepth = len(tb)-1
207 line = tb[calldepth]
208 depth = calldepth
209
210 path = []
211 cfuncname = None
212 stackRest = self.stackMax
213 while stackRest > 0:
214 try:
215 line = tb[depth]
216 except IndexError: break
217 else:
218 if (self._seFilesExclude is None)\
219 or not (self._seFilesExclude(line[0])):
220 stackRest -= 1
221 path.append( (line[0],line[1]) )
222 if cfuncname is None:
223 cfuncname = line[2]
224 else:
225
226 path.append( (None,None) )
227 depth -= 1
228 return path,cfuncname
229
230
231
232
234 """
235 @return: Tuple for the log server
236 @param depth:int,log-internal call depth when the traceback was taken.
237 """
238 self._msgCount += 1
239 if self._msgCount == self.msgCountLimit:
240 self._msgCount = 1
241 try:
242 ospid = os.getpid()
243 except Exception,e:
244 import warnings
245 warnings.warn("log: could not obtain process id from your operating system. You'll see -1 there.")
246 ospid = -1
247
248
249 path,cfuncname = self._getCallPath(tb,depth)
250 return (
251 ospid,
252 self._id,
253 self._msgCount,
254 message,
255 special,
256 cat,
257 path,
258 len(tb),
259 cfuncname,
260 )
261
262
263 - def log(self, *args, **kwargs):
264 """
265 REMOVED. There's no log.log() anymore.
266 Use log() instead (i.e. see L{__call__}
267 @raise AssertionError: Always
268 """
269 assert False, "log.log() was for compatibility with pre 0.1.0 versions. Use log()."
270
271 - def on(self): self._on = True
272 - def off(self): self._on = False
273
274 - def __call__(self,message,cat="",special=None,traceDepth=1,**kwargs):
275 """
276 You can provide any kwargs you like. This is sugar for convenience,
277 all kwargs are put into the "special" dict. Same restrictions, see below.
278 Note: kwargs items override items of special dict silently.
279 @param message: String to be logged
280 @param cat: application specific category, e.g. "E"=Error,"W"=Warning. Default is "".
281 @param special: dict-like object (only the items() method is required, delivering a (k,v)-iter); or None.
282 The items of the "special" dict appear in the Jobs at the server side. Custom observers,
283 filters and writers may use these data.
284 Type restrictions: only {str: str|int} is allowed for the dict items (!)
285 @param traceDepth: Adjusts the starting point of stacktrace logging.
286 Default=1. Typically, you may increase that if you call the log with a wrapping function which should not appear in the logged stacktrace.
287 This adjusts a single call only. See traceOffset in L{__init__} for a global adjustment.
288 @return: log-client specific message number,starting with 1 (not unique at server side, if multiple log clients are used.)
289 """
290 if (self.catsEnable is not None) and (cat not in self.catsEnable):
291 return
292 elif (self.catsDisable is not None) and (cat in self.catsDisable):
293 return
294 if not self._on: return
295 if len(kwargs) > 0:
296
297 if special is None: special = kwargs
298 else: special.update(kwargs)
299
300 if self._extractStack:
301 tb = traceback.extract_stack()
302 else:
303 tb = ()
304 sd = self._createServerData(
305 tb,
306 traceDepth+self.traceOffset,
307 message,
308 cat,
309 special,
310 )
311 try:
312 self._server.log(*sd)
313 except Exception,e:
314 if self._errorHandler is not None:
315 self._errorHandler.handleException(e)
316 else:
317 raise
318 else:
319 return sd[0]
320
322 def on():return {True:"on",False:"Off"}[self._on]
323 return "%s(%s)"%(self.name,on())
324