blob: 93fb4a2706d7fbc0f29a8513289b877c96e9e34d [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
Guido van Rossum376e6362003-04-25 14:22:00 +000091socketDataProcessed = threading.Condition()
92
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
118 socketDataProcessed.acquire()
119 socketDataProcessed.notify()
120 socketDataProcessed.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000121
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000122 def process_request(self, request, client_address):
123 #import threading
124 t = threading.Thread(target = self.finish_request,
125 args = (request, client_address))
126 t.start()
127
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000128def runTCP(tcpserver):
129 tcpserver.serve_until_stopped()
130
131#----------------------------------------------------------------------------
132# Test 0
133#----------------------------------------------------------------------------
134
135msgcount = 0
136
137def nextmessage():
138 global msgcount
139 rv = "Message %d" % msgcount
140 msgcount = msgcount + 1
141 return rv
142
143def test0():
144 ERR = logging.getLogger("ERR")
145 ERR.setLevel(logging.ERROR)
146 INF = logging.getLogger("INF")
147 INF.setLevel(logging.INFO)
148 INF_ERR = logging.getLogger("INF.ERR")
149 INF_ERR.setLevel(logging.ERROR)
150 DEB = logging.getLogger("DEB")
151 DEB.setLevel(logging.DEBUG)
152
153 INF_UNDEF = logging.getLogger("INF.UNDEF")
154 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
155 UNDEF = logging.getLogger("UNDEF")
156
157 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
158 CHILD = logging.getLogger("INF.BADPARENT")
159
160 #These should log
161 ERR.log(logging.FATAL, nextmessage())
162 ERR.error(nextmessage())
163
164 INF.log(logging.FATAL, nextmessage())
165 INF.error(nextmessage())
166 INF.warn(nextmessage())
167 INF.info(nextmessage())
168
169 INF_UNDEF.log(logging.FATAL, nextmessage())
170 INF_UNDEF.error(nextmessage())
171 INF_UNDEF.warn (nextmessage())
172 INF_UNDEF.info (nextmessage())
173
174 INF_ERR.log(logging.FATAL, nextmessage())
175 INF_ERR.error(nextmessage())
176
177 INF_ERR_UNDEF.log(logging.FATAL, nextmessage())
178 INF_ERR_UNDEF.error(nextmessage())
179
180 DEB.log(logging.FATAL, nextmessage())
181 DEB.error(nextmessage())
182 DEB.warn (nextmessage())
183 DEB.info (nextmessage())
184 DEB.debug(nextmessage())
185
186 UNDEF.log(logging.FATAL, nextmessage())
187 UNDEF.error(nextmessage())
188 UNDEF.warn (nextmessage())
189 UNDEF.info (nextmessage())
190
191 GRANDCHILD.log(logging.FATAL, nextmessage())
192 CHILD.log(logging.FATAL, nextmessage())
193
194 #These should not log
195 ERR.warn(nextmessage())
196 ERR.info(nextmessage())
197 ERR.debug(nextmessage())
198
199 INF.debug(nextmessage())
200 INF_UNDEF.debug(nextmessage())
201
202 INF_ERR.warn(nextmessage())
203 INF_ERR.info(nextmessage())
204 INF_ERR.debug(nextmessage())
205 INF_ERR_UNDEF.warn(nextmessage())
206 INF_ERR_UNDEF.info(nextmessage())
207 INF_ERR_UNDEF.debug(nextmessage())
208
Guido van Rossum376e6362003-04-25 14:22:00 +0000209 INF.info(FINISH_UP)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000210
211#----------------------------------------------------------------------------
212# Test 1
213#----------------------------------------------------------------------------
214
215#
216# First, we define our levels. There can be as many as you want - the only
217# limitations are that they should be integers, the lowest should be > 0 and
218# larger values mean less information being logged. If you need specific
219# level values which do not fit into these limitations, you can use a
220# mapping dictionary to convert between your application levels and the
221# logging system.
222#
223SILENT = 10
224TACITURN = 9
225TERSE = 8
226EFFUSIVE = 7
227SOCIABLE = 6
228VERBOSE = 5
229TALKATIVE = 4
230GARRULOUS = 3
231CHATTERBOX = 2
232BORING = 1
233
234LEVEL_RANGE = range(BORING, SILENT + 1)
235
236#
237# Next, we define names for our levels. You don't need to do this - in which
238# case the system will use "Level n" to denote the text for the level.
239#
240my_logging_levels = {
241 SILENT : 'Silent',
242 TACITURN : 'Taciturn',
243 TERSE : 'Terse',
244 EFFUSIVE : 'Effusive',
245 SOCIABLE : 'Sociable',
246 VERBOSE : 'Verbose',
247 TALKATIVE : 'Talkative',
248 GARRULOUS : 'Garrulous',
249 CHATTERBOX : 'Chatterbox',
250 BORING : 'Boring',
251}
252
253#
254# Now, to demonstrate filtering: suppose for some perverse reason we only
255# want to print out all except GARRULOUS messages. Let's create a filter for
256# this purpose...
257#
258class SpecificLevelFilter(logging.Filter):
259 def __init__(self, lvl):
260 self.level = lvl
261
262 def filter(self, record):
263 return self.level != record.levelno
264
265class GarrulousFilter(SpecificLevelFilter):
266 def __init__(self):
267 SpecificLevelFilter.__init__(self, GARRULOUS)
268
269#
270# Now, let's demonstrate filtering at the logger. This time, use a filter
271# which excludes SOCIABLE and TACITURN messages. Note that GARRULOUS events
272# are still excluded.
273#
274class VerySpecificFilter(logging.Filter):
275 def filter(self, record):
276 return record.levelno not in [SOCIABLE, TACITURN]
277
278def message(s):
279 sys.stdout.write("%s\n" % s)
280
281SHOULD1 = "This should only be seen at the '%s' logging level (or lower)"
282
283def test1():
284#
285# Now, tell the logging system to associate names with our levels.
286#
287 for lvl in my_logging_levels.keys():
288 logging.addLevelName(lvl, my_logging_levels[lvl])
289
290#
291# Now, define a test function which logs an event at each of our levels.
292#
293
294 def doLog(log):
295 for lvl in LEVEL_RANGE:
296 log.log(lvl, SHOULD1, logging.getLevelName(lvl))
297
298 log = logging.getLogger("")
299 hdlr = log.handlers[0]
300#
301# Set the logging level to each different value and call the utility
302# function to log events.
303# In the output, you should see that each time round the loop, the number of
304# logging events which are actually output decreases.
305#
306 for lvl in LEVEL_RANGE:
307 message("-- setting logging level to '%s' -----" %
308 logging.getLevelName(lvl))
309 log.setLevel(lvl)
310 doLog(log)
311 #
312 # Now, we demonstrate level filtering at the handler level. Tell the
313 # handler defined above to filter at level 'SOCIABLE', and repeat the
314 # above loop. Compare the output from the two runs.
315 #
316 hdlr.setLevel(SOCIABLE)
317 message("-- Filtering at handler level to SOCIABLE --")
318 for lvl in LEVEL_RANGE:
319 message("-- setting logging level to '%s' -----" %
320 logging.getLevelName(lvl))
321 log.setLevel(lvl)
322 doLog(log)
323
324 hdlr.setLevel(0) #turn off level filtering at the handler
325
326 garr = GarrulousFilter()
327 hdlr.addFilter(garr)
328 message("-- Filtering using GARRULOUS filter --")
329 for lvl in LEVEL_RANGE:
330 message("-- setting logging level to '%s' -----" %
331 logging.getLevelName(lvl))
332 log.setLevel(lvl)
333 doLog(log)
334 spec = VerySpecificFilter()
335 log.addFilter(spec)
336 message("-- Filtering using specific filter for SOCIABLE, TACITURN --")
337 for lvl in LEVEL_RANGE:
338 message("-- setting logging level to '%s' -----" %
339 logging.getLevelName(lvl))
340 log.setLevel(lvl)
341 doLog(log)
342
343 log.removeFilter(spec)
344 hdlr.removeFilter(garr)
345 #Undo the one level which clashes...for regression tests
346 logging.addLevelName(logging.DEBUG, "DEBUG")
347
348#----------------------------------------------------------------------------
349# Test 2
350#----------------------------------------------------------------------------
351
352MSG = "-- logging %d at INFO, messages should be seen every 10 events --"
353def test2():
354 logger = logging.getLogger("")
355 sh = logger.handlers[0]
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000356 sh.close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000357 logger.removeHandler(sh)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000358 mh = logging.handlers.MemoryHandler(10,logging.WARNING, sh)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000359 logger.setLevel(logging.DEBUG)
360 logger.addHandler(mh)
361 message("-- logging at DEBUG, nothing should be seen yet --")
362 logger.debug("Debug message")
363 message("-- logging at INFO, nothing should be seen yet --")
364 logger.info("Info message")
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000365 message("-- logging at WARNING, 3 messages should be seen --")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000366 logger.warn("Warn message")
367 for i in xrange(102):
368 message(MSG % i)
369 logger.info("Info index = %d", i)
370 mh.close()
371 logger.removeHandler(mh)
372 logger.addHandler(sh)
373
374#----------------------------------------------------------------------------
375# Test 3
376#----------------------------------------------------------------------------
377
378FILTER = "a.b"
379
380def doLog3():
381 logging.getLogger("a").info("Info 1")
382 logging.getLogger("a.b").info("Info 2")
383 logging.getLogger("a.c").info("Info 3")
384 logging.getLogger("a.b.c").info("Info 4")
385 logging.getLogger("a.b.c.d").info("Info 5")
386 logging.getLogger("a.bb.c").info("Info 6")
387 logging.getLogger("b").info("Info 7")
388 logging.getLogger("b.a").info("Info 8")
389 logging.getLogger("c.a.b").info("Info 9")
390 logging.getLogger("a.bb").info("Info 10")
391
392def test3():
393 root = logging.getLogger()
394 root.setLevel(logging.DEBUG)
395 hand = root.handlers[0]
396 message("Unfiltered...")
397 doLog3()
398 message("Filtered with '%s'..." % FILTER)
399 filt = logging.Filter(FILTER)
400 hand.addFilter(filt)
401 doLog3()
402 hand.removeFilter(filt)
403
404#----------------------------------------------------------------------------
405# Test Harness
406#----------------------------------------------------------------------------
407def banner(nm, typ):
408 sep = BANNER % (nm, typ)
409 sys.stdout.write(sep)
410 sys.stdout.flush()
411
412def test_main():
413 rootLogger = logging.getLogger("")
414 rootLogger.setLevel(logging.DEBUG)
415 hdlr = logging.StreamHandler(sys.stdout)
416 fmt = logging.Formatter(logging.BASIC_FORMAT)
417 hdlr.setFormatter(fmt)
418 rootLogger.addHandler(hdlr)
419
420 #Set up a handler such that all events are sent via a socket to the log
421 #receiver (logrecv).
422 #The handler will only be added to the rootLogger for some of the tests
423 hdlr = logging.handlers.SocketHandler('localhost',
424 logging.handlers.DEFAULT_TCP_LOGGING_PORT)
425
426 #Configure the logger for logrecv so events do not propagate beyond it.
427 #The sockLogger output is buffered in memory until the end of the test,
428 #and printed at the end.
429 sockOut = cStringIO.StringIO()
430 sockLogger = logging.getLogger("logrecv")
431 sockLogger.setLevel(logging.DEBUG)
432 sockhdlr = logging.StreamHandler(sockOut)
433 sockhdlr.setFormatter(logging.Formatter(
434 "%(name)s -> %(levelname)s: %(message)s"))
435 sockLogger.addHandler(sockhdlr)
436 sockLogger.propagate = 0
437
438 #Set up servers
439 threads = []
440 tcpserver = LogRecordSocketReceiver()
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000441 #sys.stdout.write("About to start TCP server...\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000442 threads.append(threading.Thread(target=runTCP, args=(tcpserver,)))
443
444 for thread in threads:
445 thread.start()
446 try:
447 banner("log_test0", "begin")
448
449 rootLogger.addHandler(hdlr)
450 test0()
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000451 hdlr.close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000452 rootLogger.removeHandler(hdlr)
453
454 banner("log_test0", "end")
455
456 banner("log_test1", "begin")
457 test1()
458 banner("log_test1", "end")
459
460 banner("log_test2", "begin")
461 test2()
462 banner("log_test2", "end")
463
464 banner("log_test3", "begin")
465 test3()
466 banner("log_test3", "end")
467
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000468 finally:
Guido van Rossum376e6362003-04-25 14:22:00 +0000469 #wait for TCP receiver to terminate
470 socketDataProcessed.acquire()
471 socketDataProcessed.wait()
472 socketDataProcessed.release()
Guido van Rossumecf0f022003-04-26 00:21:31 +0000473 for thread in threads:
474 thread.join()
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000475 banner("logrecv output", "begin")
476 sys.stdout.write(sockOut.getvalue())
477 sockOut.close()
478 banner("logrecv output", "end")
479 sys.stdout.flush()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000480
481if __name__ == "__main__":
482 sys.stdout.write("test_logging\n")
483 test_main()