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