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