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