test_logging.py 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011
  1. # Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
  2. #
  3. # Permission to use, copy, modify, and distribute this software and its
  4. # documentation for any purpose and without fee is hereby granted,
  5. # provided that the above copyright notice appear in all copies and that
  6. # both that copyright notice and this permission notice appear in
  7. # supporting documentation, and that the name of Vinay Sajip
  8. # not be used in advertising or publicity pertaining to distribution
  9. # of the software without specific, written prior permission.
  10. # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  11. # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  12. # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  13. # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  14. # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  15. # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. """Test harness for the logging module. Run all tests.
  17. Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
  18. """
  19. import logging
  20. import logging.handlers
  21. import logging.config
  22. import codecs
  23. import cPickle
  24. import cStringIO
  25. import gc
  26. import json
  27. import os
  28. import random
  29. import re
  30. import select
  31. import socket
  32. from SocketServer import ThreadingTCPServer, StreamRequestHandler
  33. import struct
  34. import sys
  35. import tempfile
  36. from test.test_support import captured_stdout, run_with_locale, run_unittest
  37. import textwrap
  38. import time
  39. import unittest
  40. import warnings
  41. import weakref
  42. try:
  43. import threading
  44. except ImportError:
  45. threading = None
  46. class BaseTest(unittest.TestCase):
  47. """Base class for logging tests."""
  48. log_format = "%(name)s -> %(levelname)s: %(message)s"
  49. expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
  50. message_num = 0
  51. def setUp(self):
  52. """Setup the default logging stream to an internal StringIO instance,
  53. so that we can examine log output as we want."""
  54. logger_dict = logging.getLogger().manager.loggerDict
  55. logging._acquireLock()
  56. try:
  57. self.saved_handlers = logging._handlers.copy()
  58. self.saved_handler_list = logging._handlerList[:]
  59. self.saved_loggers = logger_dict.copy()
  60. self.saved_level_names = logging._levelNames.copy()
  61. finally:
  62. logging._releaseLock()
  63. # Set two unused loggers: one non-ASCII and one Unicode.
  64. # This is to test correct operation when sorting existing
  65. # loggers in the configuration code. See issue 8201.
  66. logging.getLogger("\xab\xd7\xbb")
  67. logging.getLogger(u"\u013f\u00d6\u0047")
  68. self.root_logger = logging.getLogger("")
  69. self.original_logging_level = self.root_logger.getEffectiveLevel()
  70. self.stream = cStringIO.StringIO()
  71. self.root_logger.setLevel(logging.DEBUG)
  72. self.root_hdlr = logging.StreamHandler(self.stream)
  73. self.root_formatter = logging.Formatter(self.log_format)
  74. self.root_hdlr.setFormatter(self.root_formatter)
  75. self.root_logger.addHandler(self.root_hdlr)
  76. def tearDown(self):
  77. """Remove our logging stream, and restore the original logging
  78. level."""
  79. self.stream.close()
  80. self.root_logger.removeHandler(self.root_hdlr)
  81. while self.root_logger.handlers:
  82. h = self.root_logger.handlers[0]
  83. self.root_logger.removeHandler(h)
  84. h.close()
  85. self.root_logger.setLevel(self.original_logging_level)
  86. logging._acquireLock()
  87. try:
  88. logging._levelNames.clear()
  89. logging._levelNames.update(self.saved_level_names)
  90. logging._handlers.clear()
  91. logging._handlers.update(self.saved_handlers)
  92. logging._handlerList[:] = self.saved_handler_list
  93. loggerDict = logging.getLogger().manager.loggerDict
  94. loggerDict.clear()
  95. loggerDict.update(self.saved_loggers)
  96. finally:
  97. logging._releaseLock()
  98. def assert_log_lines(self, expected_values, stream=None):
  99. """Match the collected log lines against the regular expression
  100. self.expected_log_pat, and compare the extracted group values to
  101. the expected_values list of tuples."""
  102. stream = stream or self.stream
  103. pat = re.compile(self.expected_log_pat)
  104. try:
  105. stream.reset()
  106. actual_lines = stream.readlines()
  107. except AttributeError:
  108. # StringIO.StringIO lacks a reset() method.
  109. actual_lines = stream.getvalue().splitlines()
  110. self.assertEqual(len(actual_lines), len(expected_values))
  111. for actual, expected in zip(actual_lines, expected_values):
  112. match = pat.search(actual)
  113. if not match:
  114. self.fail("Log line does not match expected pattern:\n" +
  115. actual)
  116. self.assertEqual(tuple(match.groups()), expected)
  117. s = stream.read()
  118. if s:
  119. self.fail("Remaining output at end of log stream:\n" + s)
  120. def next_message(self):
  121. """Generate a message consisting solely of an auto-incrementing
  122. integer."""
  123. self.message_num += 1
  124. return "%d" % self.message_num
  125. class BuiltinLevelsTest(BaseTest):
  126. """Test builtin levels and their inheritance."""
  127. def test_flat(self):
  128. #Logging levels in a flat logger namespace.
  129. m = self.next_message
  130. ERR = logging.getLogger("ERR")
  131. ERR.setLevel(logging.ERROR)
  132. INF = logging.getLogger("INF")
  133. INF.setLevel(logging.INFO)
  134. DEB = logging.getLogger("DEB")
  135. DEB.setLevel(logging.DEBUG)
  136. # These should log.
  137. ERR.log(logging.CRITICAL, m())
  138. ERR.error(m())
  139. INF.log(logging.CRITICAL, m())
  140. INF.error(m())
  141. INF.warn(m())
  142. INF.info(m())
  143. DEB.log(logging.CRITICAL, m())
  144. DEB.error(m())
  145. DEB.warn (m())
  146. DEB.info (m())
  147. DEB.debug(m())
  148. # These should not log.
  149. ERR.warn(m())
  150. ERR.info(m())
  151. ERR.debug(m())
  152. INF.debug(m())
  153. self.assert_log_lines([
  154. ('ERR', 'CRITICAL', '1'),
  155. ('ERR', 'ERROR', '2'),
  156. ('INF', 'CRITICAL', '3'),
  157. ('INF', 'ERROR', '4'),
  158. ('INF', 'WARNING', '5'),
  159. ('INF', 'INFO', '6'),
  160. ('DEB', 'CRITICAL', '7'),
  161. ('DEB', 'ERROR', '8'),
  162. ('DEB', 'WARNING', '9'),
  163. ('DEB', 'INFO', '10'),
  164. ('DEB', 'DEBUG', '11'),
  165. ])
  166. def test_nested_explicit(self):
  167. # Logging levels in a nested namespace, all explicitly set.
  168. m = self.next_message
  169. INF = logging.getLogger("INF")
  170. INF.setLevel(logging.INFO)
  171. INF_ERR = logging.getLogger("INF.ERR")
  172. INF_ERR.setLevel(logging.ERROR)
  173. # These should log.
  174. INF_ERR.log(logging.CRITICAL, m())
  175. INF_ERR.error(m())
  176. # These should not log.
  177. INF_ERR.warn(m())
  178. INF_ERR.info(m())
  179. INF_ERR.debug(m())
  180. self.assert_log_lines([
  181. ('INF.ERR', 'CRITICAL', '1'),
  182. ('INF.ERR', 'ERROR', '2'),
  183. ])
  184. def test_nested_inherited(self):
  185. #Logging levels in a nested namespace, inherited from parent loggers.
  186. m = self.next_message
  187. INF = logging.getLogger("INF")
  188. INF.setLevel(logging.INFO)
  189. INF_ERR = logging.getLogger("INF.ERR")
  190. INF_ERR.setLevel(logging.ERROR)
  191. INF_UNDEF = logging.getLogger("INF.UNDEF")
  192. INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
  193. UNDEF = logging.getLogger("UNDEF")
  194. # These should log.
  195. INF_UNDEF.log(logging.CRITICAL, m())
  196. INF_UNDEF.error(m())
  197. INF_UNDEF.warn(m())
  198. INF_UNDEF.info(m())
  199. INF_ERR_UNDEF.log(logging.CRITICAL, m())
  200. INF_ERR_UNDEF.error(m())
  201. # These should not log.
  202. INF_UNDEF.debug(m())
  203. INF_ERR_UNDEF.warn(m())
  204. INF_ERR_UNDEF.info(m())
  205. INF_ERR_UNDEF.debug(m())
  206. self.assert_log_lines([
  207. ('INF.UNDEF', 'CRITICAL', '1'),
  208. ('INF.UNDEF', 'ERROR', '2'),
  209. ('INF.UNDEF', 'WARNING', '3'),
  210. ('INF.UNDEF', 'INFO', '4'),
  211. ('INF.ERR.UNDEF', 'CRITICAL', '5'),
  212. ('INF.ERR.UNDEF', 'ERROR', '6'),
  213. ])
  214. def test_nested_with_virtual_parent(self):
  215. # Logging levels when some parent does not exist yet.
  216. m = self.next_message
  217. INF = logging.getLogger("INF")
  218. GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
  219. CHILD = logging.getLogger("INF.BADPARENT")
  220. INF.setLevel(logging.INFO)
  221. # These should log.
  222. GRANDCHILD.log(logging.FATAL, m())
  223. GRANDCHILD.info(m())
  224. CHILD.log(logging.FATAL, m())
  225. CHILD.info(m())
  226. # These should not log.
  227. GRANDCHILD.debug(m())
  228. CHILD.debug(m())
  229. self.assert_log_lines([
  230. ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
  231. ('INF.BADPARENT.UNDEF', 'INFO', '2'),
  232. ('INF.BADPARENT', 'CRITICAL', '3'),
  233. ('INF.BADPARENT', 'INFO', '4'),
  234. ])
  235. def test_invalid_name(self):
  236. self.assertRaises(TypeError, logging.getLogger, any)
  237. class BasicFilterTest(BaseTest):
  238. """Test the bundled Filter class."""
  239. def test_filter(self):
  240. # Only messages satisfying the specified criteria pass through the
  241. # filter.
  242. filter_ = logging.Filter("spam.eggs")
  243. handler = self.root_logger.handlers[0]
  244. try:
  245. handler.addFilter(filter_)
  246. spam = logging.getLogger("spam")
  247. spam_eggs = logging.getLogger("spam.eggs")
  248. spam_eggs_fish = logging.getLogger("spam.eggs.fish")
  249. spam_bakedbeans = logging.getLogger("spam.bakedbeans")
  250. spam.info(self.next_message())
  251. spam_eggs.info(self.next_message()) # Good.
  252. spam_eggs_fish.info(self.next_message()) # Good.
  253. spam_bakedbeans.info(self.next_message())
  254. self.assert_log_lines([
  255. ('spam.eggs', 'INFO', '2'),
  256. ('spam.eggs.fish', 'INFO', '3'),
  257. ])
  258. finally:
  259. handler.removeFilter(filter_)
  260. #
  261. # First, we define our levels. There can be as many as you want - the only
  262. # limitations are that they should be integers, the lowest should be > 0 and
  263. # larger values mean less information being logged. If you need specific
  264. # level values which do not fit into these limitations, you can use a
  265. # mapping dictionary to convert between your application levels and the
  266. # logging system.
  267. #
  268. SILENT = 120
  269. TACITURN = 119
  270. TERSE = 118
  271. EFFUSIVE = 117
  272. SOCIABLE = 116
  273. VERBOSE = 115
  274. TALKATIVE = 114
  275. GARRULOUS = 113
  276. CHATTERBOX = 112
  277. BORING = 111
  278. LEVEL_RANGE = range(BORING, SILENT + 1)
  279. #
  280. # Next, we define names for our levels. You don't need to do this - in which
  281. # case the system will use "Level n" to denote the text for the level.
  282. #
  283. my_logging_levels = {
  284. SILENT : 'Silent',
  285. TACITURN : 'Taciturn',
  286. TERSE : 'Terse',
  287. EFFUSIVE : 'Effusive',
  288. SOCIABLE : 'Sociable',
  289. VERBOSE : 'Verbose',
  290. TALKATIVE : 'Talkative',
  291. GARRULOUS : 'Garrulous',
  292. CHATTERBOX : 'Chatterbox',
  293. BORING : 'Boring',
  294. }
  295. class GarrulousFilter(logging.Filter):
  296. """A filter which blocks garrulous messages."""
  297. def filter(self, record):
  298. return record.levelno != GARRULOUS
  299. class VerySpecificFilter(logging.Filter):
  300. """A filter which blocks sociable and taciturn messages."""
  301. def filter(self, record):
  302. return record.levelno not in [SOCIABLE, TACITURN]
  303. class CustomLevelsAndFiltersTest(BaseTest):
  304. """Test various filtering possibilities with custom logging levels."""
  305. # Skip the logger name group.
  306. expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
  307. def setUp(self):
  308. BaseTest.setUp(self)
  309. for k, v in my_logging_levels.items():
  310. logging.addLevelName(k, v)
  311. def log_at_all_levels(self, logger):
  312. for lvl in LEVEL_RANGE:
  313. logger.log(lvl, self.next_message())
  314. def test_logger_filter(self):
  315. # Filter at logger level.
  316. self.root_logger.setLevel(VERBOSE)
  317. # Levels >= 'Verbose' are good.
  318. self.log_at_all_levels(self.root_logger)
  319. self.assert_log_lines([
  320. ('Verbose', '5'),
  321. ('Sociable', '6'),
  322. ('Effusive', '7'),
  323. ('Terse', '8'),
  324. ('Taciturn', '9'),
  325. ('Silent', '10'),
  326. ])
  327. def test_handler_filter(self):
  328. # Filter at handler level.
  329. self.root_logger.handlers[0].setLevel(SOCIABLE)
  330. try:
  331. # Levels >= 'Sociable' are good.
  332. self.log_at_all_levels(self.root_logger)
  333. self.assert_log_lines([
  334. ('Sociable', '6'),
  335. ('Effusive', '7'),
  336. ('Terse', '8'),
  337. ('Taciturn', '9'),
  338. ('Silent', '10'),
  339. ])
  340. finally:
  341. self.root_logger.handlers[0].setLevel(logging.NOTSET)
  342. def test_specific_filters(self):
  343. # Set a specific filter object on the handler, and then add another
  344. # filter object on the logger itself.
  345. handler = self.root_logger.handlers[0]
  346. specific_filter = None
  347. garr = GarrulousFilter()
  348. handler.addFilter(garr)
  349. try:
  350. self.log_at_all_levels(self.root_logger)
  351. first_lines = [
  352. # Notice how 'Garrulous' is missing
  353. ('Boring', '1'),
  354. ('Chatterbox', '2'),
  355. ('Talkative', '4'),
  356. ('Verbose', '5'),
  357. ('Sociable', '6'),
  358. ('Effusive', '7'),
  359. ('Terse', '8'),
  360. ('Taciturn', '9'),
  361. ('Silent', '10'),
  362. ]
  363. self.assert_log_lines(first_lines)
  364. specific_filter = VerySpecificFilter()
  365. self.root_logger.addFilter(specific_filter)
  366. self.log_at_all_levels(self.root_logger)
  367. self.assert_log_lines(first_lines + [
  368. # Not only 'Garrulous' is still missing, but also 'Sociable'
  369. # and 'Taciturn'
  370. ('Boring', '11'),
  371. ('Chatterbox', '12'),
  372. ('Talkative', '14'),
  373. ('Verbose', '15'),
  374. ('Effusive', '17'),
  375. ('Terse', '18'),
  376. ('Silent', '20'),
  377. ])
  378. finally:
  379. if specific_filter:
  380. self.root_logger.removeFilter(specific_filter)
  381. handler.removeFilter(garr)
  382. class MemoryHandlerTest(BaseTest):
  383. """Tests for the MemoryHandler."""
  384. # Do not bother with a logger name group.
  385. expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
  386. def setUp(self):
  387. BaseTest.setUp(self)
  388. self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
  389. self.root_hdlr)
  390. self.mem_logger = logging.getLogger('mem')
  391. self.mem_logger.propagate = 0
  392. self.mem_logger.addHandler(self.mem_hdlr)
  393. def tearDown(self):
  394. self.mem_hdlr.close()
  395. BaseTest.tearDown(self)
  396. def test_flush(self):
  397. # The memory handler flushes to its target handler based on specific
  398. # criteria (message count and message level).
  399. self.mem_logger.debug(self.next_message())
  400. self.assert_log_lines([])
  401. self.mem_logger.info(self.next_message())
  402. self.assert_log_lines([])
  403. # This will flush because the level is >= logging.WARNING
  404. self.mem_logger.warn(self.next_message())
  405. lines = [
  406. ('DEBUG', '1'),
  407. ('INFO', '2'),
  408. ('WARNING', '3'),
  409. ]
  410. self.assert_log_lines(lines)
  411. for n in (4, 14):
  412. for i in range(9):
  413. self.mem_logger.debug(self.next_message())
  414. self.assert_log_lines(lines)
  415. # This will flush because it's the 10th message since the last
  416. # flush.
  417. self.mem_logger.debug(self.next_message())
  418. lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
  419. self.assert_log_lines(lines)
  420. self.mem_logger.debug(self.next_message())
  421. self.assert_log_lines(lines)
  422. class ExceptionFormatter(logging.Formatter):
  423. """A special exception formatter."""
  424. def formatException(self, ei):
  425. return "Got a [%s]" % ei[0].__name__
  426. class ConfigFileTest(BaseTest):
  427. """Reading logging config from a .ini-style config file."""
  428. expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
  429. # config0 is a standard configuration.
  430. config0 = """
  431. [loggers]
  432. keys=root
  433. [handlers]
  434. keys=hand1
  435. [formatters]
  436. keys=form1
  437. [logger_root]
  438. level=WARNING
  439. handlers=hand1
  440. [handler_hand1]
  441. class=StreamHandler
  442. level=NOTSET
  443. formatter=form1
  444. args=(sys.stdout,)
  445. [formatter_form1]
  446. format=%(levelname)s ++ %(message)s
  447. datefmt=
  448. """
  449. # config1 adds a little to the standard configuration.
  450. config1 = """
  451. [loggers]
  452. keys=root,parser
  453. [handlers]
  454. keys=hand1
  455. [formatters]
  456. keys=form1
  457. [logger_root]
  458. level=WARNING
  459. handlers=
  460. [logger_parser]
  461. level=DEBUG
  462. handlers=hand1
  463. propagate=1
  464. qualname=compiler.parser
  465. [handler_hand1]
  466. class=StreamHandler
  467. level=NOTSET
  468. formatter=form1
  469. args=(sys.stdout,)
  470. [formatter_form1]
  471. format=%(levelname)s ++ %(message)s
  472. datefmt=
  473. """
  474. # config1a moves the handler to the root.
  475. config1a = """
  476. [loggers]
  477. keys=root,parser
  478. [handlers]
  479. keys=hand1
  480. [formatters]
  481. keys=form1
  482. [logger_root]
  483. level=WARNING
  484. handlers=hand1
  485. [logger_parser]
  486. level=DEBUG
  487. handlers=
  488. propagate=1
  489. qualname=compiler.parser
  490. [handler_hand1]
  491. class=StreamHandler
  492. level=NOTSET
  493. formatter=form1
  494. args=(sys.stdout,)
  495. [formatter_form1]
  496. format=%(levelname)s ++ %(message)s
  497. datefmt=
  498. """
  499. # config2 has a subtle configuration error that should be reported
  500. config2 = config1.replace("sys.stdout", "sys.stbout")
  501. # config3 has a less subtle configuration error
  502. config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
  503. # config4 specifies a custom formatter class to be loaded
  504. config4 = """
  505. [loggers]
  506. keys=root
  507. [handlers]
  508. keys=hand1
  509. [formatters]
  510. keys=form1
  511. [logger_root]
  512. level=NOTSET
  513. handlers=hand1
  514. [handler_hand1]
  515. class=StreamHandler
  516. level=NOTSET
  517. formatter=form1
  518. args=(sys.stdout,)
  519. [formatter_form1]
  520. class=""" + __name__ + """.ExceptionFormatter
  521. format=%(levelname)s:%(name)s:%(message)s
  522. datefmt=
  523. """
  524. # config5 specifies a custom handler class to be loaded
  525. config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
  526. # config6 uses ', ' delimiters in the handlers and formatters sections
  527. config6 = """
  528. [loggers]
  529. keys=root,parser
  530. [handlers]
  531. keys=hand1, hand2
  532. [formatters]
  533. keys=form1, form2
  534. [logger_root]
  535. level=WARNING
  536. handlers=
  537. [logger_parser]
  538. level=DEBUG
  539. handlers=hand1
  540. propagate=1
  541. qualname=compiler.parser
  542. [handler_hand1]
  543. class=StreamHandler
  544. level=NOTSET
  545. formatter=form1
  546. args=(sys.stdout,)
  547. [handler_hand2]
  548. class=StreamHandler
  549. level=NOTSET
  550. formatter=form1
  551. args=(sys.stderr,)
  552. [formatter_form1]
  553. format=%(levelname)s ++ %(message)s
  554. datefmt=
  555. [formatter_form2]
  556. format=%(message)s
  557. datefmt=
  558. """
  559. # config7 adds a compiler logger.
  560. config7 = """
  561. [loggers]
  562. keys=root,parser,compiler
  563. [handlers]
  564. keys=hand1
  565. [formatters]
  566. keys=form1
  567. [logger_root]
  568. level=WARNING
  569. handlers=hand1
  570. [logger_compiler]
  571. level=DEBUG
  572. handlers=
  573. propagate=1
  574. qualname=compiler
  575. [logger_parser]
  576. level=DEBUG
  577. handlers=
  578. propagate=1
  579. qualname=compiler.parser
  580. [handler_hand1]
  581. class=StreamHandler
  582. level=NOTSET
  583. formatter=form1
  584. args=(sys.stdout,)
  585. [formatter_form1]
  586. format=%(levelname)s ++ %(message)s
  587. datefmt=
  588. """
  589. disable_test = """
  590. [loggers]
  591. keys=root
  592. [handlers]
  593. keys=screen
  594. [formatters]
  595. keys=
  596. [logger_root]
  597. level=DEBUG
  598. handlers=screen
  599. [handler_screen]
  600. level=DEBUG
  601. class=StreamHandler
  602. args=(sys.stdout,)
  603. formatter=
  604. """
  605. def apply_config(self, conf, **kwargs):
  606. file = cStringIO.StringIO(textwrap.dedent(conf))
  607. logging.config.fileConfig(file, **kwargs)
  608. def test_config0_ok(self):
  609. # A simple config file which overrides the default settings.
  610. with captured_stdout() as output:
  611. self.apply_config(self.config0)
  612. logger = logging.getLogger()
  613. # Won't output anything
  614. logger.info(self.next_message())
  615. # Outputs a message
  616. logger.error(self.next_message())
  617. self.assert_log_lines([
  618. ('ERROR', '2'),
  619. ], stream=output)
  620. # Original logger output is empty.
  621. self.assert_log_lines([])
  622. def test_config1_ok(self, config=config1):
  623. # A config file defining a sub-parser as well.
  624. with captured_stdout() as output:
  625. self.apply_config(config)
  626. logger = logging.getLogger("compiler.parser")
  627. # Both will output a message
  628. logger.info(self.next_message())
  629. logger.error(self.next_message())
  630. self.assert_log_lines([
  631. ('INFO', '1'),
  632. ('ERROR', '2'),
  633. ], stream=output)
  634. # Original logger output is empty.
  635. self.assert_log_lines([])
  636. def test_config2_failure(self):
  637. # A simple config file which overrides the default settings.
  638. self.assertRaises(StandardError, self.apply_config, self.config2)
  639. def test_config3_failure(self):
  640. # A simple config file which overrides the default settings.
  641. self.assertRaises(StandardError, self.apply_config, self.config3)
  642. def test_config4_ok(self):
  643. # A config file specifying a custom formatter class.
  644. with captured_stdout() as output:
  645. self.apply_config(self.config4)
  646. logger = logging.getLogger()
  647. try:
  648. raise RuntimeError()
  649. except RuntimeError:
  650. logging.exception("just testing")
  651. sys.stdout.seek(0)
  652. self.assertEqual(output.getvalue(),
  653. "ERROR:root:just testing\nGot a [RuntimeError]\n")
  654. # Original logger output is empty
  655. self.assert_log_lines([])
  656. def test_config5_ok(self):
  657. self.test_config1_ok(config=self.config5)
  658. def test_config6_ok(self):
  659. self.test_config1_ok(config=self.config6)
  660. def test_config7_ok(self):
  661. with captured_stdout() as output:
  662. self.apply_config(self.config1a)
  663. logger = logging.getLogger("compiler.parser")
  664. # See issue #11424. compiler-hyphenated sorts
  665. # between compiler and compiler.xyz and this
  666. # was preventing compiler.xyz from being included
  667. # in the child loggers of compiler because of an
  668. # overzealous loop termination condition.
  669. hyphenated = logging.getLogger('compiler-hyphenated')
  670. # All will output a message
  671. logger.info(self.next_message())
  672. logger.error(self.next_message())
  673. hyphenated.critical(self.next_message())
  674. self.assert_log_lines([
  675. ('INFO', '1'),
  676. ('ERROR', '2'),
  677. ('CRITICAL', '3'),
  678. ], stream=output)
  679. # Original logger output is empty.
  680. self.assert_log_lines([])
  681. with captured_stdout() as output:
  682. self.apply_config(self.config7)
  683. logger = logging.getLogger("compiler.parser")
  684. self.assertFalse(logger.disabled)
  685. # Both will output a message
  686. logger.info(self.next_message())
  687. logger.error(self.next_message())
  688. logger = logging.getLogger("compiler.lexer")
  689. # Both will output a message
  690. logger.info(self.next_message())
  691. logger.error(self.next_message())
  692. # Will not appear
  693. hyphenated.critical(self.next_message())
  694. self.assert_log_lines([
  695. ('INFO', '4'),
  696. ('ERROR', '5'),
  697. ('INFO', '6'),
  698. ('ERROR', '7'),
  699. ], stream=output)
  700. # Original logger output is empty.
  701. self.assert_log_lines([])
  702. def test_logger_disabling(self):
  703. self.apply_config(self.disable_test)
  704. logger = logging.getLogger('foo')
  705. self.assertFalse(logger.disabled)
  706. self.apply_config(self.disable_test)
  707. self.assertTrue(logger.disabled)
  708. self.apply_config(self.disable_test, disable_existing_loggers=False)
  709. self.assertFalse(logger.disabled)
  710. class LogRecordStreamHandler(StreamRequestHandler):
  711. """Handler for a streaming logging request. It saves the log message in the
  712. TCP server's 'log_output' attribute."""
  713. TCP_LOG_END = "!!!END!!!"
  714. def handle(self):
  715. """Handle multiple requests - each expected to be of 4-byte length,
  716. followed by the LogRecord in pickle format. Logs the record
  717. according to whatever policy is configured locally."""
  718. while True:
  719. chunk = self.connection.recv(4)
  720. if len(chunk) < 4:
  721. break
  722. slen = struct.unpack(">L", chunk)[0]
  723. chunk = self.connection.recv(slen)
  724. while len(chunk) < slen:
  725. chunk = chunk + self.connection.recv(slen - len(chunk))
  726. obj = self.unpickle(chunk)
  727. record = logging.makeLogRecord(obj)
  728. self.handle_log_record(record)
  729. def unpickle(self, data):
  730. return cPickle.loads(data)
  731. def handle_log_record(self, record):
  732. # If the end-of-messages sentinel is seen, tell the server to
  733. # terminate.
  734. if self.TCP_LOG_END in record.msg:
  735. self.server.abort = 1
  736. return
  737. self.server.log_output += record.msg + "\n"
  738. class LogRecordSocketReceiver(ThreadingTCPServer):
  739. """A simple-minded TCP socket-based logging receiver suitable for test
  740. purposes."""
  741. allow_reuse_address = 1
  742. log_output = ""
  743. def __init__(self, host='localhost',
  744. port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
  745. handler=LogRecordStreamHandler):
  746. ThreadingTCPServer.__init__(self, (host, port), handler)
  747. self.abort = False
  748. self.timeout = 0.1
  749. self.finished = threading.Event()
  750. def serve_until_stopped(self):
  751. while not self.abort:
  752. rd, wr, ex = select.select([self.socket.fileno()], [], [],
  753. self.timeout)
  754. if rd:
  755. self.handle_request()
  756. # Notify the main thread that we're about to exit
  757. self.finished.set()
  758. # close the listen socket
  759. self.server_close()
  760. @unittest.skipUnless(threading, 'Threading required for this test.')
  761. class SocketHandlerTest(BaseTest):
  762. """Test for SocketHandler objects."""
  763. def setUp(self):
  764. """Set up a TCP server to receive log messages, and a SocketHandler
  765. pointing to that server's address and port."""
  766. BaseTest.setUp(self)
  767. self.tcpserver = LogRecordSocketReceiver(port=0)
  768. self.port = self.tcpserver.socket.getsockname()[1]
  769. self.threads = [
  770. threading.Thread(target=self.tcpserver.serve_until_stopped)]
  771. for thread in self.threads:
  772. thread.start()
  773. self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
  774. self.sock_hdlr.setFormatter(self.root_formatter)
  775. self.root_logger.removeHandler(self.root_logger.handlers[0])
  776. self.root_logger.addHandler(self.sock_hdlr)
  777. def tearDown(self):
  778. """Shutdown the TCP server."""
  779. try:
  780. self.tcpserver.abort = True
  781. del self.tcpserver
  782. self.root_logger.removeHandler(self.sock_hdlr)
  783. self.sock_hdlr.close()
  784. for thread in self.threads:
  785. thread.join(2.0)
  786. finally:
  787. BaseTest.tearDown(self)
  788. def get_output(self):
  789. """Get the log output as received by the TCP server."""
  790. # Signal the TCP receiver and wait for it to terminate.
  791. self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
  792. self.tcpserver.finished.wait(2.0)
  793. return self.tcpserver.log_output
  794. def test_output(self):
  795. # The log message sent to the SocketHandler is properly received.
  796. logger = logging.getLogger("tcp")
  797. logger.error("spam")
  798. logger.debug("eggs")
  799. self.assertEqual(self.get_output(), "spam\neggs\n")
  800. class MemoryTest(BaseTest):
  801. """Test memory persistence of logger objects."""
  802. def setUp(self):
  803. """Create a dict to remember potentially destroyed objects."""
  804. BaseTest.setUp(self)
  805. self._survivors = {}
  806. def _watch_for_survival(self, *args):
  807. """Watch the given objects for survival, by creating weakrefs to
  808. them."""
  809. for obj in args:
  810. key = id(obj), repr(obj)
  811. self._survivors[key] = weakref.ref(obj)
  812. def _assertTruesurvival(self):
  813. """Assert that all objects watched for survival have survived."""
  814. # Trigger cycle breaking.
  815. gc.collect()
  816. dead = []
  817. for (id_, repr_), ref in self._survivors.items():
  818. if ref() is None:
  819. dead.append(repr_)
  820. if dead:
  821. self.fail("%d objects should have survived "
  822. "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
  823. def test_persistent_loggers(self):
  824. # Logger objects are persistent and retain their configuration, even
  825. # if visible references are destroyed.
  826. self.root_logger.setLevel(logging.INFO)
  827. foo = logging.getLogger("foo")
  828. self._watch_for_survival(foo)
  829. foo.setLevel(logging.DEBUG)
  830. self.root_logger.debug(self.next_message())
  831. foo.debug(self.next_message())
  832. self.assert_log_lines([
  833. ('foo', 'DEBUG', '2'),
  834. ])
  835. del foo
  836. # foo has survived.
  837. self._assertTruesurvival()
  838. # foo has retained its settings.
  839. bar = logging.getLogger("foo")
  840. bar.debug(self.next_message())
  841. self.assert_log_lines([
  842. ('foo', 'DEBUG', '2'),
  843. ('foo', 'DEBUG', '3'),
  844. ])
  845. class EncodingTest(BaseTest):
  846. def test_encoding_plain_file(self):
  847. # In Python 2.x, a plain file object is treated as having no encoding.
  848. log = logging.getLogger("test")
  849. fn = tempfile.mktemp(".log")
  850. # the non-ascii data we write to the log.
  851. data = "foo\x80"
  852. try:
  853. handler = logging.FileHandler(fn)
  854. log.addHandler(handler)
  855. try:
  856. # write non-ascii data to the log.
  857. log.warning(data)
  858. finally:
  859. log.removeHandler(handler)
  860. handler.close()
  861. # check we wrote exactly those bytes, ignoring trailing \n etc
  862. f = open(fn)
  863. try:
  864. self.assertEqual(f.read().rstrip(), data)
  865. finally:
  866. f.close()
  867. finally:
  868. if os.path.isfile(fn):
  869. os.remove(fn)
  870. def test_encoding_cyrillic_unicode(self):
  871. log = logging.getLogger("test")
  872. #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
  873. message = u'\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
  874. #Ensure it's written in a Cyrillic encoding
  875. writer_class = codecs.getwriter('cp1251')
  876. writer_class.encoding = 'cp1251'
  877. stream = cStringIO.StringIO()
  878. writer = writer_class(stream, 'strict')
  879. handler = logging.StreamHandler(writer)
  880. log.addHandler(handler)
  881. try:
  882. log.warning(message)
  883. finally:
  884. log.removeHandler(handler)
  885. handler.close()
  886. # check we wrote exactly those bytes, ignoring trailing \n etc
  887. s = stream.getvalue()
  888. #Compare against what the data should be when encoded in CP-1251
  889. self.assertEqual(s, '\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
  890. def test_encoding_utf16_unicode(self):
  891. # Issue #19267
  892. log = logging.getLogger("test")
  893. message = u'b\u0142\u0105d'
  894. writer_class = codecs.getwriter('utf-16-le')
  895. writer_class.encoding = 'utf-16-le'
  896. stream = cStringIO.StringIO()
  897. writer = writer_class(stream, 'strict')
  898. handler = logging.StreamHandler(writer)
  899. log.addHandler(handler)
  900. try:
  901. log.warning(message)
  902. finally:
  903. log.removeHandler(handler)
  904. handler.close()
  905. s = stream.getvalue()
  906. self.assertEqual(s, 'b\x00B\x01\x05\x01d\x00\n\x00')
  907. class WarningsTest(BaseTest):
  908. def test_warnings(self):
  909. with warnings.catch_warnings():
  910. logging.captureWarnings(True)
  911. try:
  912. warnings.filterwarnings("always", category=UserWarning)
  913. file = cStringIO.StringIO()
  914. h = logging.StreamHandler(file)
  915. logger = logging.getLogger("py.warnings")
  916. logger.addHandler(h)
  917. warnings.warn("I'm warning you...")
  918. logger.removeHandler(h)
  919. s = file.getvalue()
  920. h.close()
  921. self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
  922. #See if an explicit file uses the original implementation
  923. file = cStringIO.StringIO()
  924. warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
  925. file, "Dummy line")
  926. s = file.getvalue()
  927. file.close()
  928. self.assertEqual(s,
  929. "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
  930. finally:
  931. logging.captureWarnings(False)
  932. def formatFunc(format, datefmt=None):
  933. return logging.Formatter(format, datefmt)
  934. def handlerFunc():
  935. return logging.StreamHandler()
  936. class CustomHandler(logging.StreamHandler):
  937. pass
  938. class ConfigDictTest(BaseTest):
  939. """Reading logging config from a dictionary."""
  940. expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
  941. # config0 is a standard configuration.
  942. config0 = {
  943. 'version': 1,
  944. 'formatters': {
  945. 'form1' : {
  946. 'format' : '%(levelname)s ++ %(message)s',
  947. },
  948. },
  949. 'handlers' : {
  950. 'hand1' : {
  951. 'class' : 'logging.StreamHandler',
  952. 'formatter' : 'form1',
  953. 'level' : 'NOTSET',
  954. 'stream' : 'ext://sys.stdout',
  955. },
  956. },
  957. 'root' : {
  958. 'level' : 'WARNING',
  959. 'handlers' : ['hand1'],
  960. },
  961. }
  962. # config1 adds a little to the standard configuration.
  963. config1 = {
  964. 'version': 1,
  965. 'formatters': {
  966. 'form1' : {
  967. 'format' : '%(levelname)s ++ %(message)s',
  968. },
  969. },
  970. 'handlers' : {
  971. 'hand1' : {
  972. 'class' : 'logging.StreamHandler',
  973. 'formatter' : 'form1',
  974. 'level' : 'NOTSET',
  975. 'stream' : 'ext://sys.stdout',
  976. },
  977. },
  978. 'loggers' : {
  979. 'compiler.parser' : {
  980. 'level' : 'DEBUG',
  981. 'handlers' : ['hand1'],
  982. },
  983. },
  984. 'root' : {
  985. 'level' : 'WARNING',
  986. },
  987. }
  988. # config2 has a subtle configuration error that should be reported
  989. config2 = {
  990. 'version': 1,
  991. 'formatters': {
  992. 'form1' : {
  993. 'format' : '%(levelname)s ++ %(message)s',
  994. },
  995. },
  996. 'handlers' : {
  997. 'hand1' : {
  998. 'class' : 'logging.StreamHandler',
  999. 'formatter' : 'form1',
  1000. 'level' : 'NOTSET',
  1001. 'stream' : 'ext://sys.stdbout',
  1002. },
  1003. },
  1004. 'loggers' : {
  1005. 'compiler.parser' : {
  1006. 'level' : 'DEBUG',
  1007. 'handlers' : ['hand1'],
  1008. },
  1009. },
  1010. 'root' : {
  1011. 'level' : 'WARNING',
  1012. },
  1013. }
  1014. #As config1 but with a misspelt level on a handler
  1015. config2a = {
  1016. 'version': 1,
  1017. 'formatters': {
  1018. 'form1' : {
  1019. 'format' : '%(levelname)s ++ %(message)s',
  1020. },
  1021. },
  1022. 'handlers' : {
  1023. 'hand1' : {
  1024. 'class' : 'logging.StreamHandler',
  1025. 'formatter' : 'form1',
  1026. 'level' : 'NTOSET',
  1027. 'stream' : 'ext://sys.stdout',
  1028. },
  1029. },
  1030. 'loggers' : {
  1031. 'compiler.parser' : {
  1032. 'level' : 'DEBUG',
  1033. 'handlers' : ['hand1'],
  1034. },
  1035. },
  1036. 'root' : {
  1037. 'level' : 'WARNING',
  1038. },
  1039. }
  1040. #As config1 but with a misspelt level on a logger
  1041. config2b = {
  1042. 'version': 1,
  1043. 'formatters': {
  1044. 'form1' : {
  1045. 'format' : '%(levelname)s ++ %(message)s',
  1046. },
  1047. },
  1048. 'handlers' : {
  1049. 'hand1' : {
  1050. 'class' : 'logging.StreamHandler',
  1051. 'formatter' : 'form1',
  1052. 'level' : 'NOTSET',
  1053. 'stream' : 'ext://sys.stdout',
  1054. },
  1055. },
  1056. 'loggers' : {
  1057. 'compiler.parser' : {
  1058. 'level' : 'DEBUG',
  1059. 'handlers' : ['hand1'],
  1060. },
  1061. },
  1062. 'root' : {
  1063. 'level' : 'WRANING',
  1064. },
  1065. }
  1066. # config3 has a less subtle configuration error
  1067. config3 = {
  1068. 'version': 1,
  1069. 'formatters': {
  1070. 'form1' : {
  1071. 'format' : '%(levelname)s ++ %(message)s',
  1072. },
  1073. },
  1074. 'handlers' : {
  1075. 'hand1' : {
  1076. 'class' : 'logging.StreamHandler',
  1077. 'formatter' : 'misspelled_name',
  1078. 'level' : 'NOTSET',
  1079. 'stream' : 'ext://sys.stdout',
  1080. },
  1081. },
  1082. 'loggers' : {
  1083. 'compiler.parser' : {
  1084. 'level' : 'DEBUG',
  1085. 'handlers' : ['hand1'],
  1086. },
  1087. },
  1088. 'root' : {
  1089. 'level' : 'WARNING',
  1090. },
  1091. }
  1092. # config4 specifies a custom formatter class to be loaded
  1093. config4 = {
  1094. 'version': 1,
  1095. 'formatters': {
  1096. 'form1' : {
  1097. '()' : __name__ + '.ExceptionFormatter',
  1098. 'format' : '%(levelname)s:%(name)s:%(message)s',
  1099. },
  1100. },
  1101. 'handlers' : {
  1102. 'hand1' : {
  1103. 'class' : 'logging.StreamHandler',
  1104. 'formatter' : 'form1',
  1105. 'level' : 'NOTSET',
  1106. 'stream' : 'ext://sys.stdout',
  1107. },
  1108. },
  1109. 'root' : {
  1110. 'level' : 'NOTSET',
  1111. 'handlers' : ['hand1'],
  1112. },
  1113. }
  1114. # As config4 but using an actual callable rather than a string
  1115. config4a = {
  1116. 'version': 1,
  1117. 'formatters': {
  1118. 'form1' : {
  1119. '()' : ExceptionFormatter,
  1120. 'format' : '%(levelname)s:%(name)s:%(message)s',
  1121. },
  1122. 'form2' : {
  1123. '()' : __name__ + '.formatFunc',
  1124. 'format' : '%(levelname)s:%(name)s:%(message)s',
  1125. },
  1126. 'form3' : {
  1127. '()' : formatFunc,
  1128. 'format' : '%(levelname)s:%(name)s:%(message)s',
  1129. },
  1130. },
  1131. 'handlers' : {
  1132. 'hand1' : {
  1133. 'class' : 'logging.StreamHandler',
  1134. 'formatter' : 'form1',
  1135. 'level' : 'NOTSET',
  1136. 'stream' : 'ext://sys.stdout',
  1137. },
  1138. 'hand2' : {
  1139. '()' : handlerFunc,
  1140. },
  1141. },
  1142. 'root' : {
  1143. 'level' : 'NOTSET',
  1144. 'handlers' : ['hand1'],
  1145. },
  1146. }
  1147. # config5 specifies a custom handler class to be loaded
  1148. config5 = {
  1149. 'version': 1,
  1150. 'formatters': {
  1151. 'form1' : {
  1152. 'format' : '%(levelname)s ++ %(message)s',
  1153. },
  1154. },
  1155. 'handlers' : {
  1156. 'hand1' : {
  1157. 'class' : __name__ + '.CustomHandler',
  1158. 'formatter' : 'form1',
  1159. 'level' : 'NOTSET',
  1160. 'stream' : 'ext://sys.stdout',
  1161. },
  1162. },
  1163. 'loggers' : {
  1164. 'compiler.parser' : {
  1165. 'level' : 'DEBUG',
  1166. 'handlers' : ['hand1'],
  1167. },
  1168. },
  1169. 'root' : {
  1170. 'level' : 'WARNING',
  1171. },
  1172. }
  1173. # config6 specifies a custom handler class to be loaded
  1174. # but has bad arguments
  1175. config6 = {
  1176. 'version': 1,
  1177. 'formatters': {
  1178. 'form1' : {
  1179. 'format' : '%(levelname)s ++ %(message)s',
  1180. },
  1181. },
  1182. 'handlers' : {
  1183. 'hand1' : {
  1184. 'class' : __name__ + '.CustomHandler',
  1185. 'formatter' : 'form1',
  1186. 'level' : 'NOTSET',
  1187. 'stream' : 'ext://sys.stdout',
  1188. '9' : 'invalid parameter name',
  1189. },
  1190. },
  1191. 'loggers' : {
  1192. 'compiler.parser' : {
  1193. 'level' : 'DEBUG',
  1194. 'handlers' : ['hand1'],
  1195. },
  1196. },
  1197. 'root' : {
  1198. 'level' : 'WARNING',
  1199. },
  1200. }
  1201. #config 7 does not define compiler.parser but defines compiler.lexer
  1202. #so compiler.parser should be disabled after applying it
  1203. config7 = {
  1204. 'version': 1,
  1205. 'formatters': {
  1206. 'form1' : {
  1207. 'format' : '%(levelname)s ++ %(message)s',
  1208. },
  1209. },
  1210. 'handlers' : {
  1211. 'hand1' : {
  1212. 'class' : 'logging.StreamHandler',
  1213. 'formatter' : 'form1',
  1214. 'level' : 'NOTSET',
  1215. 'stream' : 'ext://sys.stdout',
  1216. },
  1217. },
  1218. 'loggers' : {
  1219. 'compiler.lexer' : {
  1220. 'level' : 'DEBUG',
  1221. 'handlers' : ['hand1'],
  1222. },
  1223. },
  1224. 'root' : {
  1225. 'level' : 'WARNING',
  1226. },
  1227. }
  1228. config8 = {
  1229. 'version': 1,
  1230. 'disable_existing_loggers' : False,
  1231. 'formatters': {
  1232. 'form1' : {
  1233. 'format' : '%(levelname)s ++ %(message)s',
  1234. },
  1235. },
  1236. 'handlers' : {
  1237. 'hand1' : {
  1238. 'class' : 'logging.StreamHandler',
  1239. 'formatter' : 'form1',
  1240. 'level' : 'NOTSET',
  1241. 'stream' : 'ext://sys.stdout',
  1242. },
  1243. },
  1244. 'loggers' : {
  1245. 'compiler' : {
  1246. 'level' : 'DEBUG',
  1247. 'handlers' : ['hand1'],
  1248. },
  1249. 'compiler.lexer' : {
  1250. },
  1251. },
  1252. 'root' : {
  1253. 'level' : 'WARNING',
  1254. },
  1255. }
  1256. config9 = {
  1257. 'version': 1,
  1258. 'formatters': {
  1259. 'form1' : {
  1260. 'format' : '%(levelname)s ++ %(message)s',
  1261. },
  1262. },
  1263. 'handlers' : {
  1264. 'hand1' : {
  1265. 'class' : 'logging.StreamHandler',
  1266. 'formatter' : 'form1',
  1267. 'level' : 'WARNING',
  1268. 'stream' : 'ext://sys.stdout',
  1269. },
  1270. },
  1271. 'loggers' : {
  1272. 'compiler.parser' : {
  1273. 'level' : 'WARNING',
  1274. 'handlers' : ['hand1'],
  1275. },
  1276. },
  1277. 'root' : {
  1278. 'level' : 'NOTSET',
  1279. },
  1280. }
  1281. config9a = {
  1282. 'version': 1,
  1283. 'incremental' : True,
  1284. 'handlers' : {
  1285. 'hand1' : {
  1286. 'level' : 'WARNING',
  1287. },
  1288. },
  1289. 'loggers' : {
  1290. 'compiler.parser' : {
  1291. 'level' : 'INFO',
  1292. },
  1293. },
  1294. }
  1295. config9b = {
  1296. 'version': 1,
  1297. 'incremental' : True,
  1298. 'handlers' : {
  1299. 'hand1' : {
  1300. 'level' : 'INFO',
  1301. },
  1302. },
  1303. 'loggers' : {
  1304. 'compiler.parser' : {
  1305. 'level' : 'INFO',
  1306. },
  1307. },
  1308. }
  1309. #As config1 but with a filter added
  1310. config10 = {
  1311. 'version': 1,
  1312. 'formatters': {
  1313. 'form1' : {
  1314. 'format' : '%(levelname)s ++ %(message)s',
  1315. },
  1316. },
  1317. 'filters' : {
  1318. 'filt1' : {
  1319. 'name' : 'compiler.parser',
  1320. },
  1321. },
  1322. 'handlers' : {
  1323. 'hand1' : {
  1324. 'class' : 'logging.StreamHandler',
  1325. 'formatter' : 'form1',
  1326. 'level' : 'NOTSET',
  1327. 'stream' : 'ext://sys.stdout',
  1328. 'filters' : ['filt1'],
  1329. },
  1330. },
  1331. 'loggers' : {
  1332. 'compiler.parser' : {
  1333. 'level' : 'DEBUG',
  1334. 'filters' : ['filt1'],
  1335. },
  1336. },
  1337. 'root' : {
  1338. 'level' : 'WARNING',
  1339. 'handlers' : ['hand1'],
  1340. },
  1341. }
  1342. #As config1 but using cfg:// references
  1343. config11 = {
  1344. 'version': 1,
  1345. 'true_formatters': {
  1346. 'form1' : {
  1347. 'format' : '%(levelname)s ++ %(message)s',
  1348. },
  1349. },
  1350. 'handler_configs': {
  1351. 'hand1' : {
  1352. 'class' : 'logging.StreamHandler',
  1353. 'formatter' : 'form1',
  1354. 'level' : 'NOTSET',
  1355. 'stream' : 'ext://sys.stdout',
  1356. },
  1357. },
  1358. 'formatters' : 'cfg://true_formatters',
  1359. 'handlers' : {
  1360. 'hand1' : 'cfg://handler_configs[hand1]',
  1361. },
  1362. 'loggers' : {
  1363. 'compiler.parser' : {
  1364. 'level' : 'DEBUG',
  1365. 'handlers' : ['hand1'],
  1366. },
  1367. },
  1368. 'root' : {
  1369. 'level' : 'WARNING',
  1370. },
  1371. }
  1372. #As config11 but missing the version key
  1373. config12 = {
  1374. 'true_formatters': {
  1375. 'form1' : {
  1376. 'format' : '%(levelname)s ++ %(message)s',
  1377. },
  1378. },
  1379. 'handler_configs': {
  1380. 'hand1' : {
  1381. 'class' : 'logging.StreamHandler',
  1382. 'formatter' : 'form1',
  1383. 'level' : 'NOTSET',
  1384. 'stream' : 'ext://sys.stdout',
  1385. },
  1386. },
  1387. 'formatters' : 'cfg://true_formatters',
  1388. 'handlers' : {
  1389. 'hand1' : 'cfg://handler_configs[hand1]',
  1390. },
  1391. 'loggers' : {
  1392. 'compiler.parser' : {
  1393. 'level' : 'DEBUG',
  1394. 'handlers' : ['hand1'],
  1395. },
  1396. },
  1397. 'root' : {
  1398. 'level' : 'WARNING',
  1399. },
  1400. }
  1401. #As config11 but using an unsupported version
  1402. config13 = {
  1403. 'version': 2,
  1404. 'true_formatters': {
  1405. 'form1' : {
  1406. 'format' : '%(levelname)s ++ %(message)s',
  1407. },
  1408. },
  1409. 'handler_configs': {
  1410. 'hand1' : {
  1411. 'class' : 'logging.StreamHandler',
  1412. 'formatter' : 'form1',
  1413. 'level' : 'NOTSET',
  1414. 'stream' : 'ext://sys.stdout',
  1415. },
  1416. },
  1417. 'formatters' : 'cfg://true_formatters',
  1418. 'handlers' : {
  1419. 'hand1' : 'cfg://handler_configs[hand1]',
  1420. },
  1421. 'loggers' : {
  1422. 'compiler.parser' : {
  1423. 'level' : 'DEBUG',
  1424. 'handlers' : ['hand1'],
  1425. },
  1426. },
  1427. 'root' : {
  1428. 'level' : 'WARNING',
  1429. },
  1430. }
  1431. out_of_order = {
  1432. "version": 1,
  1433. "formatters": {
  1434. "mySimpleFormatter": {
  1435. "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s"
  1436. }
  1437. },
  1438. "handlers": {
  1439. "fileGlobal": {
  1440. "class": "logging.StreamHandler",
  1441. "level": "DEBUG",
  1442. "formatter": "mySimpleFormatter"
  1443. },
  1444. "bufferGlobal": {
  1445. "class": "logging.handlers.MemoryHandler",
  1446. "capacity": 5,
  1447. "formatter": "mySimpleFormatter",
  1448. "target": "fileGlobal",
  1449. "level": "DEBUG"
  1450. }
  1451. },
  1452. "loggers": {
  1453. "mymodule": {
  1454. "level": "DEBUG",
  1455. "handlers": ["bufferGlobal"],
  1456. "propagate": "true"
  1457. }
  1458. }
  1459. }
  1460. def apply_config(self, conf):
  1461. logging.config.dictConfig(conf)
  1462. def test_config0_ok(self):
  1463. # A simple config which overrides the default settings.
  1464. with captured_stdout() as output:
  1465. self.apply_config(self.config0)
  1466. logger = logging.getLogger()
  1467. # Won't output anything
  1468. logger.info(self.next_message())
  1469. # Outputs a message
  1470. logger.error(self.next_message())
  1471. self.assert_log_lines([
  1472. ('ERROR', '2'),
  1473. ], stream=output)
  1474. # Original logger output is empty.
  1475. self.assert_log_lines([])
  1476. def test_config1_ok(self, config=config1):
  1477. # A config defining a sub-parser as well.
  1478. with captured_stdout() as output:
  1479. self.apply_config(config)
  1480. logger = logging.getLogger("compiler.parser")
  1481. # Both will output a message
  1482. logger.info(self.next_message())
  1483. logger.error(self.next_message())
  1484. self.assert_log_lines([
  1485. ('INFO', '1'),
  1486. ('ERROR', '2'),
  1487. ], stream=output)
  1488. # Original logger output is empty.
  1489. self.assert_log_lines([])
  1490. def test_config2_failure(self):
  1491. # A simple config which overrides the default settings.
  1492. self.assertRaises(StandardError, self.apply_config, self.config2)
  1493. def test_config2a_failure(self):
  1494. # A simple config which overrides the default settings.
  1495. self.assertRaises(StandardError, self.apply_config, self.config2a)
  1496. def test_config2b_failure(self):
  1497. # A simple config which overrides the default settings.
  1498. self.assertRaises(StandardError, self.apply_config, self.config2b)
  1499. def test_config3_failure(self):
  1500. # A simple config which overrides the default settings.
  1501. self.assertRaises(StandardError, self.apply_config, self.config3)
  1502. def test_config4_ok(self):
  1503. # A config specifying a custom formatter class.
  1504. with captured_stdout() as output:
  1505. self.apply_config(self.config4)
  1506. #logger = logging.getLogger()
  1507. try:
  1508. raise RuntimeError()
  1509. except RuntimeError:
  1510. logging.exception("just testing")
  1511. sys.stdout.seek(0)
  1512. self.assertEqual(output.getvalue(),
  1513. "ERROR:root:just testing\nGot a [RuntimeError]\n")
  1514. # Original logger output is empty
  1515. self.assert_log_lines([])
  1516. def test_config4a_ok(self):
  1517. # A config specifying a custom formatter class.
  1518. with captured_stdout() as output:
  1519. self.apply_config(self.config4a)
  1520. #logger = logging.getLogger()
  1521. try:
  1522. raise RuntimeError()
  1523. except RuntimeError:
  1524. logging.exception("just testing")
  1525. sys.stdout.seek(0)
  1526. self.assertEqual(output.getvalue(),
  1527. "ERROR:root:just testing\nGot a [RuntimeError]\n")
  1528. # Original logger output is empty
  1529. self.assert_log_lines([])
  1530. def test_config5_ok(self):
  1531. self.test_config1_ok(config=self.config5)
  1532. def test_config6_failure(self):
  1533. self.assertRaises(StandardError, self.apply_config, self.config6)
  1534. def test_config7_ok(self):
  1535. with captured_stdout() as output:
  1536. self.apply_config(self.config1)
  1537. logger = logging.getLogger("compiler.parser")
  1538. # Both will output a message
  1539. logger.info(self.next_message())
  1540. logger.error(self.next_message())
  1541. self.assert_log_lines([
  1542. ('INFO', '1'),
  1543. ('ERROR', '2'),
  1544. ], stream=output)
  1545. # Original logger output is empty.
  1546. self.assert_log_lines([])
  1547. with captured_stdout() as output:
  1548. self.apply_config(self.config7)
  1549. logger = logging.getLogger("compiler.parser")
  1550. self.assertTrue(logger.disabled)
  1551. logger = logging.getLogger("compiler.lexer")
  1552. # Both will output a message
  1553. logger.info(self.next_message())
  1554. logger.error(self.next_message())
  1555. self.assert_log_lines([
  1556. ('INFO', '3'),
  1557. ('ERROR', '4'),
  1558. ], stream=output)
  1559. # Original logger output is empty.
  1560. self.assert_log_lines([])
  1561. #Same as test_config_7_ok but don't disable old loggers.
  1562. def test_config_8_ok(self):
  1563. with captured_stdout() as output:
  1564. self.apply_config(self.config1)
  1565. logger = logging.getLogger("compiler.parser")
  1566. # Both will output a message
  1567. logger.info(self.next_message())
  1568. logger.error(self.next_message())
  1569. self.assert_log_lines([
  1570. ('INFO', '1'),
  1571. ('ERROR', '2'),
  1572. ], stream=output)
  1573. # Original logger output is empty.
  1574. self.assert_log_lines([])
  1575. with captured_stdout() as output:
  1576. self.apply_config(self.config8)
  1577. logger = logging.getLogger("compiler.parser")
  1578. self.assertFalse(logger.disabled)
  1579. # Both will output a message
  1580. logger.info(self.next_message())
  1581. logger.error(self.next_message())
  1582. logger = logging.getLogger("compiler.lexer")
  1583. # Both will output a message
  1584. logger.info(self.next_message())
  1585. logger.error(self.next_message())
  1586. self.assert_log_lines([
  1587. ('INFO', '3'),
  1588. ('ERROR', '4'),
  1589. ('INFO', '5'),
  1590. ('ERROR', '6'),
  1591. ], stream=output)
  1592. # Original logger output is empty.
  1593. self.assert_log_lines([])
  1594. def test_config_9_ok(self):
  1595. with captured_stdout() as output:
  1596. self.apply_config(self.config9)
  1597. logger = logging.getLogger("compiler.parser")
  1598. #Nothing will be output since both handler and logger are set to WARNING
  1599. logger.info(self.next_message())
  1600. self.assert_log_lines([], stream=output)
  1601. self.apply_config(self.config9a)
  1602. #Nothing will be output since both handler is still set to WARNING
  1603. logger.info(self.next_message())
  1604. self.assert_log_lines([], stream=output)
  1605. self.apply_config(self.config9b)
  1606. #Message should now be output
  1607. logger.info(self.next_message())
  1608. self.assert_log_lines([
  1609. ('INFO', '3'),
  1610. ], stream=output)
  1611. def test_config_10_ok(self):
  1612. with captured_stdout() as output:
  1613. self.apply_config(self.config10)
  1614. logger = logging.getLogger("compiler.parser")
  1615. logger.warning(self.next_message())
  1616. logger = logging.getLogger('compiler')
  1617. #Not output, because filtered
  1618. logger.warning(self.next_message())
  1619. logger = logging.getLogger('compiler.lexer')
  1620. #Not output, because filtered
  1621. logger.warning(self.next_message())
  1622. logger = logging.getLogger("compiler.parser.codegen")
  1623. #Output, as not filtered
  1624. logger.error(self.next_message())
  1625. self.assert_log_lines([
  1626. ('WARNING', '1'),
  1627. ('ERROR', '4'),
  1628. ], stream=output)
  1629. def test_config11_ok(self):
  1630. self.test_config1_ok(self.config11)
  1631. def test_config12_failure(self):
  1632. self.assertRaises(StandardError, self.apply_config, self.config12)
  1633. def test_config13_failure(self):
  1634. self.assertRaises(StandardError, self.apply_config, self.config13)
  1635. @unittest.skipUnless(threading, 'listen() needs threading to work')
  1636. def setup_via_listener(self, text):
  1637. # Ask for a randomly assigned port (by using port 0)
  1638. t = logging.config.listen(0)
  1639. t.start()
  1640. t.ready.wait()
  1641. # Now get the port allocated
  1642. port = t.port
  1643. t.ready.clear()
  1644. try:
  1645. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  1646. sock.settimeout(2.0)
  1647. sock.connect(('localhost', port))
  1648. slen = struct.pack('>L', len(text))
  1649. s = slen + text
  1650. sentsofar = 0
  1651. left = len(s)
  1652. while left > 0:
  1653. sent = sock.send(s[sentsofar:])
  1654. sentsofar += sent
  1655. left -= sent
  1656. sock.close()
  1657. finally:
  1658. t.ready.wait(2.0)
  1659. logging.config.stopListening()
  1660. t.join(2.0)
  1661. def test_listen_config_10_ok(self):
  1662. with captured_stdout() as output:
  1663. self.setup_via_listener(json.dumps(self.config10))
  1664. logger = logging.getLogger("compiler.parser")
  1665. logger.warning(self.next_message())
  1666. logger = logging.getLogger('compiler')
  1667. #Not output, because filtered
  1668. logger.warning(self.next_message())
  1669. logger = logging.getLogger('compiler.lexer')
  1670. #Not output, because filtered
  1671. logger.warning(self.next_message())
  1672. logger = logging.getLogger("compiler.parser.codegen")
  1673. #Output, as not filtered
  1674. logger.error(self.next_message())
  1675. self.assert_log_lines([
  1676. ('WARNING', '1'),
  1677. ('ERROR', '4'),
  1678. ], stream=output)
  1679. def test_listen_config_1_ok(self):
  1680. with captured_stdout() as output:
  1681. self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
  1682. logger = logging.getLogger("compiler.parser")
  1683. # Both will output a message
  1684. logger.info(self.next_message())
  1685. logger.error(self.next_message())
  1686. self.assert_log_lines([
  1687. ('INFO', '1'),
  1688. ('ERROR', '2'),
  1689. ], stream=output)
  1690. # Original logger output is empty.
  1691. self.assert_log_lines([])
  1692. def test_out_of_order(self):
  1693. self.apply_config(self.out_of_order)
  1694. handler = logging.getLogger('mymodule').handlers[0]
  1695. self.assertIsInstance(handler.target, logging.Handler)
  1696. class ManagerTest(BaseTest):
  1697. def test_manager_loggerclass(self):
  1698. logged = []
  1699. class MyLogger(logging.Logger):
  1700. def _log(self, level, msg, args, exc_info=None, extra=None):
  1701. logged.append(msg)
  1702. man = logging.Manager(None)
  1703. self.assertRaises(TypeError, man.setLoggerClass, int)
  1704. man.setLoggerClass(MyLogger)
  1705. logger = man.getLogger('test')
  1706. logger.warning('should appear in logged')
  1707. logging.warning('should not appear in logged')
  1708. self.assertEqual(logged, ['should appear in logged'])
  1709. class ChildLoggerTest(BaseTest):
  1710. def test_child_loggers(self):
  1711. r = logging.getLogger()
  1712. l1 = logging.getLogger('abc')
  1713. l2 = logging.getLogger('def.ghi')
  1714. c1 = r.getChild('xyz')
  1715. c2 = r.getChild('uvw.xyz')
  1716. self.assertTrue(c1 is logging.getLogger('xyz'))
  1717. self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
  1718. c1 = l1.getChild('def')
  1719. c2 = c1.getChild('ghi')
  1720. c3 = l1.getChild('def.ghi')
  1721. self.assertTrue(c1 is logging.getLogger('abc.def'))
  1722. self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
  1723. self.assertTrue(c2 is c3)
  1724. class HandlerTest(BaseTest):
  1725. @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
  1726. @unittest.skipUnless(threading, 'Threading required for this test.')
  1727. def test_race(self):
  1728. # Issue #14632 refers.
  1729. def remove_loop(fname, tries):
  1730. for _ in range(tries):
  1731. try:
  1732. os.unlink(fname)
  1733. except OSError:
  1734. pass
  1735. time.sleep(0.004 * random.randint(0, 4))
  1736. del_count = 500
  1737. log_count = 500
  1738. for delay in (False, True):
  1739. fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
  1740. os.close(fd)
  1741. remover = threading.Thread(target=remove_loop, args=(fn, del_count))
  1742. remover.daemon = True
  1743. remover.start()
  1744. h = logging.handlers.WatchedFileHandler(fn, delay=delay)
  1745. f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
  1746. h.setFormatter(f)
  1747. try:
  1748. for _ in range(log_count):
  1749. time.sleep(0.005)
  1750. r = logging.makeLogRecord({'msg': 'testing' })
  1751. h.handle(r)
  1752. finally:
  1753. remover.join()
  1754. try:
  1755. h.close()
  1756. except ValueError:
  1757. pass
  1758. if os.path.exists(fn):
  1759. os.unlink(fn)
  1760. # Set the locale to the platform-dependent default. I have no idea
  1761. # why the test does this, but in any case we save the current locale
  1762. # first and restore it at the end.
  1763. @run_with_locale('LC_ALL', '')
  1764. def test_main():
  1765. run_unittest(BuiltinLevelsTest, BasicFilterTest,
  1766. CustomLevelsAndFiltersTest, MemoryHandlerTest,
  1767. ConfigFileTest, SocketHandlerTest, MemoryTest,
  1768. EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
  1769. ChildLoggerTest, HandlerTest)
  1770. if __name__ == "__main__":
  1771. test_main()