blob: 360d3ac225487c75455d4ba342670e6f78073064 [file] [log] [blame]
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001#!/usr/bin/env python
2#
3# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
4#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18#
19# This file is part of the Python logging distribution. See
20# http://www.red-dove.com/python_logging.html
21#
22"""Test harness for the logging module. Run all tests.
23
24Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
25"""
26
27from select import select
28import os, sys, string, struct, types, cPickle, cStringIO
29import socket, threading, time, locale
30import logging, logging.handlers, logging.config
31
32locale.setlocale(locale.LC_ALL, '')
33
34BANNER = "-- %-10s %-6s ---------------------------------------------------\n"
35
36#----------------------------------------------------------------------------
37# Log receiver
38#----------------------------------------------------------------------------
39
40TIMEOUT = 10
41
42from SocketServer import ThreadingTCPServer, StreamRequestHandler
43
44class LogRecordStreamHandler(StreamRequestHandler):
45 """
46 Handler for a streaming logging request. It basically logs the record
47 using whatever logging policy is configured locally.
48 """
49
50 def handle(self):
51 """
52 Handle multiple requests - each expected to be a 4-byte length,
53 followed by the LogRecord in pickle format. Logs the record
54 according to whatever policy is configured locally.
55 """
56 while 1:
57 try:
58 chunk = self.connection.recv(4)
59 if len(chunk) < 4:
60 break
61 slen = struct.unpack(">L", chunk)[0]
62 #print slen
63 chunk = self.connection.recv(slen)
64 while len(chunk) < slen:
65 chunk = chunk + self.connection.recv(slen - len(chunk))
66 obj = self.unPickle(chunk)
67 record = logging.LogRecord(None, None, "", 0, "", (), None)
68 record.__dict__.update(obj)
69 self.handleLogRecord(record)
70 except:
71 raise
72
73 def unPickle(self, data):
74 return cPickle.loads(data)
75
76 def handleLogRecord(self, record):
77 logname = "logrecv.tcp." + record.name
78 record.msg = record.msg + " (via " + logname + ")"
79 logger = logging.getLogger(logname)
80 logger.handle(record)
81
82class LogRecordSocketReceiver(ThreadingTCPServer):
83 """
84 A simple-minded TCP socket-based logging receiver suitable for test
85 purposes.
86 """
87
88 allow_reuse_address = 1
89
90 def __init__(self, host='localhost',
91 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
92 handler=LogRecordStreamHandler):
93 ThreadingTCPServer.__init__(self, (host, port), handler)
94 self.abort = 0
95 self.timeout = 1
96
97 def serve_until_stopped(self):
98 abort = 0
99 while not abort:
100 rd, wr, ex = select([self.socket.fileno()],
101 [], [],
102 self.timeout)
103 if rd:
104 self.handle_request()
105 abort = self.abort
106
107def runTCP(tcpserver):
108 tcpserver.serve_until_stopped()
109
110#----------------------------------------------------------------------------
111# Test 0
112#----------------------------------------------------------------------------
113
114msgcount = 0
115
116def nextmessage():
117 global msgcount
118 rv = "Message %d" % msgcount
119 msgcount = msgcount + 1
120 return rv
121
122def test0():
123 ERR = logging.getLogger("ERR")
124 ERR.setLevel(logging.ERROR)
125 INF = logging.getLogger("INF")
126 INF.setLevel(logging.INFO)
127 INF_ERR = logging.getLogger("INF.ERR")
128 INF_ERR.setLevel(logging.ERROR)
129 DEB = logging.getLogger("DEB")
130 DEB.setLevel(logging.DEBUG)
131
132 INF_UNDEF = logging.getLogger("INF.UNDEF")
133 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
134 UNDEF = logging.getLogger("UNDEF")
135
136 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
137 CHILD = logging.getLogger("INF.BADPARENT")
138
139 #These should log
140 ERR.log(logging.FATAL, nextmessage())
141 ERR.error(nextmessage())
142
143 INF.log(logging.FATAL, nextmessage())
144 INF.error(nextmessage())
145 INF.warn(nextmessage())
146 INF.info(nextmessage())
147
148 INF_UNDEF.log(logging.FATAL, nextmessage())
149 INF_UNDEF.error(nextmessage())
150 INF_UNDEF.warn (nextmessage())
151 INF_UNDEF.info (nextmessage())
152
153 INF_ERR.log(logging.FATAL, nextmessage())
154 INF_ERR.error(nextmessage())
155
156 INF_ERR_UNDEF.log(logging.FATAL, nextmessage())
157 INF_ERR_UNDEF.error(nextmessage())
158
159 DEB.log(logging.FATAL, nextmessage())
160 DEB.error(nextmessage())
161 DEB.warn (nextmessage())
162 DEB.info (nextmessage())
163 DEB.debug(nextmessage())
164
165 UNDEF.log(logging.FATAL, nextmessage())
166 UNDEF.error(nextmessage())
167 UNDEF.warn (nextmessage())
168 UNDEF.info (nextmessage())
169
170 GRANDCHILD.log(logging.FATAL, nextmessage())
171 CHILD.log(logging.FATAL, nextmessage())
172
173 #These should not log
174 ERR.warn(nextmessage())
175 ERR.info(nextmessage())
176 ERR.debug(nextmessage())
177
178 INF.debug(nextmessage())
179 INF_UNDEF.debug(nextmessage())
180
181 INF_ERR.warn(nextmessage())
182 INF_ERR.info(nextmessage())
183 INF_ERR.debug(nextmessage())
184 INF_ERR_UNDEF.warn(nextmessage())
185 INF_ERR_UNDEF.info(nextmessage())
186 INF_ERR_UNDEF.debug(nextmessage())
187
188 INF.info("Messages should bear numbers 0 through 24.")
189
190#----------------------------------------------------------------------------
191# Test 1
192#----------------------------------------------------------------------------
193
194#
195# First, we define our levels. There can be as many as you want - the only
196# limitations are that they should be integers, the lowest should be > 0 and
197# larger values mean less information being logged. If you need specific
198# level values which do not fit into these limitations, you can use a
199# mapping dictionary to convert between your application levels and the
200# logging system.
201#
202SILENT = 10
203TACITURN = 9
204TERSE = 8
205EFFUSIVE = 7
206SOCIABLE = 6
207VERBOSE = 5
208TALKATIVE = 4
209GARRULOUS = 3
210CHATTERBOX = 2
211BORING = 1
212
213LEVEL_RANGE = range(BORING, SILENT + 1)
214
215#
216# Next, we define names for our levels. You don't need to do this - in which
217# case the system will use "Level n" to denote the text for the level.
218#
219my_logging_levels = {
220 SILENT : 'Silent',
221 TACITURN : 'Taciturn',
222 TERSE : 'Terse',
223 EFFUSIVE : 'Effusive',
224 SOCIABLE : 'Sociable',
225 VERBOSE : 'Verbose',
226 TALKATIVE : 'Talkative',
227 GARRULOUS : 'Garrulous',
228 CHATTERBOX : 'Chatterbox',
229 BORING : 'Boring',
230}
231
232#
233# Now, to demonstrate filtering: suppose for some perverse reason we only
234# want to print out all except GARRULOUS messages. Let's create a filter for
235# this purpose...
236#
237class SpecificLevelFilter(logging.Filter):
238 def __init__(self, lvl):
239 self.level = lvl
240
241 def filter(self, record):
242 return self.level != record.levelno
243
244class GarrulousFilter(SpecificLevelFilter):
245 def __init__(self):
246 SpecificLevelFilter.__init__(self, GARRULOUS)
247
248#
249# Now, let's demonstrate filtering at the logger. This time, use a filter
250# which excludes SOCIABLE and TACITURN messages. Note that GARRULOUS events
251# are still excluded.
252#
253class VerySpecificFilter(logging.Filter):
254 def filter(self, record):
255 return record.levelno not in [SOCIABLE, TACITURN]
256
257def message(s):
258 sys.stdout.write("%s\n" % s)
259
260SHOULD1 = "This should only be seen at the '%s' logging level (or lower)"
261
262def test1():
263#
264# Now, tell the logging system to associate names with our levels.
265#
266 for lvl in my_logging_levels.keys():
267 logging.addLevelName(lvl, my_logging_levels[lvl])
268
269#
270# Now, define a test function which logs an event at each of our levels.
271#
272
273 def doLog(log):
274 for lvl in LEVEL_RANGE:
275 log.log(lvl, SHOULD1, logging.getLevelName(lvl))
276
277 log = logging.getLogger("")
278 hdlr = log.handlers[0]
279#
280# Set the logging level to each different value and call the utility
281# function to log events.
282# In the output, you should see that each time round the loop, the number of
283# logging events which are actually output decreases.
284#
285 for lvl in LEVEL_RANGE:
286 message("-- setting logging level to '%s' -----" %
287 logging.getLevelName(lvl))
288 log.setLevel(lvl)
289 doLog(log)
290 #
291 # Now, we demonstrate level filtering at the handler level. Tell the
292 # handler defined above to filter at level 'SOCIABLE', and repeat the
293 # above loop. Compare the output from the two runs.
294 #
295 hdlr.setLevel(SOCIABLE)
296 message("-- Filtering at handler level to SOCIABLE --")
297 for lvl in LEVEL_RANGE:
298 message("-- setting logging level to '%s' -----" %
299 logging.getLevelName(lvl))
300 log.setLevel(lvl)
301 doLog(log)
302
303 hdlr.setLevel(0) #turn off level filtering at the handler
304
305 garr = GarrulousFilter()
306 hdlr.addFilter(garr)
307 message("-- Filtering using GARRULOUS filter --")
308 for lvl in LEVEL_RANGE:
309 message("-- setting logging level to '%s' -----" %
310 logging.getLevelName(lvl))
311 log.setLevel(lvl)
312 doLog(log)
313 spec = VerySpecificFilter()
314 log.addFilter(spec)
315 message("-- Filtering using specific filter for SOCIABLE, TACITURN --")
316 for lvl in LEVEL_RANGE:
317 message("-- setting logging level to '%s' -----" %
318 logging.getLevelName(lvl))
319 log.setLevel(lvl)
320 doLog(log)
321
322 log.removeFilter(spec)
323 hdlr.removeFilter(garr)
324 #Undo the one level which clashes...for regression tests
325 logging.addLevelName(logging.DEBUG, "DEBUG")
326
327#----------------------------------------------------------------------------
328# Test 2
329#----------------------------------------------------------------------------
330
331MSG = "-- logging %d at INFO, messages should be seen every 10 events --"
332def test2():
333 logger = logging.getLogger("")
334 sh = logger.handlers[0]
335 logger.removeHandler(sh)
336 mh = logging.handlers.MemoryHandler(10,logging.WARN, sh)
337 logger.setLevel(logging.DEBUG)
338 logger.addHandler(mh)
339 message("-- logging at DEBUG, nothing should be seen yet --")
340 logger.debug("Debug message")
341 message("-- logging at INFO, nothing should be seen yet --")
342 logger.info("Info message")
343 message("-- logging at WARN, 3 messages should be seen --")
344 logger.warn("Warn message")
345 for i in xrange(102):
346 message(MSG % i)
347 logger.info("Info index = %d", i)
348 mh.close()
349 logger.removeHandler(mh)
350 logger.addHandler(sh)
351
352#----------------------------------------------------------------------------
353# Test 3
354#----------------------------------------------------------------------------
355
356FILTER = "a.b"
357
358def doLog3():
359 logging.getLogger("a").info("Info 1")
360 logging.getLogger("a.b").info("Info 2")
361 logging.getLogger("a.c").info("Info 3")
362 logging.getLogger("a.b.c").info("Info 4")
363 logging.getLogger("a.b.c.d").info("Info 5")
364 logging.getLogger("a.bb.c").info("Info 6")
365 logging.getLogger("b").info("Info 7")
366 logging.getLogger("b.a").info("Info 8")
367 logging.getLogger("c.a.b").info("Info 9")
368 logging.getLogger("a.bb").info("Info 10")
369
370def test3():
371 root = logging.getLogger()
372 root.setLevel(logging.DEBUG)
373 hand = root.handlers[0]
374 message("Unfiltered...")
375 doLog3()
376 message("Filtered with '%s'..." % FILTER)
377 filt = logging.Filter(FILTER)
378 hand.addFilter(filt)
379 doLog3()
380 hand.removeFilter(filt)
381
382#----------------------------------------------------------------------------
383# Test Harness
384#----------------------------------------------------------------------------
385def banner(nm, typ):
386 sep = BANNER % (nm, typ)
387 sys.stdout.write(sep)
388 sys.stdout.flush()
389
390def test_main():
391 rootLogger = logging.getLogger("")
392 rootLogger.setLevel(logging.DEBUG)
393 hdlr = logging.StreamHandler(sys.stdout)
394 fmt = logging.Formatter(logging.BASIC_FORMAT)
395 hdlr.setFormatter(fmt)
396 rootLogger.addHandler(hdlr)
397
398 #Set up a handler such that all events are sent via a socket to the log
399 #receiver (logrecv).
400 #The handler will only be added to the rootLogger for some of the tests
401 hdlr = logging.handlers.SocketHandler('localhost',
402 logging.handlers.DEFAULT_TCP_LOGGING_PORT)
403
404 #Configure the logger for logrecv so events do not propagate beyond it.
405 #The sockLogger output is buffered in memory until the end of the test,
406 #and printed at the end.
407 sockOut = cStringIO.StringIO()
408 sockLogger = logging.getLogger("logrecv")
409 sockLogger.setLevel(logging.DEBUG)
410 sockhdlr = logging.StreamHandler(sockOut)
411 sockhdlr.setFormatter(logging.Formatter(
412 "%(name)s -> %(levelname)s: %(message)s"))
413 sockLogger.addHandler(sockhdlr)
414 sockLogger.propagate = 0
415
416 #Set up servers
417 threads = []
418 tcpserver = LogRecordSocketReceiver()
419 sys.stdout.write("About to start TCP server...\n")
420 threads.append(threading.Thread(target=runTCP, args=(tcpserver,)))
421
422 for thread in threads:
423 thread.start()
424 try:
425 banner("log_test0", "begin")
426
427 rootLogger.addHandler(hdlr)
428 test0()
429 rootLogger.removeHandler(hdlr)
430
431 banner("log_test0", "end")
432
433 banner("log_test1", "begin")
434 test1()
435 banner("log_test1", "end")
436
437 banner("log_test2", "begin")
438 test2()
439 banner("log_test2", "end")
440
441 banner("log_test3", "begin")
442 test3()
443 banner("log_test3", "end")
444
445 banner("logrecv output", "begin")
446 sys.stdout.write(sockOut.getvalue())
447 sockOut.close()
448 banner("logrecv output", "end")
449
450 finally:
451 #shut down server
452 tcpserver.abort = 1
453 for thread in threads:
454 thread.join()
455
456if __name__ == "__main__":
457 sys.stdout.write("test_logging\n")
458 test_main()
459 sys.stdout.flush()