test_logging.py 200 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698
  1. # Copyright 2001-2022 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-2022 Vinay Sajip. All Rights Reserved.
  18. """
  19. import logging
  20. import logging.handlers
  21. import logging.config
  22. import codecs
  23. import configparser
  24. import copy
  25. import datetime
  26. import pathlib
  27. import pickle
  28. import io
  29. import gc
  30. import json
  31. import os
  32. import queue
  33. import random
  34. import re
  35. import shutil
  36. import socket
  37. import struct
  38. import sys
  39. import tempfile
  40. from test.support.script_helper import assert_python_ok, assert_python_failure
  41. from test import support
  42. from test.support import os_helper
  43. from test.support import socket_helper
  44. from test.support import threading_helper
  45. from test.support import warnings_helper
  46. from test.support.logging_helper import TestHandler
  47. import textwrap
  48. import threading
  49. import time
  50. import unittest
  51. import warnings
  52. import weakref
  53. from http.server import HTTPServer, BaseHTTPRequestHandler
  54. from urllib.parse import urlparse, parse_qs
  55. from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
  56. ThreadingTCPServer, StreamRequestHandler)
  57. asyncore = warnings_helper.import_deprecated('asyncore')
  58. smtpd = warnings_helper.import_deprecated('smtpd')
  59. try:
  60. import win32evtlog, win32evtlogutil, pywintypes
  61. except ImportError:
  62. win32evtlog = win32evtlogutil = pywintypes = None
  63. try:
  64. import zlib
  65. except ImportError:
  66. pass
  67. class BaseTest(unittest.TestCase):
  68. """Base class for logging tests."""
  69. log_format = "%(name)s -> %(levelname)s: %(message)s"
  70. expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
  71. message_num = 0
  72. def setUp(self):
  73. """Setup the default logging stream to an internal StringIO instance,
  74. so that we can examine log output as we want."""
  75. self._threading_key = threading_helper.threading_setup()
  76. logger_dict = logging.getLogger().manager.loggerDict
  77. logging._acquireLock()
  78. try:
  79. self.saved_handlers = logging._handlers.copy()
  80. self.saved_handler_list = logging._handlerList[:]
  81. self.saved_loggers = saved_loggers = logger_dict.copy()
  82. self.saved_name_to_level = logging._nameToLevel.copy()
  83. self.saved_level_to_name = logging._levelToName.copy()
  84. self.logger_states = logger_states = {}
  85. for name in saved_loggers:
  86. logger_states[name] = getattr(saved_loggers[name],
  87. 'disabled', None)
  88. finally:
  89. logging._releaseLock()
  90. # Set two unused loggers
  91. self.logger1 = logging.getLogger("\xab\xd7\xbb")
  92. self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
  93. self.root_logger = logging.getLogger("")
  94. self.original_logging_level = self.root_logger.getEffectiveLevel()
  95. self.stream = io.StringIO()
  96. self.root_logger.setLevel(logging.DEBUG)
  97. self.root_hdlr = logging.StreamHandler(self.stream)
  98. self.root_formatter = logging.Formatter(self.log_format)
  99. self.root_hdlr.setFormatter(self.root_formatter)
  100. if self.logger1.hasHandlers():
  101. hlist = self.logger1.handlers + self.root_logger.handlers
  102. raise AssertionError('Unexpected handlers: %s' % hlist)
  103. if self.logger2.hasHandlers():
  104. hlist = self.logger2.handlers + self.root_logger.handlers
  105. raise AssertionError('Unexpected handlers: %s' % hlist)
  106. self.root_logger.addHandler(self.root_hdlr)
  107. self.assertTrue(self.logger1.hasHandlers())
  108. self.assertTrue(self.logger2.hasHandlers())
  109. def tearDown(self):
  110. """Remove our logging stream, and restore the original logging
  111. level."""
  112. self.stream.close()
  113. self.root_logger.removeHandler(self.root_hdlr)
  114. while self.root_logger.handlers:
  115. h = self.root_logger.handlers[0]
  116. self.root_logger.removeHandler(h)
  117. h.close()
  118. self.root_logger.setLevel(self.original_logging_level)
  119. logging._acquireLock()
  120. try:
  121. logging._levelToName.clear()
  122. logging._levelToName.update(self.saved_level_to_name)
  123. logging._nameToLevel.clear()
  124. logging._nameToLevel.update(self.saved_name_to_level)
  125. logging._handlers.clear()
  126. logging._handlers.update(self.saved_handlers)
  127. logging._handlerList[:] = self.saved_handler_list
  128. manager = logging.getLogger().manager
  129. manager.disable = 0
  130. loggerDict = manager.loggerDict
  131. loggerDict.clear()
  132. loggerDict.update(self.saved_loggers)
  133. logger_states = self.logger_states
  134. for name in self.logger_states:
  135. if logger_states[name] is not None:
  136. self.saved_loggers[name].disabled = logger_states[name]
  137. finally:
  138. logging._releaseLock()
  139. self.doCleanups()
  140. threading_helper.threading_cleanup(*self._threading_key)
  141. def assert_log_lines(self, expected_values, stream=None, pat=None):
  142. """Match the collected log lines against the regular expression
  143. self.expected_log_pat, and compare the extracted group values to
  144. the expected_values list of tuples."""
  145. stream = stream or self.stream
  146. pat = re.compile(pat or self.expected_log_pat)
  147. actual_lines = stream.getvalue().splitlines()
  148. self.assertEqual(len(actual_lines), len(expected_values))
  149. for actual, expected in zip(actual_lines, expected_values):
  150. match = pat.search(actual)
  151. if not match:
  152. self.fail("Log line does not match expected pattern:\n" +
  153. actual)
  154. self.assertEqual(tuple(match.groups()), expected)
  155. s = stream.read()
  156. if s:
  157. self.fail("Remaining output at end of log stream:\n" + s)
  158. def next_message(self):
  159. """Generate a message consisting solely of an auto-incrementing
  160. integer."""
  161. self.message_num += 1
  162. return "%d" % self.message_num
  163. class BuiltinLevelsTest(BaseTest):
  164. """Test builtin levels and their inheritance."""
  165. def test_flat(self):
  166. # Logging levels in a flat logger namespace.
  167. m = self.next_message
  168. ERR = logging.getLogger("ERR")
  169. ERR.setLevel(logging.ERROR)
  170. INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
  171. INF.setLevel(logging.INFO)
  172. DEB = logging.getLogger("DEB")
  173. DEB.setLevel(logging.DEBUG)
  174. # These should log.
  175. ERR.log(logging.CRITICAL, m())
  176. ERR.error(m())
  177. INF.log(logging.CRITICAL, m())
  178. INF.error(m())
  179. INF.warning(m())
  180. INF.info(m())
  181. DEB.log(logging.CRITICAL, m())
  182. DEB.error(m())
  183. DEB.warning(m())
  184. DEB.info(m())
  185. DEB.debug(m())
  186. # These should not log.
  187. ERR.warning(m())
  188. ERR.info(m())
  189. ERR.debug(m())
  190. INF.debug(m())
  191. self.assert_log_lines([
  192. ('ERR', 'CRITICAL', '1'),
  193. ('ERR', 'ERROR', '2'),
  194. ('INF', 'CRITICAL', '3'),
  195. ('INF', 'ERROR', '4'),
  196. ('INF', 'WARNING', '5'),
  197. ('INF', 'INFO', '6'),
  198. ('DEB', 'CRITICAL', '7'),
  199. ('DEB', 'ERROR', '8'),
  200. ('DEB', 'WARNING', '9'),
  201. ('DEB', 'INFO', '10'),
  202. ('DEB', 'DEBUG', '11'),
  203. ])
  204. def test_nested_explicit(self):
  205. # Logging levels in a nested namespace, all explicitly set.
  206. m = self.next_message
  207. INF = logging.getLogger("INF")
  208. INF.setLevel(logging.INFO)
  209. INF_ERR = logging.getLogger("INF.ERR")
  210. INF_ERR.setLevel(logging.ERROR)
  211. # These should log.
  212. INF_ERR.log(logging.CRITICAL, m())
  213. INF_ERR.error(m())
  214. # These should not log.
  215. INF_ERR.warning(m())
  216. INF_ERR.info(m())
  217. INF_ERR.debug(m())
  218. self.assert_log_lines([
  219. ('INF.ERR', 'CRITICAL', '1'),
  220. ('INF.ERR', 'ERROR', '2'),
  221. ])
  222. def test_nested_inherited(self):
  223. # Logging levels in a nested namespace, inherited from parent loggers.
  224. m = self.next_message
  225. INF = logging.getLogger("INF")
  226. INF.setLevel(logging.INFO)
  227. INF_ERR = logging.getLogger("INF.ERR")
  228. INF_ERR.setLevel(logging.ERROR)
  229. INF_UNDEF = logging.getLogger("INF.UNDEF")
  230. INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
  231. UNDEF = logging.getLogger("UNDEF")
  232. # These should log.
  233. INF_UNDEF.log(logging.CRITICAL, m())
  234. INF_UNDEF.error(m())
  235. INF_UNDEF.warning(m())
  236. INF_UNDEF.info(m())
  237. INF_ERR_UNDEF.log(logging.CRITICAL, m())
  238. INF_ERR_UNDEF.error(m())
  239. # These should not log.
  240. INF_UNDEF.debug(m())
  241. INF_ERR_UNDEF.warning(m())
  242. INF_ERR_UNDEF.info(m())
  243. INF_ERR_UNDEF.debug(m())
  244. self.assert_log_lines([
  245. ('INF.UNDEF', 'CRITICAL', '1'),
  246. ('INF.UNDEF', 'ERROR', '2'),
  247. ('INF.UNDEF', 'WARNING', '3'),
  248. ('INF.UNDEF', 'INFO', '4'),
  249. ('INF.ERR.UNDEF', 'CRITICAL', '5'),
  250. ('INF.ERR.UNDEF', 'ERROR', '6'),
  251. ])
  252. def test_nested_with_virtual_parent(self):
  253. # Logging levels when some parent does not exist yet.
  254. m = self.next_message
  255. INF = logging.getLogger("INF")
  256. GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
  257. CHILD = logging.getLogger("INF.BADPARENT")
  258. INF.setLevel(logging.INFO)
  259. # These should log.
  260. GRANDCHILD.log(logging.FATAL, m())
  261. GRANDCHILD.info(m())
  262. CHILD.log(logging.FATAL, m())
  263. CHILD.info(m())
  264. # These should not log.
  265. GRANDCHILD.debug(m())
  266. CHILD.debug(m())
  267. self.assert_log_lines([
  268. ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
  269. ('INF.BADPARENT.UNDEF', 'INFO', '2'),
  270. ('INF.BADPARENT', 'CRITICAL', '3'),
  271. ('INF.BADPARENT', 'INFO', '4'),
  272. ])
  273. def test_regression_22386(self):
  274. """See issue #22386 for more information."""
  275. self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
  276. self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
  277. def test_issue27935(self):
  278. fatal = logging.getLevelName('FATAL')
  279. self.assertEqual(fatal, logging.FATAL)
  280. def test_regression_29220(self):
  281. """See issue #29220 for more information."""
  282. logging.addLevelName(logging.INFO, '')
  283. self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
  284. self.assertEqual(logging.getLevelName(logging.INFO), '')
  285. self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
  286. self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
  287. class BasicFilterTest(BaseTest):
  288. """Test the bundled Filter class."""
  289. def test_filter(self):
  290. # Only messages satisfying the specified criteria pass through the
  291. # filter.
  292. filter_ = logging.Filter("spam.eggs")
  293. handler = self.root_logger.handlers[0]
  294. try:
  295. handler.addFilter(filter_)
  296. spam = logging.getLogger("spam")
  297. spam_eggs = logging.getLogger("spam.eggs")
  298. spam_eggs_fish = logging.getLogger("spam.eggs.fish")
  299. spam_bakedbeans = logging.getLogger("spam.bakedbeans")
  300. spam.info(self.next_message())
  301. spam_eggs.info(self.next_message()) # Good.
  302. spam_eggs_fish.info(self.next_message()) # Good.
  303. spam_bakedbeans.info(self.next_message())
  304. self.assert_log_lines([
  305. ('spam.eggs', 'INFO', '2'),
  306. ('spam.eggs.fish', 'INFO', '3'),
  307. ])
  308. finally:
  309. handler.removeFilter(filter_)
  310. def test_callable_filter(self):
  311. # Only messages satisfying the specified criteria pass through the
  312. # filter.
  313. def filterfunc(record):
  314. parts = record.name.split('.')
  315. prefix = '.'.join(parts[:2])
  316. return prefix == 'spam.eggs'
  317. handler = self.root_logger.handlers[0]
  318. try:
  319. handler.addFilter(filterfunc)
  320. spam = logging.getLogger("spam")
  321. spam_eggs = logging.getLogger("spam.eggs")
  322. spam_eggs_fish = logging.getLogger("spam.eggs.fish")
  323. spam_bakedbeans = logging.getLogger("spam.bakedbeans")
  324. spam.info(self.next_message())
  325. spam_eggs.info(self.next_message()) # Good.
  326. spam_eggs_fish.info(self.next_message()) # Good.
  327. spam_bakedbeans.info(self.next_message())
  328. self.assert_log_lines([
  329. ('spam.eggs', 'INFO', '2'),
  330. ('spam.eggs.fish', 'INFO', '3'),
  331. ])
  332. finally:
  333. handler.removeFilter(filterfunc)
  334. def test_empty_filter(self):
  335. f = logging.Filter()
  336. r = logging.makeLogRecord({'name': 'spam.eggs'})
  337. self.assertTrue(f.filter(r))
  338. #
  339. # First, we define our levels. There can be as many as you want - the only
  340. # limitations are that they should be integers, the lowest should be > 0 and
  341. # larger values mean less information being logged. If you need specific
  342. # level values which do not fit into these limitations, you can use a
  343. # mapping dictionary to convert between your application levels and the
  344. # logging system.
  345. #
  346. SILENT = 120
  347. TACITURN = 119
  348. TERSE = 118
  349. EFFUSIVE = 117
  350. SOCIABLE = 116
  351. VERBOSE = 115
  352. TALKATIVE = 114
  353. GARRULOUS = 113
  354. CHATTERBOX = 112
  355. BORING = 111
  356. LEVEL_RANGE = range(BORING, SILENT + 1)
  357. #
  358. # Next, we define names for our levels. You don't need to do this - in which
  359. # case the system will use "Level n" to denote the text for the level.
  360. #
  361. my_logging_levels = {
  362. SILENT : 'Silent',
  363. TACITURN : 'Taciturn',
  364. TERSE : 'Terse',
  365. EFFUSIVE : 'Effusive',
  366. SOCIABLE : 'Sociable',
  367. VERBOSE : 'Verbose',
  368. TALKATIVE : 'Talkative',
  369. GARRULOUS : 'Garrulous',
  370. CHATTERBOX : 'Chatterbox',
  371. BORING : 'Boring',
  372. }
  373. class GarrulousFilter(logging.Filter):
  374. """A filter which blocks garrulous messages."""
  375. def filter(self, record):
  376. return record.levelno != GARRULOUS
  377. class VerySpecificFilter(logging.Filter):
  378. """A filter which blocks sociable and taciturn messages."""
  379. def filter(self, record):
  380. return record.levelno not in [SOCIABLE, TACITURN]
  381. class CustomLevelsAndFiltersTest(BaseTest):
  382. """Test various filtering possibilities with custom logging levels."""
  383. # Skip the logger name group.
  384. expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
  385. def setUp(self):
  386. BaseTest.setUp(self)
  387. for k, v in my_logging_levels.items():
  388. logging.addLevelName(k, v)
  389. def log_at_all_levels(self, logger):
  390. for lvl in LEVEL_RANGE:
  391. logger.log(lvl, self.next_message())
  392. def test_logger_filter(self):
  393. # Filter at logger level.
  394. self.root_logger.setLevel(VERBOSE)
  395. # Levels >= 'Verbose' are good.
  396. self.log_at_all_levels(self.root_logger)
  397. self.assert_log_lines([
  398. ('Verbose', '5'),
  399. ('Sociable', '6'),
  400. ('Effusive', '7'),
  401. ('Terse', '8'),
  402. ('Taciturn', '9'),
  403. ('Silent', '10'),
  404. ])
  405. def test_handler_filter(self):
  406. # Filter at handler level.
  407. self.root_logger.handlers[0].setLevel(SOCIABLE)
  408. try:
  409. # Levels >= 'Sociable' are good.
  410. self.log_at_all_levels(self.root_logger)
  411. self.assert_log_lines([
  412. ('Sociable', '6'),
  413. ('Effusive', '7'),
  414. ('Terse', '8'),
  415. ('Taciturn', '9'),
  416. ('Silent', '10'),
  417. ])
  418. finally:
  419. self.root_logger.handlers[0].setLevel(logging.NOTSET)
  420. def test_specific_filters(self):
  421. # Set a specific filter object on the handler, and then add another
  422. # filter object on the logger itself.
  423. handler = self.root_logger.handlers[0]
  424. specific_filter = None
  425. garr = GarrulousFilter()
  426. handler.addFilter(garr)
  427. try:
  428. self.log_at_all_levels(self.root_logger)
  429. first_lines = [
  430. # Notice how 'Garrulous' is missing
  431. ('Boring', '1'),
  432. ('Chatterbox', '2'),
  433. ('Talkative', '4'),
  434. ('Verbose', '5'),
  435. ('Sociable', '6'),
  436. ('Effusive', '7'),
  437. ('Terse', '8'),
  438. ('Taciturn', '9'),
  439. ('Silent', '10'),
  440. ]
  441. self.assert_log_lines(first_lines)
  442. specific_filter = VerySpecificFilter()
  443. self.root_logger.addFilter(specific_filter)
  444. self.log_at_all_levels(self.root_logger)
  445. self.assert_log_lines(first_lines + [
  446. # Not only 'Garrulous' is still missing, but also 'Sociable'
  447. # and 'Taciturn'
  448. ('Boring', '11'),
  449. ('Chatterbox', '12'),
  450. ('Talkative', '14'),
  451. ('Verbose', '15'),
  452. ('Effusive', '17'),
  453. ('Terse', '18'),
  454. ('Silent', '20'),
  455. ])
  456. finally:
  457. if specific_filter:
  458. self.root_logger.removeFilter(specific_filter)
  459. handler.removeFilter(garr)
  460. class HandlerTest(BaseTest):
  461. def test_name(self):
  462. h = logging.Handler()
  463. h.name = 'generic'
  464. self.assertEqual(h.name, 'generic')
  465. h.name = 'anothergeneric'
  466. self.assertEqual(h.name, 'anothergeneric')
  467. self.assertRaises(NotImplementedError, h.emit, None)
  468. def test_builtin_handlers(self):
  469. # We can't actually *use* too many handlers in the tests,
  470. # but we can try instantiating them with various options
  471. if sys.platform in ('linux', 'darwin'):
  472. for existing in (True, False):
  473. fd, fn = tempfile.mkstemp()
  474. os.close(fd)
  475. if not existing:
  476. os.unlink(fn)
  477. h = logging.handlers.WatchedFileHandler(fn, encoding='utf-8', delay=True)
  478. if existing:
  479. dev, ino = h.dev, h.ino
  480. self.assertEqual(dev, -1)
  481. self.assertEqual(ino, -1)
  482. r = logging.makeLogRecord({'msg': 'Test'})
  483. h.handle(r)
  484. # Now remove the file.
  485. os.unlink(fn)
  486. self.assertFalse(os.path.exists(fn))
  487. # The next call should recreate the file.
  488. h.handle(r)
  489. self.assertTrue(os.path.exists(fn))
  490. else:
  491. self.assertEqual(h.dev, -1)
  492. self.assertEqual(h.ino, -1)
  493. h.close()
  494. if existing:
  495. os.unlink(fn)
  496. if sys.platform == 'darwin':
  497. sockname = '/var/run/syslog'
  498. else:
  499. sockname = '/dev/log'
  500. try:
  501. h = logging.handlers.SysLogHandler(sockname)
  502. self.assertEqual(h.facility, h.LOG_USER)
  503. self.assertTrue(h.unixsocket)
  504. h.close()
  505. except OSError: # syslogd might not be available
  506. pass
  507. for method in ('GET', 'POST', 'PUT'):
  508. if method == 'PUT':
  509. self.assertRaises(ValueError, logging.handlers.HTTPHandler,
  510. 'localhost', '/log', method)
  511. else:
  512. h = logging.handlers.HTTPHandler('localhost', '/log', method)
  513. h.close()
  514. h = logging.handlers.BufferingHandler(0)
  515. r = logging.makeLogRecord({})
  516. self.assertTrue(h.shouldFlush(r))
  517. h.close()
  518. h = logging.handlers.BufferingHandler(1)
  519. self.assertFalse(h.shouldFlush(r))
  520. h.close()
  521. def test_path_objects(self):
  522. """
  523. Test that Path objects are accepted as filename arguments to handlers.
  524. See Issue #27493.
  525. """
  526. fd, fn = tempfile.mkstemp()
  527. os.close(fd)
  528. os.unlink(fn)
  529. pfn = pathlib.Path(fn)
  530. cases = (
  531. (logging.FileHandler, (pfn, 'w')),
  532. (logging.handlers.RotatingFileHandler, (pfn, 'a')),
  533. (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
  534. )
  535. if sys.platform in ('linux', 'darwin'):
  536. cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
  537. for cls, args in cases:
  538. h = cls(*args, encoding="utf-8")
  539. self.assertTrue(os.path.exists(fn))
  540. h.close()
  541. os.unlink(fn)
  542. @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
  543. @unittest.skipIf(
  544. support.is_emscripten, "Emscripten cannot fstat unlinked files."
  545. )
  546. @threading_helper.requires_working_threading()
  547. def test_race(self):
  548. # Issue #14632 refers.
  549. def remove_loop(fname, tries):
  550. for _ in range(tries):
  551. try:
  552. os.unlink(fname)
  553. self.deletion_time = time.time()
  554. except OSError:
  555. pass
  556. time.sleep(0.004 * random.randint(0, 4))
  557. del_count = 500
  558. log_count = 500
  559. self.handle_time = None
  560. self.deletion_time = None
  561. for delay in (False, True):
  562. fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
  563. os.close(fd)
  564. remover = threading.Thread(target=remove_loop, args=(fn, del_count))
  565. remover.daemon = True
  566. remover.start()
  567. h = logging.handlers.WatchedFileHandler(fn, encoding='utf-8', delay=delay)
  568. f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
  569. h.setFormatter(f)
  570. try:
  571. for _ in range(log_count):
  572. time.sleep(0.005)
  573. r = logging.makeLogRecord({'msg': 'testing' })
  574. try:
  575. self.handle_time = time.time()
  576. h.handle(r)
  577. except Exception:
  578. print('Deleted at %s, '
  579. 'opened at %s' % (self.deletion_time,
  580. self.handle_time))
  581. raise
  582. finally:
  583. remover.join()
  584. h.close()
  585. if os.path.exists(fn):
  586. os.unlink(fn)
  587. # The implementation relies on os.register_at_fork existing, but we test
  588. # based on os.fork existing because that is what users and this test use.
  589. # This helps ensure that when fork exists (the important concept) that the
  590. # register_at_fork mechanism is also present and used.
  591. @support.requires_fork()
  592. @threading_helper.requires_working_threading()
  593. def test_post_fork_child_no_deadlock(self):
  594. """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
  595. class _OurHandler(logging.Handler):
  596. def __init__(self):
  597. super().__init__()
  598. self.sub_handler = logging.StreamHandler(
  599. stream=open('/dev/null', 'wt', encoding='utf-8'))
  600. def emit(self, record):
  601. self.sub_handler.acquire()
  602. try:
  603. self.sub_handler.emit(record)
  604. finally:
  605. self.sub_handler.release()
  606. self.assertEqual(len(logging._handlers), 0)
  607. refed_h = _OurHandler()
  608. self.addCleanup(refed_h.sub_handler.stream.close)
  609. refed_h.name = 'because we need at least one for this test'
  610. self.assertGreater(len(logging._handlers), 0)
  611. self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
  612. test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
  613. test_logger.addHandler(refed_h)
  614. test_logger.setLevel(logging.DEBUG)
  615. locks_held__ready_to_fork = threading.Event()
  616. fork_happened__release_locks_and_end_thread = threading.Event()
  617. def lock_holder_thread_fn():
  618. logging._acquireLock()
  619. try:
  620. refed_h.acquire()
  621. try:
  622. # Tell the main thread to do the fork.
  623. locks_held__ready_to_fork.set()
  624. # If the deadlock bug exists, the fork will happen
  625. # without dealing with the locks we hold, deadlocking
  626. # the child.
  627. # Wait for a successful fork or an unreasonable amount of
  628. # time before releasing our locks. To avoid a timing based
  629. # test we'd need communication from os.fork() as to when it
  630. # has actually happened. Given this is a regression test
  631. # for a fixed issue, potentially less reliably detecting
  632. # regression via timing is acceptable for simplicity.
  633. # The test will always take at least this long. :(
  634. fork_happened__release_locks_and_end_thread.wait(0.5)
  635. finally:
  636. refed_h.release()
  637. finally:
  638. logging._releaseLock()
  639. lock_holder_thread = threading.Thread(
  640. target=lock_holder_thread_fn,
  641. name='test_post_fork_child_no_deadlock lock holder')
  642. lock_holder_thread.start()
  643. locks_held__ready_to_fork.wait()
  644. pid = os.fork()
  645. if pid == 0:
  646. # Child process
  647. try:
  648. test_logger.info(r'Child process did not deadlock. \o/')
  649. finally:
  650. os._exit(0)
  651. else:
  652. # Parent process
  653. test_logger.info(r'Parent process returned from fork. \o/')
  654. fork_happened__release_locks_and_end_thread.set()
  655. lock_holder_thread.join()
  656. support.wait_process(pid, exitcode=0)
  657. class BadStream(object):
  658. def write(self, data):
  659. raise RuntimeError('deliberate mistake')
  660. class TestStreamHandler(logging.StreamHandler):
  661. def handleError(self, record):
  662. self.error_record = record
  663. class StreamWithIntName(object):
  664. level = logging.NOTSET
  665. name = 2
  666. class StreamHandlerTest(BaseTest):
  667. def test_error_handling(self):
  668. h = TestStreamHandler(BadStream())
  669. r = logging.makeLogRecord({})
  670. old_raise = logging.raiseExceptions
  671. try:
  672. h.handle(r)
  673. self.assertIs(h.error_record, r)
  674. h = logging.StreamHandler(BadStream())
  675. with support.captured_stderr() as stderr:
  676. h.handle(r)
  677. msg = '\nRuntimeError: deliberate mistake\n'
  678. self.assertIn(msg, stderr.getvalue())
  679. logging.raiseExceptions = False
  680. with support.captured_stderr() as stderr:
  681. h.handle(r)
  682. self.assertEqual('', stderr.getvalue())
  683. finally:
  684. logging.raiseExceptions = old_raise
  685. def test_stream_setting(self):
  686. """
  687. Test setting the handler's stream
  688. """
  689. h = logging.StreamHandler()
  690. stream = io.StringIO()
  691. old = h.setStream(stream)
  692. self.assertIs(old, sys.stderr)
  693. actual = h.setStream(old)
  694. self.assertIs(actual, stream)
  695. # test that setting to existing value returns None
  696. actual = h.setStream(old)
  697. self.assertIsNone(actual)
  698. def test_can_represent_stream_with_int_name(self):
  699. h = logging.StreamHandler(StreamWithIntName())
  700. self.assertEqual(repr(h), '<StreamHandler 2 (NOTSET)>')
  701. # -- The following section could be moved into a server_helper.py module
  702. # -- if it proves to be of wider utility than just test_logging
  703. class TestSMTPServer(smtpd.SMTPServer):
  704. """
  705. This class implements a test SMTP server.
  706. :param addr: A (host, port) tuple which the server listens on.
  707. You can specify a port value of zero: the server's
  708. *port* attribute will hold the actual port number
  709. used, which can be used in client connections.
  710. :param handler: A callable which will be called to process
  711. incoming messages. The handler will be passed
  712. the client address tuple, who the message is from,
  713. a list of recipients and the message data.
  714. :param poll_interval: The interval, in seconds, used in the underlying
  715. :func:`select` or :func:`poll` call by
  716. :func:`asyncore.loop`.
  717. :param sockmap: A dictionary which will be used to hold
  718. :class:`asyncore.dispatcher` instances used by
  719. :func:`asyncore.loop`. This avoids changing the
  720. :mod:`asyncore` module's global state.
  721. """
  722. def __init__(self, addr, handler, poll_interval, sockmap):
  723. smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
  724. decode_data=True)
  725. self.port = self.socket.getsockname()[1]
  726. self._handler = handler
  727. self._thread = None
  728. self._quit = False
  729. self.poll_interval = poll_interval
  730. def process_message(self, peer, mailfrom, rcpttos, data):
  731. """
  732. Delegates to the handler passed in to the server's constructor.
  733. Typically, this will be a test case method.
  734. :param peer: The client (host, port) tuple.
  735. :param mailfrom: The address of the sender.
  736. :param rcpttos: The addresses of the recipients.
  737. :param data: The message.
  738. """
  739. self._handler(peer, mailfrom, rcpttos, data)
  740. def start(self):
  741. """
  742. Start the server running on a separate daemon thread.
  743. """
  744. self._thread = t = threading.Thread(target=self.serve_forever,
  745. args=(self.poll_interval,))
  746. t.daemon = True
  747. t.start()
  748. def serve_forever(self, poll_interval):
  749. """
  750. Run the :mod:`asyncore` loop until normal termination
  751. conditions arise.
  752. :param poll_interval: The interval, in seconds, used in the underlying
  753. :func:`select` or :func:`poll` call by
  754. :func:`asyncore.loop`.
  755. """
  756. while not self._quit:
  757. asyncore.loop(poll_interval, map=self._map, count=1)
  758. def stop(self):
  759. """
  760. Stop the thread by closing the server instance.
  761. Wait for the server thread to terminate.
  762. """
  763. self._quit = True
  764. threading_helper.join_thread(self._thread)
  765. self._thread = None
  766. self.close()
  767. asyncore.close_all(map=self._map, ignore_all=True)
  768. class ControlMixin(object):
  769. """
  770. This mixin is used to start a server on a separate thread, and
  771. shut it down programmatically. Request handling is simplified - instead
  772. of needing to derive a suitable RequestHandler subclass, you just
  773. provide a callable which will be passed each received request to be
  774. processed.
  775. :param handler: A handler callable which will be called with a
  776. single parameter - the request - in order to
  777. process the request. This handler is called on the
  778. server thread, effectively meaning that requests are
  779. processed serially. While not quite web scale ;-),
  780. this should be fine for testing applications.
  781. :param poll_interval: The polling interval in seconds.
  782. """
  783. def __init__(self, handler, poll_interval):
  784. self._thread = None
  785. self.poll_interval = poll_interval
  786. self._handler = handler
  787. self.ready = threading.Event()
  788. def start(self):
  789. """
  790. Create a daemon thread to run the server, and start it.
  791. """
  792. self._thread = t = threading.Thread(target=self.serve_forever,
  793. args=(self.poll_interval,))
  794. t.daemon = True
  795. t.start()
  796. def serve_forever(self, poll_interval):
  797. """
  798. Run the server. Set the ready flag before entering the
  799. service loop.
  800. """
  801. self.ready.set()
  802. super(ControlMixin, self).serve_forever(poll_interval)
  803. def stop(self):
  804. """
  805. Tell the server thread to stop, and wait for it to do so.
  806. """
  807. self.shutdown()
  808. if self._thread is not None:
  809. threading_helper.join_thread(self._thread)
  810. self._thread = None
  811. self.server_close()
  812. self.ready.clear()
  813. class TestHTTPServer(ControlMixin, HTTPServer):
  814. """
  815. An HTTP server which is controllable using :class:`ControlMixin`.
  816. :param addr: A tuple with the IP address and port to listen on.
  817. :param handler: A handler callable which will be called with a
  818. single parameter - the request - in order to
  819. process the request.
  820. :param poll_interval: The polling interval in seconds.
  821. :param log: Pass ``True`` to enable log messages.
  822. """
  823. def __init__(self, addr, handler, poll_interval=0.5,
  824. log=False, sslctx=None):
  825. class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
  826. def __getattr__(self, name, default=None):
  827. if name.startswith('do_'):
  828. return self.process_request
  829. raise AttributeError(name)
  830. def process_request(self):
  831. self.server._handler(self)
  832. def log_message(self, format, *args):
  833. if log:
  834. super(DelegatingHTTPRequestHandler,
  835. self).log_message(format, *args)
  836. HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
  837. ControlMixin.__init__(self, handler, poll_interval)
  838. self.sslctx = sslctx
  839. def get_request(self):
  840. try:
  841. sock, addr = self.socket.accept()
  842. if self.sslctx:
  843. sock = self.sslctx.wrap_socket(sock, server_side=True)
  844. except OSError as e:
  845. # socket errors are silenced by the caller, print them here
  846. sys.stderr.write("Got an error:\n%s\n" % e)
  847. raise
  848. return sock, addr
  849. class TestTCPServer(ControlMixin, ThreadingTCPServer):
  850. """
  851. A TCP server which is controllable using :class:`ControlMixin`.
  852. :param addr: A tuple with the IP address and port to listen on.
  853. :param handler: A handler callable which will be called with a single
  854. parameter - the request - in order to process the request.
  855. :param poll_interval: The polling interval in seconds.
  856. :bind_and_activate: If True (the default), binds the server and starts it
  857. listening. If False, you need to call
  858. :meth:`server_bind` and :meth:`server_activate` at
  859. some later time before calling :meth:`start`, so that
  860. the server will set up the socket and listen on it.
  861. """
  862. allow_reuse_address = True
  863. def __init__(self, addr, handler, poll_interval=0.5,
  864. bind_and_activate=True):
  865. class DelegatingTCPRequestHandler(StreamRequestHandler):
  866. def handle(self):
  867. self.server._handler(self)
  868. ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
  869. bind_and_activate)
  870. ControlMixin.__init__(self, handler, poll_interval)
  871. def server_bind(self):
  872. super(TestTCPServer, self).server_bind()
  873. self.port = self.socket.getsockname()[1]
  874. class TestUDPServer(ControlMixin, ThreadingUDPServer):
  875. """
  876. A UDP server which is controllable using :class:`ControlMixin`.
  877. :param addr: A tuple with the IP address and port to listen on.
  878. :param handler: A handler callable which will be called with a
  879. single parameter - the request - in order to
  880. process the request.
  881. :param poll_interval: The polling interval for shutdown requests,
  882. in seconds.
  883. :bind_and_activate: If True (the default), binds the server and
  884. starts it listening. If False, you need to
  885. call :meth:`server_bind` and
  886. :meth:`server_activate` at some later time
  887. before calling :meth:`start`, so that the server will
  888. set up the socket and listen on it.
  889. """
  890. def __init__(self, addr, handler, poll_interval=0.5,
  891. bind_and_activate=True):
  892. class DelegatingUDPRequestHandler(DatagramRequestHandler):
  893. def handle(self):
  894. self.server._handler(self)
  895. def finish(self):
  896. data = self.wfile.getvalue()
  897. if data:
  898. try:
  899. super(DelegatingUDPRequestHandler, self).finish()
  900. except OSError:
  901. if not self.server._closed:
  902. raise
  903. ThreadingUDPServer.__init__(self, addr,
  904. DelegatingUDPRequestHandler,
  905. bind_and_activate)
  906. ControlMixin.__init__(self, handler, poll_interval)
  907. self._closed = False
  908. def server_bind(self):
  909. super(TestUDPServer, self).server_bind()
  910. self.port = self.socket.getsockname()[1]
  911. def server_close(self):
  912. super(TestUDPServer, self).server_close()
  913. self._closed = True
  914. if hasattr(socket, "AF_UNIX"):
  915. class TestUnixStreamServer(TestTCPServer):
  916. address_family = socket.AF_UNIX
  917. class TestUnixDatagramServer(TestUDPServer):
  918. address_family = socket.AF_UNIX
  919. # - end of server_helper section
  920. @support.requires_working_socket()
  921. @threading_helper.requires_working_threading()
  922. class SMTPHandlerTest(BaseTest):
  923. # bpo-14314, bpo-19665, bpo-34092: don't wait forever
  924. TIMEOUT = support.LONG_TIMEOUT
  925. def test_basic(self):
  926. sockmap = {}
  927. server = TestSMTPServer((socket_helper.HOST, 0), self.process_message, 0.001,
  928. sockmap)
  929. server.start()
  930. addr = (socket_helper.HOST, server.port)
  931. h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
  932. timeout=self.TIMEOUT)
  933. self.assertEqual(h.toaddrs, ['you'])
  934. self.messages = []
  935. r = logging.makeLogRecord({'msg': 'Hello \u2713'})
  936. self.handled = threading.Event()
  937. h.handle(r)
  938. self.handled.wait(self.TIMEOUT)
  939. server.stop()
  940. self.assertTrue(self.handled.is_set())
  941. self.assertEqual(len(self.messages), 1)
  942. peer, mailfrom, rcpttos, data = self.messages[0]
  943. self.assertEqual(mailfrom, 'me')
  944. self.assertEqual(rcpttos, ['you'])
  945. self.assertIn('\nSubject: Log\n', data)
  946. self.assertTrue(data.endswith('\n\nHello \u2713'))
  947. h.close()
  948. def process_message(self, *args):
  949. self.messages.append(args)
  950. self.handled.set()
  951. class MemoryHandlerTest(BaseTest):
  952. """Tests for the MemoryHandler."""
  953. # Do not bother with a logger name group.
  954. expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
  955. def setUp(self):
  956. BaseTest.setUp(self)
  957. self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
  958. self.root_hdlr)
  959. self.mem_logger = logging.getLogger('mem')
  960. self.mem_logger.propagate = 0
  961. self.mem_logger.addHandler(self.mem_hdlr)
  962. def tearDown(self):
  963. self.mem_hdlr.close()
  964. BaseTest.tearDown(self)
  965. def test_flush(self):
  966. # The memory handler flushes to its target handler based on specific
  967. # criteria (message count and message level).
  968. self.mem_logger.debug(self.next_message())
  969. self.assert_log_lines([])
  970. self.mem_logger.info(self.next_message())
  971. self.assert_log_lines([])
  972. # This will flush because the level is >= logging.WARNING
  973. self.mem_logger.warning(self.next_message())
  974. lines = [
  975. ('DEBUG', '1'),
  976. ('INFO', '2'),
  977. ('WARNING', '3'),
  978. ]
  979. self.assert_log_lines(lines)
  980. for n in (4, 14):
  981. for i in range(9):
  982. self.mem_logger.debug(self.next_message())
  983. self.assert_log_lines(lines)
  984. # This will flush because it's the 10th message since the last
  985. # flush.
  986. self.mem_logger.debug(self.next_message())
  987. lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
  988. self.assert_log_lines(lines)
  989. self.mem_logger.debug(self.next_message())
  990. self.assert_log_lines(lines)
  991. def test_flush_on_close(self):
  992. """
  993. Test that the flush-on-close configuration works as expected.
  994. """
  995. self.mem_logger.debug(self.next_message())
  996. self.assert_log_lines([])
  997. self.mem_logger.info(self.next_message())
  998. self.assert_log_lines([])
  999. self.mem_logger.removeHandler(self.mem_hdlr)
  1000. # Default behaviour is to flush on close. Check that it happens.
  1001. self.mem_hdlr.close()
  1002. lines = [
  1003. ('DEBUG', '1'),
  1004. ('INFO', '2'),
  1005. ]
  1006. self.assert_log_lines(lines)
  1007. # Now configure for flushing not to be done on close.
  1008. self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
  1009. self.root_hdlr,
  1010. False)
  1011. self.mem_logger.addHandler(self.mem_hdlr)
  1012. self.mem_logger.debug(self.next_message())
  1013. self.assert_log_lines(lines) # no change
  1014. self.mem_logger.info(self.next_message())
  1015. self.assert_log_lines(lines) # no change
  1016. self.mem_logger.removeHandler(self.mem_hdlr)
  1017. self.mem_hdlr.close()
  1018. # assert that no new lines have been added
  1019. self.assert_log_lines(lines) # no change
  1020. @threading_helper.requires_working_threading()
  1021. def test_race_between_set_target_and_flush(self):
  1022. class MockRaceConditionHandler:
  1023. def __init__(self, mem_hdlr):
  1024. self.mem_hdlr = mem_hdlr
  1025. self.threads = []
  1026. def removeTarget(self):
  1027. self.mem_hdlr.setTarget(None)
  1028. def handle(self, msg):
  1029. thread = threading.Thread(target=self.removeTarget)
  1030. self.threads.append(thread)
  1031. thread.start()
  1032. target = MockRaceConditionHandler(self.mem_hdlr)
  1033. try:
  1034. self.mem_hdlr.setTarget(target)
  1035. for _ in range(10):
  1036. time.sleep(0.005)
  1037. self.mem_logger.info("not flushed")
  1038. self.mem_logger.warning("flushed")
  1039. finally:
  1040. for thread in target.threads:
  1041. threading_helper.join_thread(thread)
  1042. class ExceptionFormatter(logging.Formatter):
  1043. """A special exception formatter."""
  1044. def formatException(self, ei):
  1045. return "Got a [%s]" % ei[0].__name__
  1046. class ConfigFileTest(BaseTest):
  1047. """Reading logging config from a .ini-style config file."""
  1048. check_no_resource_warning = warnings_helper.check_no_resource_warning
  1049. expected_log_pat = r"^(\w+) \+\+ (\w+)$"
  1050. # config0 is a standard configuration.
  1051. config0 = """
  1052. [loggers]
  1053. keys=root
  1054. [handlers]
  1055. keys=hand1
  1056. [formatters]
  1057. keys=form1
  1058. [logger_root]
  1059. level=WARNING
  1060. handlers=hand1
  1061. [handler_hand1]
  1062. class=StreamHandler
  1063. level=NOTSET
  1064. formatter=form1
  1065. args=(sys.stdout,)
  1066. [formatter_form1]
  1067. format=%(levelname)s ++ %(message)s
  1068. datefmt=
  1069. """
  1070. # config1 adds a little to the standard configuration.
  1071. config1 = """
  1072. [loggers]
  1073. keys=root,parser
  1074. [handlers]
  1075. keys=hand1
  1076. [formatters]
  1077. keys=form1
  1078. [logger_root]
  1079. level=WARNING
  1080. handlers=
  1081. [logger_parser]
  1082. level=DEBUG
  1083. handlers=hand1
  1084. propagate=1
  1085. qualname=compiler.parser
  1086. [handler_hand1]
  1087. class=StreamHandler
  1088. level=NOTSET
  1089. formatter=form1
  1090. args=(sys.stdout,)
  1091. [formatter_form1]
  1092. format=%(levelname)s ++ %(message)s
  1093. datefmt=
  1094. """
  1095. # config1a moves the handler to the root.
  1096. config1a = """
  1097. [loggers]
  1098. keys=root,parser
  1099. [handlers]
  1100. keys=hand1
  1101. [formatters]
  1102. keys=form1
  1103. [logger_root]
  1104. level=WARNING
  1105. handlers=hand1
  1106. [logger_parser]
  1107. level=DEBUG
  1108. handlers=
  1109. propagate=1
  1110. qualname=compiler.parser
  1111. [handler_hand1]
  1112. class=StreamHandler
  1113. level=NOTSET
  1114. formatter=form1
  1115. args=(sys.stdout,)
  1116. [formatter_form1]
  1117. format=%(levelname)s ++ %(message)s
  1118. datefmt=
  1119. """
  1120. # config2 has a subtle configuration error that should be reported
  1121. config2 = config1.replace("sys.stdout", "sys.stbout")
  1122. # config3 has a less subtle configuration error
  1123. config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
  1124. # config4 specifies a custom formatter class to be loaded
  1125. config4 = """
  1126. [loggers]
  1127. keys=root
  1128. [handlers]
  1129. keys=hand1
  1130. [formatters]
  1131. keys=form1
  1132. [logger_root]
  1133. level=NOTSET
  1134. handlers=hand1
  1135. [handler_hand1]
  1136. class=StreamHandler
  1137. level=NOTSET
  1138. formatter=form1
  1139. args=(sys.stdout,)
  1140. [formatter_form1]
  1141. class=""" + __name__ + """.ExceptionFormatter
  1142. format=%(levelname)s:%(name)s:%(message)s
  1143. datefmt=
  1144. """
  1145. # config5 specifies a custom handler class to be loaded
  1146. config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
  1147. # config6 uses ', ' delimiters in the handlers and formatters sections
  1148. config6 = """
  1149. [loggers]
  1150. keys=root,parser
  1151. [handlers]
  1152. keys=hand1, hand2
  1153. [formatters]
  1154. keys=form1, form2
  1155. [logger_root]
  1156. level=WARNING
  1157. handlers=
  1158. [logger_parser]
  1159. level=DEBUG
  1160. handlers=hand1
  1161. propagate=1
  1162. qualname=compiler.parser
  1163. [handler_hand1]
  1164. class=StreamHandler
  1165. level=NOTSET
  1166. formatter=form1
  1167. args=(sys.stdout,)
  1168. [handler_hand2]
  1169. class=StreamHandler
  1170. level=NOTSET
  1171. formatter=form1
  1172. args=(sys.stderr,)
  1173. [formatter_form1]
  1174. format=%(levelname)s ++ %(message)s
  1175. datefmt=
  1176. [formatter_form2]
  1177. format=%(message)s
  1178. datefmt=
  1179. """
  1180. # config7 adds a compiler logger, and uses kwargs instead of args.
  1181. config7 = """
  1182. [loggers]
  1183. keys=root,parser,compiler
  1184. [handlers]
  1185. keys=hand1
  1186. [formatters]
  1187. keys=form1
  1188. [logger_root]
  1189. level=WARNING
  1190. handlers=hand1
  1191. [logger_compiler]
  1192. level=DEBUG
  1193. handlers=
  1194. propagate=1
  1195. qualname=compiler
  1196. [logger_parser]
  1197. level=DEBUG
  1198. handlers=
  1199. propagate=1
  1200. qualname=compiler.parser
  1201. [handler_hand1]
  1202. class=StreamHandler
  1203. level=NOTSET
  1204. formatter=form1
  1205. kwargs={'stream': sys.stdout,}
  1206. [formatter_form1]
  1207. format=%(levelname)s ++ %(message)s
  1208. datefmt=
  1209. """
  1210. # config 8, check for resource warning
  1211. config8 = r"""
  1212. [loggers]
  1213. keys=root
  1214. [handlers]
  1215. keys=file
  1216. [formatters]
  1217. keys=
  1218. [logger_root]
  1219. level=DEBUG
  1220. handlers=file
  1221. [handler_file]
  1222. class=FileHandler
  1223. level=DEBUG
  1224. args=("{tempfile}",)
  1225. kwargs={{"encoding": "utf-8"}}
  1226. """
  1227. disable_test = """
  1228. [loggers]
  1229. keys=root
  1230. [handlers]
  1231. keys=screen
  1232. [formatters]
  1233. keys=
  1234. [logger_root]
  1235. level=DEBUG
  1236. handlers=screen
  1237. [handler_screen]
  1238. level=DEBUG
  1239. class=StreamHandler
  1240. args=(sys.stdout,)
  1241. formatter=
  1242. """
  1243. def apply_config(self, conf, **kwargs):
  1244. file = io.StringIO(textwrap.dedent(conf))
  1245. logging.config.fileConfig(file, encoding="utf-8", **kwargs)
  1246. def test_config0_ok(self):
  1247. # A simple config file which overrides the default settings.
  1248. with support.captured_stdout() as output:
  1249. self.apply_config(self.config0)
  1250. logger = logging.getLogger()
  1251. # Won't output anything
  1252. logger.info(self.next_message())
  1253. # Outputs a message
  1254. logger.error(self.next_message())
  1255. self.assert_log_lines([
  1256. ('ERROR', '2'),
  1257. ], stream=output)
  1258. # Original logger output is empty.
  1259. self.assert_log_lines([])
  1260. def test_config0_using_cp_ok(self):
  1261. # A simple config file which overrides the default settings.
  1262. with support.captured_stdout() as output:
  1263. file = io.StringIO(textwrap.dedent(self.config0))
  1264. cp = configparser.ConfigParser()
  1265. cp.read_file(file)
  1266. logging.config.fileConfig(cp)
  1267. logger = logging.getLogger()
  1268. # Won't output anything
  1269. logger.info(self.next_message())
  1270. # Outputs a message
  1271. logger.error(self.next_message())
  1272. self.assert_log_lines([
  1273. ('ERROR', '2'),
  1274. ], stream=output)
  1275. # Original logger output is empty.
  1276. self.assert_log_lines([])
  1277. def test_config1_ok(self, config=config1):
  1278. # A config file defining a sub-parser as well.
  1279. with support.captured_stdout() as output:
  1280. self.apply_config(config)
  1281. logger = logging.getLogger("compiler.parser")
  1282. # Both will output a message
  1283. logger.info(self.next_message())
  1284. logger.error(self.next_message())
  1285. self.assert_log_lines([
  1286. ('INFO', '1'),
  1287. ('ERROR', '2'),
  1288. ], stream=output)
  1289. # Original logger output is empty.
  1290. self.assert_log_lines([])
  1291. def test_config2_failure(self):
  1292. # A simple config file which overrides the default settings.
  1293. self.assertRaises(Exception, self.apply_config, self.config2)
  1294. def test_config3_failure(self):
  1295. # A simple config file which overrides the default settings.
  1296. self.assertRaises(Exception, self.apply_config, self.config3)
  1297. def test_config4_ok(self):
  1298. # A config file specifying a custom formatter class.
  1299. with support.captured_stdout() as output:
  1300. self.apply_config(self.config4)
  1301. logger = logging.getLogger()
  1302. try:
  1303. raise RuntimeError()
  1304. except RuntimeError:
  1305. logging.exception("just testing")
  1306. sys.stdout.seek(0)
  1307. self.assertEqual(output.getvalue(),
  1308. "ERROR:root:just testing\nGot a [RuntimeError]\n")
  1309. # Original logger output is empty
  1310. self.assert_log_lines([])
  1311. def test_config5_ok(self):
  1312. self.test_config1_ok(config=self.config5)
  1313. def test_config6_ok(self):
  1314. self.test_config1_ok(config=self.config6)
  1315. def test_config7_ok(self):
  1316. with support.captured_stdout() as output:
  1317. self.apply_config(self.config1a)
  1318. logger = logging.getLogger("compiler.parser")
  1319. # See issue #11424. compiler-hyphenated sorts
  1320. # between compiler and compiler.xyz and this
  1321. # was preventing compiler.xyz from being included
  1322. # in the child loggers of compiler because of an
  1323. # overzealous loop termination condition.
  1324. hyphenated = logging.getLogger('compiler-hyphenated')
  1325. # All will output a message
  1326. logger.info(self.next_message())
  1327. logger.error(self.next_message())
  1328. hyphenated.critical(self.next_message())
  1329. self.assert_log_lines([
  1330. ('INFO', '1'),
  1331. ('ERROR', '2'),
  1332. ('CRITICAL', '3'),
  1333. ], stream=output)
  1334. # Original logger output is empty.
  1335. self.assert_log_lines([])
  1336. with support.captured_stdout() as output:
  1337. self.apply_config(self.config7)
  1338. logger = logging.getLogger("compiler.parser")
  1339. self.assertFalse(logger.disabled)
  1340. # Both will output a message
  1341. logger.info(self.next_message())
  1342. logger.error(self.next_message())
  1343. logger = logging.getLogger("compiler.lexer")
  1344. # Both will output a message
  1345. logger.info(self.next_message())
  1346. logger.error(self.next_message())
  1347. # Will not appear
  1348. hyphenated.critical(self.next_message())
  1349. self.assert_log_lines([
  1350. ('INFO', '4'),
  1351. ('ERROR', '5'),
  1352. ('INFO', '6'),
  1353. ('ERROR', '7'),
  1354. ], stream=output)
  1355. # Original logger output is empty.
  1356. self.assert_log_lines([])
  1357. def test_config8_ok(self):
  1358. def cleanup(h1, fn):
  1359. h1.close()
  1360. os.remove(fn)
  1361. with self.check_no_resource_warning():
  1362. fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
  1363. os.close(fd)
  1364. # Replace single backslash with double backslash in windows
  1365. # to avoid unicode error during string formatting
  1366. if os.name == "nt":
  1367. fn = fn.replace("\\", "\\\\")
  1368. config8 = self.config8.format(tempfile=fn)
  1369. self.apply_config(config8)
  1370. self.apply_config(config8)
  1371. handler = logging.root.handlers[0]
  1372. self.addCleanup(cleanup, handler, fn)
  1373. def test_logger_disabling(self):
  1374. self.apply_config(self.disable_test)
  1375. logger = logging.getLogger('some_pristine_logger')
  1376. self.assertFalse(logger.disabled)
  1377. self.apply_config(self.disable_test)
  1378. self.assertTrue(logger.disabled)
  1379. self.apply_config(self.disable_test, disable_existing_loggers=False)
  1380. self.assertFalse(logger.disabled)
  1381. def test_config_set_handler_names(self):
  1382. test_config = """
  1383. [loggers]
  1384. keys=root
  1385. [handlers]
  1386. keys=hand1
  1387. [formatters]
  1388. keys=form1
  1389. [logger_root]
  1390. handlers=hand1
  1391. [handler_hand1]
  1392. class=StreamHandler
  1393. formatter=form1
  1394. [formatter_form1]
  1395. format=%(levelname)s ++ %(message)s
  1396. """
  1397. self.apply_config(test_config)
  1398. self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
  1399. def test_defaults_do_no_interpolation(self):
  1400. """bpo-33802 defaults should not get interpolated"""
  1401. ini = textwrap.dedent("""
  1402. [formatters]
  1403. keys=default
  1404. [formatter_default]
  1405. [handlers]
  1406. keys=console
  1407. [handler_console]
  1408. class=logging.StreamHandler
  1409. args=tuple()
  1410. [loggers]
  1411. keys=root
  1412. [logger_root]
  1413. formatter=default
  1414. handlers=console
  1415. """).strip()
  1416. fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
  1417. try:
  1418. os.write(fd, ini.encode('ascii'))
  1419. os.close(fd)
  1420. logging.config.fileConfig(
  1421. fn,
  1422. encoding="utf-8",
  1423. defaults=dict(
  1424. version=1,
  1425. disable_existing_loggers=False,
  1426. formatters={
  1427. "generic": {
  1428. "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
  1429. "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
  1430. "class": "logging.Formatter"
  1431. },
  1432. },
  1433. )
  1434. )
  1435. finally:
  1436. os.unlink(fn)
  1437. @support.requires_working_socket()
  1438. @threading_helper.requires_working_threading()
  1439. class SocketHandlerTest(BaseTest):
  1440. """Test for SocketHandler objects."""
  1441. server_class = TestTCPServer
  1442. address = ('localhost', 0)
  1443. def setUp(self):
  1444. """Set up a TCP server to receive log messages, and a SocketHandler
  1445. pointing to that server's address and port."""
  1446. BaseTest.setUp(self)
  1447. # Issue #29177: deal with errors that happen during setup
  1448. self.server = self.sock_hdlr = self.server_exception = None
  1449. try:
  1450. self.server = server = self.server_class(self.address,
  1451. self.handle_socket, 0.01)
  1452. server.start()
  1453. # Uncomment next line to test error recovery in setUp()
  1454. # raise OSError('dummy error raised')
  1455. except OSError as e:
  1456. self.server_exception = e
  1457. return
  1458. server.ready.wait()
  1459. hcls = logging.handlers.SocketHandler
  1460. if isinstance(server.server_address, tuple):
  1461. self.sock_hdlr = hcls('localhost', server.port)
  1462. else:
  1463. self.sock_hdlr = hcls(server.server_address, None)
  1464. self.log_output = ''
  1465. self.root_logger.removeHandler(self.root_logger.handlers[0])
  1466. self.root_logger.addHandler(self.sock_hdlr)
  1467. self.handled = threading.Semaphore(0)
  1468. def tearDown(self):
  1469. """Shutdown the TCP server."""
  1470. try:
  1471. if self.sock_hdlr:
  1472. self.root_logger.removeHandler(self.sock_hdlr)
  1473. self.sock_hdlr.close()
  1474. if self.server:
  1475. self.server.stop()
  1476. finally:
  1477. BaseTest.tearDown(self)
  1478. def handle_socket(self, request):
  1479. conn = request.connection
  1480. while True:
  1481. chunk = conn.recv(4)
  1482. if len(chunk) < 4:
  1483. break
  1484. slen = struct.unpack(">L", chunk)[0]
  1485. chunk = conn.recv(slen)
  1486. while len(chunk) < slen:
  1487. chunk = chunk + conn.recv(slen - len(chunk))
  1488. obj = pickle.loads(chunk)
  1489. record = logging.makeLogRecord(obj)
  1490. self.log_output += record.msg + '\n'
  1491. self.handled.release()
  1492. def test_output(self):
  1493. # The log message sent to the SocketHandler is properly received.
  1494. if self.server_exception:
  1495. self.skipTest(self.server_exception)
  1496. logger = logging.getLogger("tcp")
  1497. logger.error("spam")
  1498. self.handled.acquire()
  1499. logger.debug("eggs")
  1500. self.handled.acquire()
  1501. self.assertEqual(self.log_output, "spam\neggs\n")
  1502. def test_noserver(self):
  1503. if self.server_exception:
  1504. self.skipTest(self.server_exception)
  1505. # Avoid timing-related failures due to SocketHandler's own hard-wired
  1506. # one-second timeout on socket.create_connection() (issue #16264).
  1507. self.sock_hdlr.retryStart = 2.5
  1508. # Kill the server
  1509. self.server.stop()
  1510. # The logging call should try to connect, which should fail
  1511. try:
  1512. raise RuntimeError('Deliberate mistake')
  1513. except RuntimeError:
  1514. self.root_logger.exception('Never sent')
  1515. self.root_logger.error('Never sent, either')
  1516. now = time.time()
  1517. self.assertGreater(self.sock_hdlr.retryTime, now)
  1518. time.sleep(self.sock_hdlr.retryTime - now + 0.001)
  1519. self.root_logger.error('Nor this')
  1520. def _get_temp_domain_socket():
  1521. fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
  1522. os.close(fd)
  1523. # just need a name - file can't be present, or we'll get an
  1524. # 'address already in use' error.
  1525. os.remove(fn)
  1526. return fn
  1527. @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
  1528. class UnixSocketHandlerTest(SocketHandlerTest):
  1529. """Test for SocketHandler with unix sockets."""
  1530. if hasattr(socket, "AF_UNIX"):
  1531. server_class = TestUnixStreamServer
  1532. def setUp(self):
  1533. # override the definition in the base class
  1534. self.address = _get_temp_domain_socket()
  1535. SocketHandlerTest.setUp(self)
  1536. def tearDown(self):
  1537. SocketHandlerTest.tearDown(self)
  1538. os_helper.unlink(self.address)
  1539. @support.requires_working_socket()
  1540. @threading_helper.requires_working_threading()
  1541. class DatagramHandlerTest(BaseTest):
  1542. """Test for DatagramHandler."""
  1543. server_class = TestUDPServer
  1544. address = ('localhost', 0)
  1545. def setUp(self):
  1546. """Set up a UDP server to receive log messages, and a DatagramHandler
  1547. pointing to that server's address and port."""
  1548. BaseTest.setUp(self)
  1549. # Issue #29177: deal with errors that happen during setup
  1550. self.server = self.sock_hdlr = self.server_exception = None
  1551. try:
  1552. self.server = server = self.server_class(self.address,
  1553. self.handle_datagram, 0.01)
  1554. server.start()
  1555. # Uncomment next line to test error recovery in setUp()
  1556. # raise OSError('dummy error raised')
  1557. except OSError as e:
  1558. self.server_exception = e
  1559. return
  1560. server.ready.wait()
  1561. hcls = logging.handlers.DatagramHandler
  1562. if isinstance(server.server_address, tuple):
  1563. self.sock_hdlr = hcls('localhost', server.port)
  1564. else:
  1565. self.sock_hdlr = hcls(server.server_address, None)
  1566. self.log_output = ''
  1567. self.root_logger.removeHandler(self.root_logger.handlers[0])
  1568. self.root_logger.addHandler(self.sock_hdlr)
  1569. self.handled = threading.Event()
  1570. def tearDown(self):
  1571. """Shutdown the UDP server."""
  1572. try:
  1573. if self.server:
  1574. self.server.stop()
  1575. if self.sock_hdlr:
  1576. self.root_logger.removeHandler(self.sock_hdlr)
  1577. self.sock_hdlr.close()
  1578. finally:
  1579. BaseTest.tearDown(self)
  1580. def handle_datagram(self, request):
  1581. slen = struct.pack('>L', 0) # length of prefix
  1582. packet = request.packet[len(slen):]
  1583. obj = pickle.loads(packet)
  1584. record = logging.makeLogRecord(obj)
  1585. self.log_output += record.msg + '\n'
  1586. self.handled.set()
  1587. def test_output(self):
  1588. # The log message sent to the DatagramHandler is properly received.
  1589. if self.server_exception:
  1590. self.skipTest(self.server_exception)
  1591. logger = logging.getLogger("udp")
  1592. logger.error("spam")
  1593. self.handled.wait()
  1594. self.handled.clear()
  1595. logger.error("eggs")
  1596. self.handled.wait()
  1597. self.assertEqual(self.log_output, "spam\neggs\n")
  1598. @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
  1599. class UnixDatagramHandlerTest(DatagramHandlerTest):
  1600. """Test for DatagramHandler using Unix sockets."""
  1601. if hasattr(socket, "AF_UNIX"):
  1602. server_class = TestUnixDatagramServer
  1603. def setUp(self):
  1604. # override the definition in the base class
  1605. self.address = _get_temp_domain_socket()
  1606. DatagramHandlerTest.setUp(self)
  1607. def tearDown(self):
  1608. DatagramHandlerTest.tearDown(self)
  1609. os_helper.unlink(self.address)
  1610. @support.requires_working_socket()
  1611. @threading_helper.requires_working_threading()
  1612. class SysLogHandlerTest(BaseTest):
  1613. """Test for SysLogHandler using UDP."""
  1614. server_class = TestUDPServer
  1615. address = ('localhost', 0)
  1616. def setUp(self):
  1617. """Set up a UDP server to receive log messages, and a SysLogHandler
  1618. pointing to that server's address and port."""
  1619. BaseTest.setUp(self)
  1620. # Issue #29177: deal with errors that happen during setup
  1621. self.server = self.sl_hdlr = self.server_exception = None
  1622. try:
  1623. self.server = server = self.server_class(self.address,
  1624. self.handle_datagram, 0.01)
  1625. server.start()
  1626. # Uncomment next line to test error recovery in setUp()
  1627. # raise OSError('dummy error raised')
  1628. except OSError as e:
  1629. self.server_exception = e
  1630. return
  1631. server.ready.wait()
  1632. hcls = logging.handlers.SysLogHandler
  1633. if isinstance(server.server_address, tuple):
  1634. self.sl_hdlr = hcls((server.server_address[0], server.port))
  1635. else:
  1636. self.sl_hdlr = hcls(server.server_address)
  1637. self.log_output = b''
  1638. self.root_logger.removeHandler(self.root_logger.handlers[0])
  1639. self.root_logger.addHandler(self.sl_hdlr)
  1640. self.handled = threading.Event()
  1641. def tearDown(self):
  1642. """Shutdown the server."""
  1643. try:
  1644. if self.server:
  1645. self.server.stop()
  1646. if self.sl_hdlr:
  1647. self.root_logger.removeHandler(self.sl_hdlr)
  1648. self.sl_hdlr.close()
  1649. finally:
  1650. BaseTest.tearDown(self)
  1651. def handle_datagram(self, request):
  1652. self.log_output = request.packet
  1653. self.handled.set()
  1654. def test_output(self):
  1655. if self.server_exception:
  1656. self.skipTest(self.server_exception)
  1657. # The log message sent to the SysLogHandler is properly received.
  1658. logger = logging.getLogger("slh")
  1659. logger.error("sp\xe4m")
  1660. self.handled.wait()
  1661. self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
  1662. self.handled.clear()
  1663. self.sl_hdlr.append_nul = False
  1664. logger.error("sp\xe4m")
  1665. self.handled.wait()
  1666. self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
  1667. self.handled.clear()
  1668. self.sl_hdlr.ident = "h\xe4m-"
  1669. logger.error("sp\xe4m")
  1670. self.handled.wait()
  1671. self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
  1672. def test_udp_reconnection(self):
  1673. logger = logging.getLogger("slh")
  1674. self.sl_hdlr.close()
  1675. self.handled.clear()
  1676. logger.error("sp\xe4m")
  1677. self.handled.wait(0.1)
  1678. self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
  1679. @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
  1680. class UnixSysLogHandlerTest(SysLogHandlerTest):
  1681. """Test for SysLogHandler with Unix sockets."""
  1682. if hasattr(socket, "AF_UNIX"):
  1683. server_class = TestUnixDatagramServer
  1684. def setUp(self):
  1685. # override the definition in the base class
  1686. self.address = _get_temp_domain_socket()
  1687. SysLogHandlerTest.setUp(self)
  1688. def tearDown(self):
  1689. SysLogHandlerTest.tearDown(self)
  1690. os_helper.unlink(self.address)
  1691. @unittest.skipUnless(socket_helper.IPV6_ENABLED,
  1692. 'IPv6 support required for this test.')
  1693. class IPv6SysLogHandlerTest(SysLogHandlerTest):
  1694. """Test for SysLogHandler with IPv6 host."""
  1695. server_class = TestUDPServer
  1696. address = ('::1', 0)
  1697. def setUp(self):
  1698. self.server_class.address_family = socket.AF_INET6
  1699. super(IPv6SysLogHandlerTest, self).setUp()
  1700. def tearDown(self):
  1701. self.server_class.address_family = socket.AF_INET
  1702. super(IPv6SysLogHandlerTest, self).tearDown()
  1703. @support.requires_working_socket()
  1704. @threading_helper.requires_working_threading()
  1705. class HTTPHandlerTest(BaseTest):
  1706. """Test for HTTPHandler."""
  1707. def setUp(self):
  1708. """Set up an HTTP server to receive log messages, and a HTTPHandler
  1709. pointing to that server's address and port."""
  1710. BaseTest.setUp(self)
  1711. self.handled = threading.Event()
  1712. def handle_request(self, request):
  1713. self.command = request.command
  1714. self.log_data = urlparse(request.path)
  1715. if self.command == 'POST':
  1716. try:
  1717. rlen = int(request.headers['Content-Length'])
  1718. self.post_data = request.rfile.read(rlen)
  1719. except:
  1720. self.post_data = None
  1721. request.send_response(200)
  1722. request.end_headers()
  1723. self.handled.set()
  1724. def test_output(self):
  1725. # The log message sent to the HTTPHandler is properly received.
  1726. logger = logging.getLogger("http")
  1727. root_logger = self.root_logger
  1728. root_logger.removeHandler(self.root_logger.handlers[0])
  1729. for secure in (False, True):
  1730. addr = ('localhost', 0)
  1731. if secure:
  1732. try:
  1733. import ssl
  1734. except ImportError:
  1735. sslctx = None
  1736. else:
  1737. here = os.path.dirname(__file__)
  1738. localhost_cert = os.path.join(here, "keycert.pem")
  1739. sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
  1740. sslctx.load_cert_chain(localhost_cert)
  1741. context = ssl.create_default_context(cafile=localhost_cert)
  1742. else:
  1743. sslctx = None
  1744. context = None
  1745. self.server = server = TestHTTPServer(addr, self.handle_request,
  1746. 0.01, sslctx=sslctx)
  1747. server.start()
  1748. server.ready.wait()
  1749. host = 'localhost:%d' % server.server_port
  1750. secure_client = secure and sslctx
  1751. self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
  1752. secure=secure_client,
  1753. context=context,
  1754. credentials=('foo', 'bar'))
  1755. self.log_data = None
  1756. root_logger.addHandler(self.h_hdlr)
  1757. for method in ('GET', 'POST'):
  1758. self.h_hdlr.method = method
  1759. self.handled.clear()
  1760. msg = "sp\xe4m"
  1761. logger.error(msg)
  1762. self.handled.wait()
  1763. self.assertEqual(self.log_data.path, '/frob')
  1764. self.assertEqual(self.command, method)
  1765. if method == 'GET':
  1766. d = parse_qs(self.log_data.query)
  1767. else:
  1768. d = parse_qs(self.post_data.decode('utf-8'))
  1769. self.assertEqual(d['name'], ['http'])
  1770. self.assertEqual(d['funcName'], ['test_output'])
  1771. self.assertEqual(d['msg'], [msg])
  1772. self.server.stop()
  1773. self.root_logger.removeHandler(self.h_hdlr)
  1774. self.h_hdlr.close()
  1775. class MemoryTest(BaseTest):
  1776. """Test memory persistence of logger objects."""
  1777. def setUp(self):
  1778. """Create a dict to remember potentially destroyed objects."""
  1779. BaseTest.setUp(self)
  1780. self._survivors = {}
  1781. def _watch_for_survival(self, *args):
  1782. """Watch the given objects for survival, by creating weakrefs to
  1783. them."""
  1784. for obj in args:
  1785. key = id(obj), repr(obj)
  1786. self._survivors[key] = weakref.ref(obj)
  1787. def _assertTruesurvival(self):
  1788. """Assert that all objects watched for survival have survived."""
  1789. # Trigger cycle breaking.
  1790. gc.collect()
  1791. dead = []
  1792. for (id_, repr_), ref in self._survivors.items():
  1793. if ref() is None:
  1794. dead.append(repr_)
  1795. if dead:
  1796. self.fail("%d objects should have survived "
  1797. "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
  1798. def test_persistent_loggers(self):
  1799. # Logger objects are persistent and retain their configuration, even
  1800. # if visible references are destroyed.
  1801. self.root_logger.setLevel(logging.INFO)
  1802. foo = logging.getLogger("foo")
  1803. self._watch_for_survival(foo)
  1804. foo.setLevel(logging.DEBUG)
  1805. self.root_logger.debug(self.next_message())
  1806. foo.debug(self.next_message())
  1807. self.assert_log_lines([
  1808. ('foo', 'DEBUG', '2'),
  1809. ])
  1810. del foo
  1811. # foo has survived.
  1812. self._assertTruesurvival()
  1813. # foo has retained its settings.
  1814. bar = logging.getLogger("foo")
  1815. bar.debug(self.next_message())
  1816. self.assert_log_lines([
  1817. ('foo', 'DEBUG', '2'),
  1818. ('foo', 'DEBUG', '3'),
  1819. ])
  1820. class EncodingTest(BaseTest):
  1821. def test_encoding_plain_file(self):
  1822. # In Python 2.x, a plain file object is treated as having no encoding.
  1823. log = logging.getLogger("test")
  1824. fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
  1825. os.close(fd)
  1826. # the non-ascii data we write to the log.
  1827. data = "foo\x80"
  1828. try:
  1829. handler = logging.FileHandler(fn, encoding="utf-8")
  1830. log.addHandler(handler)
  1831. try:
  1832. # write non-ascii data to the log.
  1833. log.warning(data)
  1834. finally:
  1835. log.removeHandler(handler)
  1836. handler.close()
  1837. # check we wrote exactly those bytes, ignoring trailing \n etc
  1838. f = open(fn, encoding="utf-8")
  1839. try:
  1840. self.assertEqual(f.read().rstrip(), data)
  1841. finally:
  1842. f.close()
  1843. finally:
  1844. if os.path.isfile(fn):
  1845. os.remove(fn)
  1846. def test_encoding_cyrillic_unicode(self):
  1847. log = logging.getLogger("test")
  1848. # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
  1849. message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
  1850. # Ensure it's written in a Cyrillic encoding
  1851. writer_class = codecs.getwriter('cp1251')
  1852. writer_class.encoding = 'cp1251'
  1853. stream = io.BytesIO()
  1854. writer = writer_class(stream, 'strict')
  1855. handler = logging.StreamHandler(writer)
  1856. log.addHandler(handler)
  1857. try:
  1858. log.warning(message)
  1859. finally:
  1860. log.removeHandler(handler)
  1861. handler.close()
  1862. # check we wrote exactly those bytes, ignoring trailing \n etc
  1863. s = stream.getvalue()
  1864. # Compare against what the data should be when encoded in CP-1251
  1865. self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
  1866. class WarningsTest(BaseTest):
  1867. def test_warnings(self):
  1868. with warnings.catch_warnings():
  1869. logging.captureWarnings(True)
  1870. self.addCleanup(logging.captureWarnings, False)
  1871. warnings.filterwarnings("always", category=UserWarning)
  1872. stream = io.StringIO()
  1873. h = logging.StreamHandler(stream)
  1874. logger = logging.getLogger("py.warnings")
  1875. logger.addHandler(h)
  1876. warnings.warn("I'm warning you...")
  1877. logger.removeHandler(h)
  1878. s = stream.getvalue()
  1879. h.close()
  1880. self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
  1881. # See if an explicit file uses the original implementation
  1882. a_file = io.StringIO()
  1883. warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
  1884. a_file, "Dummy line")
  1885. s = a_file.getvalue()
  1886. a_file.close()
  1887. self.assertEqual(s,
  1888. "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
  1889. def test_warnings_no_handlers(self):
  1890. with warnings.catch_warnings():
  1891. logging.captureWarnings(True)
  1892. self.addCleanup(logging.captureWarnings, False)
  1893. # confirm our assumption: no loggers are set
  1894. logger = logging.getLogger("py.warnings")
  1895. self.assertEqual(logger.handlers, [])
  1896. warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
  1897. self.assertEqual(len(logger.handlers), 1)
  1898. self.assertIsInstance(logger.handlers[0], logging.NullHandler)
  1899. def formatFunc(format, datefmt=None):
  1900. return logging.Formatter(format, datefmt)
  1901. class myCustomFormatter:
  1902. def __init__(self, fmt, datefmt=None):
  1903. pass
  1904. def handlerFunc():
  1905. return logging.StreamHandler()
  1906. class CustomHandler(logging.StreamHandler):
  1907. pass
  1908. class ConfigDictTest(BaseTest):
  1909. """Reading logging config from a dictionary."""
  1910. check_no_resource_warning = warnings_helper.check_no_resource_warning
  1911. expected_log_pat = r"^(\w+) \+\+ (\w+)$"
  1912. # config0 is a standard configuration.
  1913. config0 = {
  1914. 'version': 1,
  1915. 'formatters': {
  1916. 'form1' : {
  1917. 'format' : '%(levelname)s ++ %(message)s',
  1918. },
  1919. },
  1920. 'handlers' : {
  1921. 'hand1' : {
  1922. 'class' : 'logging.StreamHandler',
  1923. 'formatter' : 'form1',
  1924. 'level' : 'NOTSET',
  1925. 'stream' : 'ext://sys.stdout',
  1926. },
  1927. },
  1928. 'root' : {
  1929. 'level' : 'WARNING',
  1930. 'handlers' : ['hand1'],
  1931. },
  1932. }
  1933. # config1 adds a little to the standard configuration.
  1934. config1 = {
  1935. 'version': 1,
  1936. 'formatters': {
  1937. 'form1' : {
  1938. 'format' : '%(levelname)s ++ %(message)s',
  1939. },
  1940. },
  1941. 'handlers' : {
  1942. 'hand1' : {
  1943. 'class' : 'logging.StreamHandler',
  1944. 'formatter' : 'form1',
  1945. 'level' : 'NOTSET',
  1946. 'stream' : 'ext://sys.stdout',
  1947. },
  1948. },
  1949. 'loggers' : {
  1950. 'compiler.parser' : {
  1951. 'level' : 'DEBUG',
  1952. 'handlers' : ['hand1'],
  1953. },
  1954. },
  1955. 'root' : {
  1956. 'level' : 'WARNING',
  1957. },
  1958. }
  1959. # config1a moves the handler to the root. Used with config8a
  1960. config1a = {
  1961. 'version': 1,
  1962. 'formatters': {
  1963. 'form1' : {
  1964. 'format' : '%(levelname)s ++ %(message)s',
  1965. },
  1966. },
  1967. 'handlers' : {
  1968. 'hand1' : {
  1969. 'class' : 'logging.StreamHandler',
  1970. 'formatter' : 'form1',
  1971. 'level' : 'NOTSET',
  1972. 'stream' : 'ext://sys.stdout',
  1973. },
  1974. },
  1975. 'loggers' : {
  1976. 'compiler.parser' : {
  1977. 'level' : 'DEBUG',
  1978. },
  1979. },
  1980. 'root' : {
  1981. 'level' : 'WARNING',
  1982. 'handlers' : ['hand1'],
  1983. },
  1984. }
  1985. # config2 has a subtle configuration error that should be reported
  1986. config2 = {
  1987. 'version': 1,
  1988. 'formatters': {
  1989. 'form1' : {
  1990. 'format' : '%(levelname)s ++ %(message)s',
  1991. },
  1992. },
  1993. 'handlers' : {
  1994. 'hand1' : {
  1995. 'class' : 'logging.StreamHandler',
  1996. 'formatter' : 'form1',
  1997. 'level' : 'NOTSET',
  1998. 'stream' : 'ext://sys.stdbout',
  1999. },
  2000. },
  2001. 'loggers' : {
  2002. 'compiler.parser' : {
  2003. 'level' : 'DEBUG',
  2004. 'handlers' : ['hand1'],
  2005. },
  2006. },
  2007. 'root' : {
  2008. 'level' : 'WARNING',
  2009. },
  2010. }
  2011. # As config1 but with a misspelt level on a handler
  2012. config2a = {
  2013. 'version': 1,
  2014. 'formatters': {
  2015. 'form1' : {
  2016. 'format' : '%(levelname)s ++ %(message)s',
  2017. },
  2018. },
  2019. 'handlers' : {
  2020. 'hand1' : {
  2021. 'class' : 'logging.StreamHandler',
  2022. 'formatter' : 'form1',
  2023. 'level' : 'NTOSET',
  2024. 'stream' : 'ext://sys.stdout',
  2025. },
  2026. },
  2027. 'loggers' : {
  2028. 'compiler.parser' : {
  2029. 'level' : 'DEBUG',
  2030. 'handlers' : ['hand1'],
  2031. },
  2032. },
  2033. 'root' : {
  2034. 'level' : 'WARNING',
  2035. },
  2036. }
  2037. # As config1 but with a misspelt level on a logger
  2038. config2b = {
  2039. 'version': 1,
  2040. 'formatters': {
  2041. 'form1' : {
  2042. 'format' : '%(levelname)s ++ %(message)s',
  2043. },
  2044. },
  2045. 'handlers' : {
  2046. 'hand1' : {
  2047. 'class' : 'logging.StreamHandler',
  2048. 'formatter' : 'form1',
  2049. 'level' : 'NOTSET',
  2050. 'stream' : 'ext://sys.stdout',
  2051. },
  2052. },
  2053. 'loggers' : {
  2054. 'compiler.parser' : {
  2055. 'level' : 'DEBUG',
  2056. 'handlers' : ['hand1'],
  2057. },
  2058. },
  2059. 'root' : {
  2060. 'level' : 'WRANING',
  2061. },
  2062. }
  2063. # config3 has a less subtle configuration error
  2064. config3 = {
  2065. 'version': 1,
  2066. 'formatters': {
  2067. 'form1' : {
  2068. 'format' : '%(levelname)s ++ %(message)s',
  2069. },
  2070. },
  2071. 'handlers' : {
  2072. 'hand1' : {
  2073. 'class' : 'logging.StreamHandler',
  2074. 'formatter' : 'misspelled_name',
  2075. 'level' : 'NOTSET',
  2076. 'stream' : 'ext://sys.stdout',
  2077. },
  2078. },
  2079. 'loggers' : {
  2080. 'compiler.parser' : {
  2081. 'level' : 'DEBUG',
  2082. 'handlers' : ['hand1'],
  2083. },
  2084. },
  2085. 'root' : {
  2086. 'level' : 'WARNING',
  2087. },
  2088. }
  2089. # config4 specifies a custom formatter class to be loaded
  2090. config4 = {
  2091. 'version': 1,
  2092. 'formatters': {
  2093. 'form1' : {
  2094. '()' : __name__ + '.ExceptionFormatter',
  2095. 'format' : '%(levelname)s:%(name)s:%(message)s',
  2096. },
  2097. },
  2098. 'handlers' : {
  2099. 'hand1' : {
  2100. 'class' : 'logging.StreamHandler',
  2101. 'formatter' : 'form1',
  2102. 'level' : 'NOTSET',
  2103. 'stream' : 'ext://sys.stdout',
  2104. },
  2105. },
  2106. 'root' : {
  2107. 'level' : 'NOTSET',
  2108. 'handlers' : ['hand1'],
  2109. },
  2110. }
  2111. # As config4 but using an actual callable rather than a string
  2112. config4a = {
  2113. 'version': 1,
  2114. 'formatters': {
  2115. 'form1' : {
  2116. '()' : ExceptionFormatter,
  2117. 'format' : '%(levelname)s:%(name)s:%(message)s',
  2118. },
  2119. 'form2' : {
  2120. '()' : __name__ + '.formatFunc',
  2121. 'format' : '%(levelname)s:%(name)s:%(message)s',
  2122. },
  2123. 'form3' : {
  2124. '()' : formatFunc,
  2125. 'format' : '%(levelname)s:%(name)s:%(message)s',
  2126. },
  2127. },
  2128. 'handlers' : {
  2129. 'hand1' : {
  2130. 'class' : 'logging.StreamHandler',
  2131. 'formatter' : 'form1',
  2132. 'level' : 'NOTSET',
  2133. 'stream' : 'ext://sys.stdout',
  2134. },
  2135. 'hand2' : {
  2136. '()' : handlerFunc,
  2137. },
  2138. },
  2139. 'root' : {
  2140. 'level' : 'NOTSET',
  2141. 'handlers' : ['hand1'],
  2142. },
  2143. }
  2144. # config5 specifies a custom handler class to be loaded
  2145. config5 = {
  2146. 'version': 1,
  2147. 'formatters': {
  2148. 'form1' : {
  2149. 'format' : '%(levelname)s ++ %(message)s',
  2150. },
  2151. },
  2152. 'handlers' : {
  2153. 'hand1' : {
  2154. 'class' : __name__ + '.CustomHandler',
  2155. 'formatter' : 'form1',
  2156. 'level' : 'NOTSET',
  2157. 'stream' : 'ext://sys.stdout',
  2158. },
  2159. },
  2160. 'loggers' : {
  2161. 'compiler.parser' : {
  2162. 'level' : 'DEBUG',
  2163. 'handlers' : ['hand1'],
  2164. },
  2165. },
  2166. 'root' : {
  2167. 'level' : 'WARNING',
  2168. },
  2169. }
  2170. # config6 specifies a custom handler class to be loaded
  2171. # but has bad arguments
  2172. config6 = {
  2173. 'version': 1,
  2174. 'formatters': {
  2175. 'form1' : {
  2176. 'format' : '%(levelname)s ++ %(message)s',
  2177. },
  2178. },
  2179. 'handlers' : {
  2180. 'hand1' : {
  2181. 'class' : __name__ + '.CustomHandler',
  2182. 'formatter' : 'form1',
  2183. 'level' : 'NOTSET',
  2184. 'stream' : 'ext://sys.stdout',
  2185. '9' : 'invalid parameter name',
  2186. },
  2187. },
  2188. 'loggers' : {
  2189. 'compiler.parser' : {
  2190. 'level' : 'DEBUG',
  2191. 'handlers' : ['hand1'],
  2192. },
  2193. },
  2194. 'root' : {
  2195. 'level' : 'WARNING',
  2196. },
  2197. }
  2198. # config 7 does not define compiler.parser but defines compiler.lexer
  2199. # so compiler.parser should be disabled after applying it
  2200. config7 = {
  2201. 'version': 1,
  2202. 'formatters': {
  2203. 'form1' : {
  2204. 'format' : '%(levelname)s ++ %(message)s',
  2205. },
  2206. },
  2207. 'handlers' : {
  2208. 'hand1' : {
  2209. 'class' : 'logging.StreamHandler',
  2210. 'formatter' : 'form1',
  2211. 'level' : 'NOTSET',
  2212. 'stream' : 'ext://sys.stdout',
  2213. },
  2214. },
  2215. 'loggers' : {
  2216. 'compiler.lexer' : {
  2217. 'level' : 'DEBUG',
  2218. 'handlers' : ['hand1'],
  2219. },
  2220. },
  2221. 'root' : {
  2222. 'level' : 'WARNING',
  2223. },
  2224. }
  2225. # config8 defines both compiler and compiler.lexer
  2226. # so compiler.parser should not be disabled (since
  2227. # compiler is defined)
  2228. config8 = {
  2229. 'version': 1,
  2230. 'disable_existing_loggers' : False,
  2231. 'formatters': {
  2232. 'form1' : {
  2233. 'format' : '%(levelname)s ++ %(message)s',
  2234. },
  2235. },
  2236. 'handlers' : {
  2237. 'hand1' : {
  2238. 'class' : 'logging.StreamHandler',
  2239. 'formatter' : 'form1',
  2240. 'level' : 'NOTSET',
  2241. 'stream' : 'ext://sys.stdout',
  2242. },
  2243. },
  2244. 'loggers' : {
  2245. 'compiler' : {
  2246. 'level' : 'DEBUG',
  2247. 'handlers' : ['hand1'],
  2248. },
  2249. 'compiler.lexer' : {
  2250. },
  2251. },
  2252. 'root' : {
  2253. 'level' : 'WARNING',
  2254. },
  2255. }
  2256. # config8a disables existing loggers
  2257. config8a = {
  2258. 'version': 1,
  2259. 'disable_existing_loggers' : True,
  2260. 'formatters': {
  2261. 'form1' : {
  2262. 'format' : '%(levelname)s ++ %(message)s',
  2263. },
  2264. },
  2265. 'handlers' : {
  2266. 'hand1' : {
  2267. 'class' : 'logging.StreamHandler',
  2268. 'formatter' : 'form1',
  2269. 'level' : 'NOTSET',
  2270. 'stream' : 'ext://sys.stdout',
  2271. },
  2272. },
  2273. 'loggers' : {
  2274. 'compiler' : {
  2275. 'level' : 'DEBUG',
  2276. 'handlers' : ['hand1'],
  2277. },
  2278. 'compiler.lexer' : {
  2279. },
  2280. },
  2281. 'root' : {
  2282. 'level' : 'WARNING',
  2283. },
  2284. }
  2285. config9 = {
  2286. 'version': 1,
  2287. 'formatters': {
  2288. 'form1' : {
  2289. 'format' : '%(levelname)s ++ %(message)s',
  2290. },
  2291. },
  2292. 'handlers' : {
  2293. 'hand1' : {
  2294. 'class' : 'logging.StreamHandler',
  2295. 'formatter' : 'form1',
  2296. 'level' : 'WARNING',
  2297. 'stream' : 'ext://sys.stdout',
  2298. },
  2299. },
  2300. 'loggers' : {
  2301. 'compiler.parser' : {
  2302. 'level' : 'WARNING',
  2303. 'handlers' : ['hand1'],
  2304. },
  2305. },
  2306. 'root' : {
  2307. 'level' : 'NOTSET',
  2308. },
  2309. }
  2310. config9a = {
  2311. 'version': 1,
  2312. 'incremental' : True,
  2313. 'handlers' : {
  2314. 'hand1' : {
  2315. 'level' : 'WARNING',
  2316. },
  2317. },
  2318. 'loggers' : {
  2319. 'compiler.parser' : {
  2320. 'level' : 'INFO',
  2321. },
  2322. },
  2323. }
  2324. config9b = {
  2325. 'version': 1,
  2326. 'incremental' : True,
  2327. 'handlers' : {
  2328. 'hand1' : {
  2329. 'level' : 'INFO',
  2330. },
  2331. },
  2332. 'loggers' : {
  2333. 'compiler.parser' : {
  2334. 'level' : 'INFO',
  2335. },
  2336. },
  2337. }
  2338. # As config1 but with a filter added
  2339. config10 = {
  2340. 'version': 1,
  2341. 'formatters': {
  2342. 'form1' : {
  2343. 'format' : '%(levelname)s ++ %(message)s',
  2344. },
  2345. },
  2346. 'filters' : {
  2347. 'filt1' : {
  2348. 'name' : 'compiler.parser',
  2349. },
  2350. },
  2351. 'handlers' : {
  2352. 'hand1' : {
  2353. 'class' : 'logging.StreamHandler',
  2354. 'formatter' : 'form1',
  2355. 'level' : 'NOTSET',
  2356. 'stream' : 'ext://sys.stdout',
  2357. 'filters' : ['filt1'],
  2358. },
  2359. },
  2360. 'loggers' : {
  2361. 'compiler.parser' : {
  2362. 'level' : 'DEBUG',
  2363. 'filters' : ['filt1'],
  2364. },
  2365. },
  2366. 'root' : {
  2367. 'level' : 'WARNING',
  2368. 'handlers' : ['hand1'],
  2369. },
  2370. }
  2371. # As config1 but using cfg:// references
  2372. config11 = {
  2373. 'version': 1,
  2374. 'true_formatters': {
  2375. 'form1' : {
  2376. 'format' : '%(levelname)s ++ %(message)s',
  2377. },
  2378. },
  2379. 'handler_configs': {
  2380. 'hand1' : {
  2381. 'class' : 'logging.StreamHandler',
  2382. 'formatter' : 'form1',
  2383. 'level' : 'NOTSET',
  2384. 'stream' : 'ext://sys.stdout',
  2385. },
  2386. },
  2387. 'formatters' : 'cfg://true_formatters',
  2388. 'handlers' : {
  2389. 'hand1' : 'cfg://handler_configs[hand1]',
  2390. },
  2391. 'loggers' : {
  2392. 'compiler.parser' : {
  2393. 'level' : 'DEBUG',
  2394. 'handlers' : ['hand1'],
  2395. },
  2396. },
  2397. 'root' : {
  2398. 'level' : 'WARNING',
  2399. },
  2400. }
  2401. # As config11 but missing the version key
  2402. config12 = {
  2403. 'true_formatters': {
  2404. 'form1' : {
  2405. 'format' : '%(levelname)s ++ %(message)s',
  2406. },
  2407. },
  2408. 'handler_configs': {
  2409. 'hand1' : {
  2410. 'class' : 'logging.StreamHandler',
  2411. 'formatter' : 'form1',
  2412. 'level' : 'NOTSET',
  2413. 'stream' : 'ext://sys.stdout',
  2414. },
  2415. },
  2416. 'formatters' : 'cfg://true_formatters',
  2417. 'handlers' : {
  2418. 'hand1' : 'cfg://handler_configs[hand1]',
  2419. },
  2420. 'loggers' : {
  2421. 'compiler.parser' : {
  2422. 'level' : 'DEBUG',
  2423. 'handlers' : ['hand1'],
  2424. },
  2425. },
  2426. 'root' : {
  2427. 'level' : 'WARNING',
  2428. },
  2429. }
  2430. # As config11 but using an unsupported version
  2431. config13 = {
  2432. 'version': 2,
  2433. 'true_formatters': {
  2434. 'form1' : {
  2435. 'format' : '%(levelname)s ++ %(message)s',
  2436. },
  2437. },
  2438. 'handler_configs': {
  2439. 'hand1' : {
  2440. 'class' : 'logging.StreamHandler',
  2441. 'formatter' : 'form1',
  2442. 'level' : 'NOTSET',
  2443. 'stream' : 'ext://sys.stdout',
  2444. },
  2445. },
  2446. 'formatters' : 'cfg://true_formatters',
  2447. 'handlers' : {
  2448. 'hand1' : 'cfg://handler_configs[hand1]',
  2449. },
  2450. 'loggers' : {
  2451. 'compiler.parser' : {
  2452. 'level' : 'DEBUG',
  2453. 'handlers' : ['hand1'],
  2454. },
  2455. },
  2456. 'root' : {
  2457. 'level' : 'WARNING',
  2458. },
  2459. }
  2460. # As config0, but with properties
  2461. config14 = {
  2462. 'version': 1,
  2463. 'formatters': {
  2464. 'form1' : {
  2465. 'format' : '%(levelname)s ++ %(message)s',
  2466. },
  2467. },
  2468. 'handlers' : {
  2469. 'hand1' : {
  2470. 'class' : 'logging.StreamHandler',
  2471. 'formatter' : 'form1',
  2472. 'level' : 'NOTSET',
  2473. 'stream' : 'ext://sys.stdout',
  2474. '.': {
  2475. 'foo': 'bar',
  2476. 'terminator': '!\n',
  2477. }
  2478. },
  2479. },
  2480. 'root' : {
  2481. 'level' : 'WARNING',
  2482. 'handlers' : ['hand1'],
  2483. },
  2484. }
  2485. out_of_order = {
  2486. "version": 1,
  2487. "formatters": {
  2488. "mySimpleFormatter": {
  2489. "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
  2490. "style": "$"
  2491. }
  2492. },
  2493. "handlers": {
  2494. "fileGlobal": {
  2495. "class": "logging.StreamHandler",
  2496. "level": "DEBUG",
  2497. "formatter": "mySimpleFormatter"
  2498. },
  2499. "bufferGlobal": {
  2500. "class": "logging.handlers.MemoryHandler",
  2501. "capacity": 5,
  2502. "formatter": "mySimpleFormatter",
  2503. "target": "fileGlobal",
  2504. "level": "DEBUG"
  2505. }
  2506. },
  2507. "loggers": {
  2508. "mymodule": {
  2509. "level": "DEBUG",
  2510. "handlers": ["bufferGlobal"],
  2511. "propagate": "true"
  2512. }
  2513. }
  2514. }
  2515. # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
  2516. custom_formatter_class_validate = {
  2517. 'version': 1,
  2518. 'formatters': {
  2519. 'form1': {
  2520. '()': __name__ + '.ExceptionFormatter',
  2521. 'format': '%(levelname)s:%(name)s:%(message)s',
  2522. 'validate': False,
  2523. },
  2524. },
  2525. 'handlers' : {
  2526. 'hand1' : {
  2527. 'class': 'logging.StreamHandler',
  2528. 'formatter': 'form1',
  2529. 'level': 'NOTSET',
  2530. 'stream': 'ext://sys.stdout',
  2531. },
  2532. },
  2533. "loggers": {
  2534. "my_test_logger_custom_formatter": {
  2535. "level": "DEBUG",
  2536. "handlers": ["hand1"],
  2537. "propagate": "true"
  2538. }
  2539. }
  2540. }
  2541. # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
  2542. custom_formatter_class_validate2 = {
  2543. 'version': 1,
  2544. 'formatters': {
  2545. 'form1': {
  2546. 'class': __name__ + '.ExceptionFormatter',
  2547. 'format': '%(levelname)s:%(name)s:%(message)s',
  2548. 'validate': False,
  2549. },
  2550. },
  2551. 'handlers' : {
  2552. 'hand1' : {
  2553. 'class': 'logging.StreamHandler',
  2554. 'formatter': 'form1',
  2555. 'level': 'NOTSET',
  2556. 'stream': 'ext://sys.stdout',
  2557. },
  2558. },
  2559. "loggers": {
  2560. "my_test_logger_custom_formatter": {
  2561. "level": "DEBUG",
  2562. "handlers": ["hand1"],
  2563. "propagate": "true"
  2564. }
  2565. }
  2566. }
  2567. # Configuration with custom class that is not inherited from logging.Formatter
  2568. custom_formatter_class_validate3 = {
  2569. 'version': 1,
  2570. 'formatters': {
  2571. 'form1': {
  2572. 'class': __name__ + '.myCustomFormatter',
  2573. 'format': '%(levelname)s:%(name)s:%(message)s',
  2574. 'validate': False,
  2575. },
  2576. },
  2577. 'handlers' : {
  2578. 'hand1' : {
  2579. 'class': 'logging.StreamHandler',
  2580. 'formatter': 'form1',
  2581. 'level': 'NOTSET',
  2582. 'stream': 'ext://sys.stdout',
  2583. },
  2584. },
  2585. "loggers": {
  2586. "my_test_logger_custom_formatter": {
  2587. "level": "DEBUG",
  2588. "handlers": ["hand1"],
  2589. "propagate": "true"
  2590. }
  2591. }
  2592. }
  2593. # Configuration with custom function and 'validate' set to False
  2594. custom_formatter_with_function = {
  2595. 'version': 1,
  2596. 'formatters': {
  2597. 'form1': {
  2598. '()': formatFunc,
  2599. 'format': '%(levelname)s:%(name)s:%(message)s',
  2600. 'validate': False,
  2601. },
  2602. },
  2603. 'handlers' : {
  2604. 'hand1' : {
  2605. 'class': 'logging.StreamHandler',
  2606. 'formatter': 'form1',
  2607. 'level': 'NOTSET',
  2608. 'stream': 'ext://sys.stdout',
  2609. },
  2610. },
  2611. "loggers": {
  2612. "my_test_logger_custom_formatter": {
  2613. "level": "DEBUG",
  2614. "handlers": ["hand1"],
  2615. "propagate": "true"
  2616. }
  2617. }
  2618. }
  2619. def apply_config(self, conf):
  2620. logging.config.dictConfig(conf)
  2621. def test_config0_ok(self):
  2622. # A simple config which overrides the default settings.
  2623. with support.captured_stdout() as output:
  2624. self.apply_config(self.config0)
  2625. logger = logging.getLogger()
  2626. # Won't output anything
  2627. logger.info(self.next_message())
  2628. # Outputs a message
  2629. logger.error(self.next_message())
  2630. self.assert_log_lines([
  2631. ('ERROR', '2'),
  2632. ], stream=output)
  2633. # Original logger output is empty.
  2634. self.assert_log_lines([])
  2635. def test_config1_ok(self, config=config1):
  2636. # A config defining a sub-parser as well.
  2637. with support.captured_stdout() as output:
  2638. self.apply_config(config)
  2639. logger = logging.getLogger("compiler.parser")
  2640. # Both will output a message
  2641. logger.info(self.next_message())
  2642. logger.error(self.next_message())
  2643. self.assert_log_lines([
  2644. ('INFO', '1'),
  2645. ('ERROR', '2'),
  2646. ], stream=output)
  2647. # Original logger output is empty.
  2648. self.assert_log_lines([])
  2649. def test_config2_failure(self):
  2650. # A simple config which overrides the default settings.
  2651. self.assertRaises(Exception, self.apply_config, self.config2)
  2652. def test_config2a_failure(self):
  2653. # A simple config which overrides the default settings.
  2654. self.assertRaises(Exception, self.apply_config, self.config2a)
  2655. def test_config2b_failure(self):
  2656. # A simple config which overrides the default settings.
  2657. self.assertRaises(Exception, self.apply_config, self.config2b)
  2658. def test_config3_failure(self):
  2659. # A simple config which overrides the default settings.
  2660. self.assertRaises(Exception, self.apply_config, self.config3)
  2661. def test_config4_ok(self):
  2662. # A config specifying a custom formatter class.
  2663. with support.captured_stdout() as output:
  2664. self.apply_config(self.config4)
  2665. #logger = logging.getLogger()
  2666. try:
  2667. raise RuntimeError()
  2668. except RuntimeError:
  2669. logging.exception("just testing")
  2670. sys.stdout.seek(0)
  2671. self.assertEqual(output.getvalue(),
  2672. "ERROR:root:just testing\nGot a [RuntimeError]\n")
  2673. # Original logger output is empty
  2674. self.assert_log_lines([])
  2675. def test_config4a_ok(self):
  2676. # A config specifying a custom formatter class.
  2677. with support.captured_stdout() as output:
  2678. self.apply_config(self.config4a)
  2679. #logger = logging.getLogger()
  2680. try:
  2681. raise RuntimeError()
  2682. except RuntimeError:
  2683. logging.exception("just testing")
  2684. sys.stdout.seek(0)
  2685. self.assertEqual(output.getvalue(),
  2686. "ERROR:root:just testing\nGot a [RuntimeError]\n")
  2687. # Original logger output is empty
  2688. self.assert_log_lines([])
  2689. def test_config5_ok(self):
  2690. self.test_config1_ok(config=self.config5)
  2691. def test_config6_failure(self):
  2692. self.assertRaises(Exception, self.apply_config, self.config6)
  2693. def test_config7_ok(self):
  2694. with support.captured_stdout() as output:
  2695. self.apply_config(self.config1)
  2696. logger = logging.getLogger("compiler.parser")
  2697. # Both will output a message
  2698. logger.info(self.next_message())
  2699. logger.error(self.next_message())
  2700. self.assert_log_lines([
  2701. ('INFO', '1'),
  2702. ('ERROR', '2'),
  2703. ], stream=output)
  2704. # Original logger output is empty.
  2705. self.assert_log_lines([])
  2706. with support.captured_stdout() as output:
  2707. self.apply_config(self.config7)
  2708. logger = logging.getLogger("compiler.parser")
  2709. self.assertTrue(logger.disabled)
  2710. logger = logging.getLogger("compiler.lexer")
  2711. # Both will output a message
  2712. logger.info(self.next_message())
  2713. logger.error(self.next_message())
  2714. self.assert_log_lines([
  2715. ('INFO', '3'),
  2716. ('ERROR', '4'),
  2717. ], stream=output)
  2718. # Original logger output is empty.
  2719. self.assert_log_lines([])
  2720. # Same as test_config_7_ok but don't disable old loggers.
  2721. def test_config_8_ok(self):
  2722. with support.captured_stdout() as output:
  2723. self.apply_config(self.config1)
  2724. logger = logging.getLogger("compiler.parser")
  2725. # All will output a message
  2726. logger.info(self.next_message())
  2727. logger.error(self.next_message())
  2728. self.assert_log_lines([
  2729. ('INFO', '1'),
  2730. ('ERROR', '2'),
  2731. ], stream=output)
  2732. # Original logger output is empty.
  2733. self.assert_log_lines([])
  2734. with support.captured_stdout() as output:
  2735. self.apply_config(self.config8)
  2736. logger = logging.getLogger("compiler.parser")
  2737. self.assertFalse(logger.disabled)
  2738. # Both will output a message
  2739. logger.info(self.next_message())
  2740. logger.error(self.next_message())
  2741. logger = logging.getLogger("compiler.lexer")
  2742. # Both will output a message
  2743. logger.info(self.next_message())
  2744. logger.error(self.next_message())
  2745. self.assert_log_lines([
  2746. ('INFO', '3'),
  2747. ('ERROR', '4'),
  2748. ('INFO', '5'),
  2749. ('ERROR', '6'),
  2750. ], stream=output)
  2751. # Original logger output is empty.
  2752. self.assert_log_lines([])
  2753. def test_config_8a_ok(self):
  2754. with support.captured_stdout() as output:
  2755. self.apply_config(self.config1a)
  2756. logger = logging.getLogger("compiler.parser")
  2757. # See issue #11424. compiler-hyphenated sorts
  2758. # between compiler and compiler.xyz and this
  2759. # was preventing compiler.xyz from being included
  2760. # in the child loggers of compiler because of an
  2761. # overzealous loop termination condition.
  2762. hyphenated = logging.getLogger('compiler-hyphenated')
  2763. # All will output a message
  2764. logger.info(self.next_message())
  2765. logger.error(self.next_message())
  2766. hyphenated.critical(self.next_message())
  2767. self.assert_log_lines([
  2768. ('INFO', '1'),
  2769. ('ERROR', '2'),
  2770. ('CRITICAL', '3'),
  2771. ], stream=output)
  2772. # Original logger output is empty.
  2773. self.assert_log_lines([])
  2774. with support.captured_stdout() as output:
  2775. self.apply_config(self.config8a)
  2776. logger = logging.getLogger("compiler.parser")
  2777. self.assertFalse(logger.disabled)
  2778. # Both will output a message
  2779. logger.info(self.next_message())
  2780. logger.error(self.next_message())
  2781. logger = logging.getLogger("compiler.lexer")
  2782. # Both will output a message
  2783. logger.info(self.next_message())
  2784. logger.error(self.next_message())
  2785. # Will not appear
  2786. hyphenated.critical(self.next_message())
  2787. self.assert_log_lines([
  2788. ('INFO', '4'),
  2789. ('ERROR', '5'),
  2790. ('INFO', '6'),
  2791. ('ERROR', '7'),
  2792. ], stream=output)
  2793. # Original logger output is empty.
  2794. self.assert_log_lines([])
  2795. def test_config_9_ok(self):
  2796. with support.captured_stdout() as output:
  2797. self.apply_config(self.config9)
  2798. logger = logging.getLogger("compiler.parser")
  2799. # Nothing will be output since both handler and logger are set to WARNING
  2800. logger.info(self.next_message())
  2801. self.assert_log_lines([], stream=output)
  2802. self.apply_config(self.config9a)
  2803. # Nothing will be output since handler is still set to WARNING
  2804. logger.info(self.next_message())
  2805. self.assert_log_lines([], stream=output)
  2806. self.apply_config(self.config9b)
  2807. # Message should now be output
  2808. logger.info(self.next_message())
  2809. self.assert_log_lines([
  2810. ('INFO', '3'),
  2811. ], stream=output)
  2812. def test_config_10_ok(self):
  2813. with support.captured_stdout() as output:
  2814. self.apply_config(self.config10)
  2815. logger = logging.getLogger("compiler.parser")
  2816. logger.warning(self.next_message())
  2817. logger = logging.getLogger('compiler')
  2818. # Not output, because filtered
  2819. logger.warning(self.next_message())
  2820. logger = logging.getLogger('compiler.lexer')
  2821. # Not output, because filtered
  2822. logger.warning(self.next_message())
  2823. logger = logging.getLogger("compiler.parser.codegen")
  2824. # Output, as not filtered
  2825. logger.error(self.next_message())
  2826. self.assert_log_lines([
  2827. ('WARNING', '1'),
  2828. ('ERROR', '4'),
  2829. ], stream=output)
  2830. def test_config11_ok(self):
  2831. self.test_config1_ok(self.config11)
  2832. def test_config12_failure(self):
  2833. self.assertRaises(Exception, self.apply_config, self.config12)
  2834. def test_config13_failure(self):
  2835. self.assertRaises(Exception, self.apply_config, self.config13)
  2836. def test_config14_ok(self):
  2837. with support.captured_stdout() as output:
  2838. self.apply_config(self.config14)
  2839. h = logging._handlers['hand1']
  2840. self.assertEqual(h.foo, 'bar')
  2841. self.assertEqual(h.terminator, '!\n')
  2842. logging.warning('Exclamation')
  2843. self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
  2844. def test_config15_ok(self):
  2845. def cleanup(h1, fn):
  2846. h1.close()
  2847. os.remove(fn)
  2848. with self.check_no_resource_warning():
  2849. fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
  2850. os.close(fd)
  2851. config = {
  2852. "version": 1,
  2853. "handlers": {
  2854. "file": {
  2855. "class": "logging.FileHandler",
  2856. "filename": fn,
  2857. "encoding": "utf-8",
  2858. }
  2859. },
  2860. "root": {
  2861. "handlers": ["file"]
  2862. }
  2863. }
  2864. self.apply_config(config)
  2865. self.apply_config(config)
  2866. handler = logging.root.handlers[0]
  2867. self.addCleanup(cleanup, handler, fn)
  2868. def setup_via_listener(self, text, verify=None):
  2869. text = text.encode("utf-8")
  2870. # Ask for a randomly assigned port (by using port 0)
  2871. t = logging.config.listen(0, verify)
  2872. t.start()
  2873. t.ready.wait()
  2874. # Now get the port allocated
  2875. port = t.port
  2876. t.ready.clear()
  2877. try:
  2878. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  2879. sock.settimeout(2.0)
  2880. sock.connect(('localhost', port))
  2881. slen = struct.pack('>L', len(text))
  2882. s = slen + text
  2883. sentsofar = 0
  2884. left = len(s)
  2885. while left > 0:
  2886. sent = sock.send(s[sentsofar:])
  2887. sentsofar += sent
  2888. left -= sent
  2889. sock.close()
  2890. finally:
  2891. t.ready.wait(2.0)
  2892. logging.config.stopListening()
  2893. threading_helper.join_thread(t)
  2894. @support.requires_working_socket()
  2895. def test_listen_config_10_ok(self):
  2896. with support.captured_stdout() as output:
  2897. self.setup_via_listener(json.dumps(self.config10))
  2898. logger = logging.getLogger("compiler.parser")
  2899. logger.warning(self.next_message())
  2900. logger = logging.getLogger('compiler')
  2901. # Not output, because filtered
  2902. logger.warning(self.next_message())
  2903. logger = logging.getLogger('compiler.lexer')
  2904. # Not output, because filtered
  2905. logger.warning(self.next_message())
  2906. logger = logging.getLogger("compiler.parser.codegen")
  2907. # Output, as not filtered
  2908. logger.error(self.next_message())
  2909. self.assert_log_lines([
  2910. ('WARNING', '1'),
  2911. ('ERROR', '4'),
  2912. ], stream=output)
  2913. @support.requires_working_socket()
  2914. def test_listen_config_1_ok(self):
  2915. with support.captured_stdout() as output:
  2916. self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
  2917. logger = logging.getLogger("compiler.parser")
  2918. # Both will output a message
  2919. logger.info(self.next_message())
  2920. logger.error(self.next_message())
  2921. self.assert_log_lines([
  2922. ('INFO', '1'),
  2923. ('ERROR', '2'),
  2924. ], stream=output)
  2925. # Original logger output is empty.
  2926. self.assert_log_lines([])
  2927. @support.requires_working_socket()
  2928. def test_listen_verify(self):
  2929. def verify_fail(stuff):
  2930. return None
  2931. def verify_reverse(stuff):
  2932. return stuff[::-1]
  2933. logger = logging.getLogger("compiler.parser")
  2934. to_send = textwrap.dedent(ConfigFileTest.config1)
  2935. # First, specify a verification function that will fail.
  2936. # We expect to see no output, since our configuration
  2937. # never took effect.
  2938. with support.captured_stdout() as output:
  2939. self.setup_via_listener(to_send, verify_fail)
  2940. # Both will output a message
  2941. logger.info(self.next_message())
  2942. logger.error(self.next_message())
  2943. self.assert_log_lines([], stream=output)
  2944. # Original logger output has the stuff we logged.
  2945. self.assert_log_lines([
  2946. ('INFO', '1'),
  2947. ('ERROR', '2'),
  2948. ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
  2949. # Now, perform no verification. Our configuration
  2950. # should take effect.
  2951. with support.captured_stdout() as output:
  2952. self.setup_via_listener(to_send) # no verify callable specified
  2953. logger = logging.getLogger("compiler.parser")
  2954. # Both will output a message
  2955. logger.info(self.next_message())
  2956. logger.error(self.next_message())
  2957. self.assert_log_lines([
  2958. ('INFO', '3'),
  2959. ('ERROR', '4'),
  2960. ], stream=output)
  2961. # Original logger output still has the stuff we logged before.
  2962. self.assert_log_lines([
  2963. ('INFO', '1'),
  2964. ('ERROR', '2'),
  2965. ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
  2966. # Now, perform verification which transforms the bytes.
  2967. with support.captured_stdout() as output:
  2968. self.setup_via_listener(to_send[::-1], verify_reverse)
  2969. logger = logging.getLogger("compiler.parser")
  2970. # Both will output a message
  2971. logger.info(self.next_message())
  2972. logger.error(self.next_message())
  2973. self.assert_log_lines([
  2974. ('INFO', '5'),
  2975. ('ERROR', '6'),
  2976. ], stream=output)
  2977. # Original logger output still has the stuff we logged before.
  2978. self.assert_log_lines([
  2979. ('INFO', '1'),
  2980. ('ERROR', '2'),
  2981. ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
  2982. def test_out_of_order(self):
  2983. self.assertRaises(ValueError, self.apply_config, self.out_of_order)
  2984. def test_out_of_order_with_dollar_style(self):
  2985. config = copy.deepcopy(self.out_of_order)
  2986. config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
  2987. self.apply_config(config)
  2988. handler = logging.getLogger('mymodule').handlers[0]
  2989. self.assertIsInstance(handler.target, logging.Handler)
  2990. self.assertIsInstance(handler.formatter._style,
  2991. logging.StringTemplateStyle)
  2992. def test_custom_formatter_class_with_validate(self):
  2993. self.apply_config(self.custom_formatter_class_validate)
  2994. handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
  2995. self.assertIsInstance(handler.formatter, ExceptionFormatter)
  2996. def test_custom_formatter_class_with_validate2(self):
  2997. self.apply_config(self.custom_formatter_class_validate2)
  2998. handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
  2999. self.assertIsInstance(handler.formatter, ExceptionFormatter)
  3000. def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
  3001. config = self.custom_formatter_class_validate.copy()
  3002. config['formatters']['form1']['style'] = "$"
  3003. # Exception should not be raise as we have configured 'validate' to False
  3004. self.apply_config(config)
  3005. handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
  3006. self.assertIsInstance(handler.formatter, ExceptionFormatter)
  3007. def test_custom_formatter_class_with_validate3(self):
  3008. self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
  3009. def test_custom_formatter_function_with_validate(self):
  3010. self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
  3011. def test_baseconfig(self):
  3012. d = {
  3013. 'atuple': (1, 2, 3),
  3014. 'alist': ['a', 'b', 'c'],
  3015. 'adict': {'d': 'e', 'f': 3 },
  3016. 'nest1': ('g', ('h', 'i'), 'j'),
  3017. 'nest2': ['k', ['l', 'm'], 'n'],
  3018. 'nest3': ['o', 'cfg://alist', 'p'],
  3019. }
  3020. bc = logging.config.BaseConfigurator(d)
  3021. self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
  3022. self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
  3023. self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
  3024. self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
  3025. self.assertEqual(bc.convert('cfg://adict.d'), 'e')
  3026. self.assertEqual(bc.convert('cfg://adict[f]'), 3)
  3027. v = bc.convert('cfg://nest3')
  3028. self.assertEqual(v.pop(1), ['a', 'b', 'c'])
  3029. self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
  3030. self.assertRaises(ValueError, bc.convert, 'cfg://!')
  3031. self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
  3032. def test_namedtuple(self):
  3033. # see bpo-39142
  3034. from collections import namedtuple
  3035. class MyHandler(logging.StreamHandler):
  3036. def __init__(self, resource, *args, **kwargs):
  3037. super().__init__(*args, **kwargs)
  3038. self.resource: namedtuple = resource
  3039. def emit(self, record):
  3040. record.msg += f' {self.resource.type}'
  3041. return super().emit(record)
  3042. Resource = namedtuple('Resource', ['type', 'labels'])
  3043. resource = Resource(type='my_type', labels=['a'])
  3044. config = {
  3045. 'version': 1,
  3046. 'handlers': {
  3047. 'myhandler': {
  3048. '()': MyHandler,
  3049. 'resource': resource
  3050. }
  3051. },
  3052. 'root': {'level': 'INFO', 'handlers': ['myhandler']},
  3053. }
  3054. with support.captured_stderr() as stderr:
  3055. self.apply_config(config)
  3056. logging.info('some log')
  3057. self.assertEqual(stderr.getvalue(), 'some log my_type\n')
  3058. def test_config_callable_filter_works(self):
  3059. def filter_(_):
  3060. return 1
  3061. self.apply_config({
  3062. "version": 1, "root": {"level": "DEBUG", "filters": [filter_]}
  3063. })
  3064. assert logging.getLogger().filters[0] is filter_
  3065. logging.getLogger().filters = []
  3066. def test_config_filter_works(self):
  3067. filter_ = logging.Filter("spam.eggs")
  3068. self.apply_config({
  3069. "version": 1, "root": {"level": "DEBUG", "filters": [filter_]}
  3070. })
  3071. assert logging.getLogger().filters[0] is filter_
  3072. logging.getLogger().filters = []
  3073. def test_config_filter_method_works(self):
  3074. class FakeFilter:
  3075. def filter(self, _):
  3076. return 1
  3077. filter_ = FakeFilter()
  3078. self.apply_config({
  3079. "version": 1, "root": {"level": "DEBUG", "filters": [filter_]}
  3080. })
  3081. assert logging.getLogger().filters[0] is filter_
  3082. logging.getLogger().filters = []
  3083. def test_invalid_type_raises(self):
  3084. class NotAFilter: pass
  3085. for filter_ in [None, 1, NotAFilter()]:
  3086. self.assertRaises(
  3087. ValueError,
  3088. self.apply_config,
  3089. {"version": 1, "root": {"level": "DEBUG", "filters": [filter_]}}
  3090. )
  3091. def test_90195(self):
  3092. # See gh-90195
  3093. config = {
  3094. 'version': 1,
  3095. 'disable_existing_loggers': False,
  3096. 'handlers': {
  3097. 'console': {
  3098. 'level': 'DEBUG',
  3099. 'class': 'logging.StreamHandler',
  3100. },
  3101. },
  3102. 'loggers': {
  3103. 'a': {
  3104. 'level': 'DEBUG',
  3105. 'handlers': ['console']
  3106. }
  3107. }
  3108. }
  3109. logger = logging.getLogger('a')
  3110. self.assertFalse(logger.disabled)
  3111. self.apply_config(config)
  3112. self.assertFalse(logger.disabled)
  3113. # Should disable all loggers ...
  3114. self.apply_config({'version': 1})
  3115. self.assertTrue(logger.disabled)
  3116. del config['disable_existing_loggers']
  3117. self.apply_config(config)
  3118. # Logger should be enabled, since explicitly mentioned
  3119. self.assertFalse(logger.disabled)
  3120. class ManagerTest(BaseTest):
  3121. def test_manager_loggerclass(self):
  3122. logged = []
  3123. class MyLogger(logging.Logger):
  3124. def _log(self, level, msg, args, exc_info=None, extra=None):
  3125. logged.append(msg)
  3126. man = logging.Manager(None)
  3127. self.assertRaises(TypeError, man.setLoggerClass, int)
  3128. man.setLoggerClass(MyLogger)
  3129. logger = man.getLogger('test')
  3130. logger.warning('should appear in logged')
  3131. logging.warning('should not appear in logged')
  3132. self.assertEqual(logged, ['should appear in logged'])
  3133. def test_set_log_record_factory(self):
  3134. man = logging.Manager(None)
  3135. expected = object()
  3136. man.setLogRecordFactory(expected)
  3137. self.assertEqual(man.logRecordFactory, expected)
  3138. class ChildLoggerTest(BaseTest):
  3139. def test_child_loggers(self):
  3140. r = logging.getLogger()
  3141. l1 = logging.getLogger('abc')
  3142. l2 = logging.getLogger('def.ghi')
  3143. c1 = r.getChild('xyz')
  3144. c2 = r.getChild('uvw.xyz')
  3145. self.assertIs(c1, logging.getLogger('xyz'))
  3146. self.assertIs(c2, logging.getLogger('uvw.xyz'))
  3147. c1 = l1.getChild('def')
  3148. c2 = c1.getChild('ghi')
  3149. c3 = l1.getChild('def.ghi')
  3150. self.assertIs(c1, logging.getLogger('abc.def'))
  3151. self.assertIs(c2, logging.getLogger('abc.def.ghi'))
  3152. self.assertIs(c2, c3)
  3153. class DerivedLogRecord(logging.LogRecord):
  3154. pass
  3155. class LogRecordFactoryTest(BaseTest):
  3156. def setUp(self):
  3157. class CheckingFilter(logging.Filter):
  3158. def __init__(self, cls):
  3159. self.cls = cls
  3160. def filter(self, record):
  3161. t = type(record)
  3162. if t is not self.cls:
  3163. msg = 'Unexpected LogRecord type %s, expected %s' % (t,
  3164. self.cls)
  3165. raise TypeError(msg)
  3166. return True
  3167. BaseTest.setUp(self)
  3168. self.filter = CheckingFilter(DerivedLogRecord)
  3169. self.root_logger.addFilter(self.filter)
  3170. self.orig_factory = logging.getLogRecordFactory()
  3171. def tearDown(self):
  3172. self.root_logger.removeFilter(self.filter)
  3173. BaseTest.tearDown(self)
  3174. logging.setLogRecordFactory(self.orig_factory)
  3175. def test_logrecord_class(self):
  3176. self.assertRaises(TypeError, self.root_logger.warning,
  3177. self.next_message())
  3178. logging.setLogRecordFactory(DerivedLogRecord)
  3179. self.root_logger.error(self.next_message())
  3180. self.assert_log_lines([
  3181. ('root', 'ERROR', '2'),
  3182. ])
  3183. @threading_helper.requires_working_threading()
  3184. class QueueHandlerTest(BaseTest):
  3185. # Do not bother with a logger name group.
  3186. expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
  3187. def setUp(self):
  3188. BaseTest.setUp(self)
  3189. self.queue = queue.Queue(-1)
  3190. self.que_hdlr = logging.handlers.QueueHandler(self.queue)
  3191. self.name = 'que'
  3192. self.que_logger = logging.getLogger('que')
  3193. self.que_logger.propagate = False
  3194. self.que_logger.setLevel(logging.WARNING)
  3195. self.que_logger.addHandler(self.que_hdlr)
  3196. def tearDown(self):
  3197. self.que_hdlr.close()
  3198. BaseTest.tearDown(self)
  3199. def test_queue_handler(self):
  3200. self.que_logger.debug(self.next_message())
  3201. self.assertRaises(queue.Empty, self.queue.get_nowait)
  3202. self.que_logger.info(self.next_message())
  3203. self.assertRaises(queue.Empty, self.queue.get_nowait)
  3204. msg = self.next_message()
  3205. self.que_logger.warning(msg)
  3206. data = self.queue.get_nowait()
  3207. self.assertTrue(isinstance(data, logging.LogRecord))
  3208. self.assertEqual(data.name, self.que_logger.name)
  3209. self.assertEqual((data.msg, data.args), (msg, None))
  3210. def test_formatting(self):
  3211. msg = self.next_message()
  3212. levelname = logging.getLevelName(logging.WARNING)
  3213. log_format_str = '{name} -> {levelname}: {message}'
  3214. formatted_msg = log_format_str.format(name=self.name,
  3215. levelname=levelname, message=msg)
  3216. formatter = logging.Formatter(self.log_format)
  3217. self.que_hdlr.setFormatter(formatter)
  3218. self.que_logger.warning(msg)
  3219. log_record = self.queue.get_nowait()
  3220. self.assertEqual(formatted_msg, log_record.msg)
  3221. self.assertEqual(formatted_msg, log_record.message)
  3222. @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
  3223. 'logging.handlers.QueueListener required for this test')
  3224. def test_queue_listener(self):
  3225. handler = TestHandler(support.Matcher())
  3226. listener = logging.handlers.QueueListener(self.queue, handler)
  3227. listener.start()
  3228. try:
  3229. self.que_logger.warning(self.next_message())
  3230. self.que_logger.error(self.next_message())
  3231. self.que_logger.critical(self.next_message())
  3232. finally:
  3233. listener.stop()
  3234. self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
  3235. self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
  3236. self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
  3237. handler.close()
  3238. # Now test with respect_handler_level set
  3239. handler = TestHandler(support.Matcher())
  3240. handler.setLevel(logging.CRITICAL)
  3241. listener = logging.handlers.QueueListener(self.queue, handler,
  3242. respect_handler_level=True)
  3243. listener.start()
  3244. try:
  3245. self.que_logger.warning(self.next_message())
  3246. self.que_logger.error(self.next_message())
  3247. self.que_logger.critical(self.next_message())
  3248. finally:
  3249. listener.stop()
  3250. self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
  3251. self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
  3252. self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
  3253. handler.close()
  3254. @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
  3255. 'logging.handlers.QueueListener required for this test')
  3256. def test_queue_listener_with_StreamHandler(self):
  3257. # Test that traceback and stack-info only appends once (bpo-34334, bpo-46755).
  3258. listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
  3259. listener.start()
  3260. try:
  3261. 1 / 0
  3262. except ZeroDivisionError as e:
  3263. exc = e
  3264. self.que_logger.exception(self.next_message(), exc_info=exc)
  3265. self.que_logger.error(self.next_message(), stack_info=True)
  3266. listener.stop()
  3267. self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
  3268. self.assertEqual(self.stream.getvalue().strip().count('Stack'), 1)
  3269. @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
  3270. 'logging.handlers.QueueListener required for this test')
  3271. def test_queue_listener_with_multiple_handlers(self):
  3272. # Test that queue handler format doesn't affect other handler formats (bpo-35726).
  3273. self.que_hdlr.setFormatter(self.root_formatter)
  3274. self.que_logger.addHandler(self.root_hdlr)
  3275. listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
  3276. listener.start()
  3277. self.que_logger.error("error")
  3278. listener.stop()
  3279. self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
  3280. if hasattr(logging.handlers, 'QueueListener'):
  3281. import multiprocessing
  3282. from unittest.mock import patch
  3283. @threading_helper.requires_working_threading()
  3284. class QueueListenerTest(BaseTest):
  3285. """
  3286. Tests based on patch submitted for issue #27930. Ensure that
  3287. QueueListener handles all log messages.
  3288. """
  3289. repeat = 20
  3290. @staticmethod
  3291. def setup_and_log(log_queue, ident):
  3292. """
  3293. Creates a logger with a QueueHandler that logs to a queue read by a
  3294. QueueListener. Starts the listener, logs five messages, and stops
  3295. the listener.
  3296. """
  3297. logger = logging.getLogger('test_logger_with_id_%s' % ident)
  3298. logger.setLevel(logging.DEBUG)
  3299. handler = logging.handlers.QueueHandler(log_queue)
  3300. logger.addHandler(handler)
  3301. listener = logging.handlers.QueueListener(log_queue)
  3302. listener.start()
  3303. logger.info('one')
  3304. logger.info('two')
  3305. logger.info('three')
  3306. logger.info('four')
  3307. logger.info('five')
  3308. listener.stop()
  3309. logger.removeHandler(handler)
  3310. handler.close()
  3311. @patch.object(logging.handlers.QueueListener, 'handle')
  3312. def test_handle_called_with_queue_queue(self, mock_handle):
  3313. for i in range(self.repeat):
  3314. log_queue = queue.Queue()
  3315. self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
  3316. self.assertEqual(mock_handle.call_count, 5 * self.repeat,
  3317. 'correct number of handled log messages')
  3318. @patch.object(logging.handlers.QueueListener, 'handle')
  3319. def test_handle_called_with_mp_queue(self, mock_handle):
  3320. # bpo-28668: The multiprocessing (mp) module is not functional
  3321. # when the mp.synchronize module cannot be imported.
  3322. support.skip_if_broken_multiprocessing_synchronize()
  3323. for i in range(self.repeat):
  3324. log_queue = multiprocessing.Queue()
  3325. self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
  3326. log_queue.close()
  3327. log_queue.join_thread()
  3328. self.assertEqual(mock_handle.call_count, 5 * self.repeat,
  3329. 'correct number of handled log messages')
  3330. @staticmethod
  3331. def get_all_from_queue(log_queue):
  3332. try:
  3333. while True:
  3334. yield log_queue.get_nowait()
  3335. except queue.Empty:
  3336. return []
  3337. def test_no_messages_in_queue_after_stop(self):
  3338. """
  3339. Five messages are logged then the QueueListener is stopped. This
  3340. test then gets everything off the queue. Failure of this test
  3341. indicates that messages were not registered on the queue until
  3342. _after_ the QueueListener stopped.
  3343. """
  3344. # bpo-28668: The multiprocessing (mp) module is not functional
  3345. # when the mp.synchronize module cannot be imported.
  3346. support.skip_if_broken_multiprocessing_synchronize()
  3347. for i in range(self.repeat):
  3348. queue = multiprocessing.Queue()
  3349. self.setup_and_log(queue, '%s_%s' %(self.id(), i))
  3350. # time.sleep(1)
  3351. items = list(self.get_all_from_queue(queue))
  3352. queue.close()
  3353. queue.join_thread()
  3354. expected = [[], [logging.handlers.QueueListener._sentinel]]
  3355. self.assertIn(items, expected,
  3356. 'Found unexpected messages in queue: %s' % (
  3357. [m.msg if isinstance(m, logging.LogRecord)
  3358. else m for m in items]))
  3359. def test_calls_task_done_after_stop(self):
  3360. # Issue 36813: Make sure queue.join does not deadlock.
  3361. log_queue = queue.Queue()
  3362. listener = logging.handlers.QueueListener(log_queue)
  3363. listener.start()
  3364. listener.stop()
  3365. with self.assertRaises(ValueError):
  3366. # Make sure all tasks are done and .join won't block.
  3367. log_queue.task_done()
  3368. ZERO = datetime.timedelta(0)
  3369. class UTC(datetime.tzinfo):
  3370. def utcoffset(self, dt):
  3371. return ZERO
  3372. dst = utcoffset
  3373. def tzname(self, dt):
  3374. return 'UTC'
  3375. utc = UTC()
  3376. class AssertErrorMessage:
  3377. def assert_error_message(self, exception, message, *args, **kwargs):
  3378. try:
  3379. self.assertRaises((), *args, **kwargs)
  3380. except exception as e:
  3381. self.assertEqual(message, str(e))
  3382. class FormatterTest(unittest.TestCase, AssertErrorMessage):
  3383. def setUp(self):
  3384. self.common = {
  3385. 'name': 'formatter.test',
  3386. 'level': logging.DEBUG,
  3387. 'pathname': os.path.join('path', 'to', 'dummy.ext'),
  3388. 'lineno': 42,
  3389. 'exc_info': None,
  3390. 'func': None,
  3391. 'msg': 'Message with %d %s',
  3392. 'args': (2, 'placeholders'),
  3393. }
  3394. self.variants = {
  3395. 'custom': {
  3396. 'custom': 1234
  3397. }
  3398. }
  3399. def get_record(self, name=None):
  3400. result = dict(self.common)
  3401. if name is not None:
  3402. result.update(self.variants[name])
  3403. return logging.makeLogRecord(result)
  3404. def test_percent(self):
  3405. # Test %-formatting
  3406. r = self.get_record()
  3407. f = logging.Formatter('${%(message)s}')
  3408. self.assertEqual(f.format(r), '${Message with 2 placeholders}')
  3409. f = logging.Formatter('%(random)s')
  3410. self.assertRaises(ValueError, f.format, r)
  3411. self.assertFalse(f.usesTime())
  3412. f = logging.Formatter('%(asctime)s')
  3413. self.assertTrue(f.usesTime())
  3414. f = logging.Formatter('%(asctime)-15s')
  3415. self.assertTrue(f.usesTime())
  3416. f = logging.Formatter('%(asctime)#15s')
  3417. self.assertTrue(f.usesTime())
  3418. def test_braces(self):
  3419. # Test {}-formatting
  3420. r = self.get_record()
  3421. f = logging.Formatter('$%{message}%$', style='{')
  3422. self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
  3423. f = logging.Formatter('{random}', style='{')
  3424. self.assertRaises(ValueError, f.format, r)
  3425. f = logging.Formatter("{message}", style='{')
  3426. self.assertFalse(f.usesTime())
  3427. f = logging.Formatter('{asctime}', style='{')
  3428. self.assertTrue(f.usesTime())
  3429. f = logging.Formatter('{asctime!s:15}', style='{')
  3430. self.assertTrue(f.usesTime())
  3431. f = logging.Formatter('{asctime:15}', style='{')
  3432. self.assertTrue(f.usesTime())
  3433. def test_dollars(self):
  3434. # Test $-formatting
  3435. r = self.get_record()
  3436. f = logging.Formatter('${message}', style='$')
  3437. self.assertEqual(f.format(r), 'Message with 2 placeholders')
  3438. f = logging.Formatter('$message', style='$')
  3439. self.assertEqual(f.format(r), 'Message with 2 placeholders')
  3440. f = logging.Formatter('$$%${message}%$$', style='$')
  3441. self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
  3442. f = logging.Formatter('${random}', style='$')
  3443. self.assertRaises(ValueError, f.format, r)
  3444. self.assertFalse(f.usesTime())
  3445. f = logging.Formatter('${asctime}', style='$')
  3446. self.assertTrue(f.usesTime())
  3447. f = logging.Formatter('$asctime', style='$')
  3448. self.assertTrue(f.usesTime())
  3449. f = logging.Formatter('${message}', style='$')
  3450. self.assertFalse(f.usesTime())
  3451. f = logging.Formatter('${asctime}--', style='$')
  3452. self.assertTrue(f.usesTime())
  3453. def test_format_validate(self):
  3454. # Check correct formatting
  3455. # Percentage style
  3456. f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
  3457. self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
  3458. f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
  3459. self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
  3460. f = logging.Formatter("%(process)#+027.23X")
  3461. self.assertEqual(f._fmt, "%(process)#+027.23X")
  3462. f = logging.Formatter("%(foo)#.*g")
  3463. self.assertEqual(f._fmt, "%(foo)#.*g")
  3464. # StrFormat Style
  3465. f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
  3466. self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
  3467. f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
  3468. self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
  3469. f = logging.Formatter("{customfield!s:#<30}", style="{")
  3470. self.assertEqual(f._fmt, "{customfield!s:#<30}")
  3471. f = logging.Formatter("{message!r}", style="{")
  3472. self.assertEqual(f._fmt, "{message!r}")
  3473. f = logging.Formatter("{message!s}", style="{")
  3474. self.assertEqual(f._fmt, "{message!s}")
  3475. f = logging.Formatter("{message!a}", style="{")
  3476. self.assertEqual(f._fmt, "{message!a}")
  3477. f = logging.Formatter("{process!r:4.2}", style="{")
  3478. self.assertEqual(f._fmt, "{process!r:4.2}")
  3479. f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
  3480. self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
  3481. f = logging.Formatter("{process!s:{w},.{p}}", style="{")
  3482. self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
  3483. f = logging.Formatter("{foo:12.{p}}", style="{")
  3484. self.assertEqual(f._fmt, "{foo:12.{p}}")
  3485. f = logging.Formatter("{foo:{w}.6}", style="{")
  3486. self.assertEqual(f._fmt, "{foo:{w}.6}")
  3487. f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
  3488. self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
  3489. f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
  3490. self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
  3491. f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
  3492. self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
  3493. # Dollar style
  3494. f = logging.Formatter("${asctime} - $message", style="$")
  3495. self.assertEqual(f._fmt, "${asctime} - $message")
  3496. f = logging.Formatter("$bar $$", style="$")
  3497. self.assertEqual(f._fmt, "$bar $$")
  3498. f = logging.Formatter("$bar $$$$", style="$")
  3499. self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
  3500. # Testing when ValueError being raised from incorrect format
  3501. # Percentage Style
  3502. self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
  3503. self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
  3504. self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
  3505. self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
  3506. self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
  3507. self.assertRaises(ValueError, logging.Formatter, '{asctime}')
  3508. self.assertRaises(ValueError, logging.Formatter, '${message}')
  3509. self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
  3510. self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
  3511. # StrFormat Style
  3512. # Testing failure for '-' in field name
  3513. self.assert_error_message(
  3514. ValueError,
  3515. "invalid format: invalid field name/expression: 'name-thing'",
  3516. logging.Formatter, "{name-thing}", style="{"
  3517. )
  3518. # Testing failure for style mismatch
  3519. self.assert_error_message(
  3520. ValueError,
  3521. "invalid format: no fields",
  3522. logging.Formatter, '%(asctime)s', style='{'
  3523. )
  3524. # Testing failure for invalid conversion
  3525. self.assert_error_message(
  3526. ValueError,
  3527. "invalid conversion: 'Z'"
  3528. )
  3529. self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
  3530. self.assert_error_message(
  3531. ValueError,
  3532. "invalid format: expected ':' after conversion specifier",
  3533. logging.Formatter, '{asctime!aa:15}', style='{'
  3534. )
  3535. # Testing failure for invalid spec
  3536. self.assert_error_message(
  3537. ValueError,
  3538. "invalid format: bad specifier: '.2ff'",
  3539. logging.Formatter, '{process:.2ff}', style='{'
  3540. )
  3541. self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
  3542. self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
  3543. self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
  3544. self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
  3545. # Testing failure for mismatch braces
  3546. self.assert_error_message(
  3547. ValueError,
  3548. "invalid format: expected '}' before end of string",
  3549. logging.Formatter, '{process', style='{'
  3550. )
  3551. self.assert_error_message(
  3552. ValueError,
  3553. "invalid format: Single '}' encountered in format string",
  3554. logging.Formatter, 'process}', style='{'
  3555. )
  3556. self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
  3557. self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
  3558. self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
  3559. self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
  3560. self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
  3561. self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
  3562. self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
  3563. self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
  3564. self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
  3565. # Dollar style
  3566. # Testing failure for mismatch bare $
  3567. self.assert_error_message(
  3568. ValueError,
  3569. "invalid format: bare \'$\' not allowed",
  3570. logging.Formatter, '$bar $$$', style='$'
  3571. )
  3572. self.assert_error_message(
  3573. ValueError,
  3574. "invalid format: bare \'$\' not allowed",
  3575. logging.Formatter, 'bar $', style='$'
  3576. )
  3577. self.assert_error_message(
  3578. ValueError,
  3579. "invalid format: bare \'$\' not allowed",
  3580. logging.Formatter, 'foo $.', style='$'
  3581. )
  3582. # Testing failure for mismatch style
  3583. self.assert_error_message(
  3584. ValueError,
  3585. "invalid format: no fields",
  3586. logging.Formatter, '{asctime}', style='$'
  3587. )
  3588. self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
  3589. # Testing failure for incorrect fields
  3590. self.assert_error_message(
  3591. ValueError,
  3592. "invalid format: no fields",
  3593. logging.Formatter, 'foo', style='$'
  3594. )
  3595. self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
  3596. def test_defaults_parameter(self):
  3597. fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message']
  3598. styles = ['%', '{', '$']
  3599. for fmt, style in zip(fmts, styles):
  3600. f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'})
  3601. r = self.get_record()
  3602. self.assertEqual(f.format(r), 'Default Message with 2 placeholders')
  3603. r = self.get_record("custom")
  3604. self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
  3605. # Without default
  3606. f = logging.Formatter(fmt, style=style)
  3607. r = self.get_record()
  3608. self.assertRaises(ValueError, f.format, r)
  3609. # Non-existing default is ignored
  3610. f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'})
  3611. r = self.get_record("custom")
  3612. self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
  3613. def test_invalid_style(self):
  3614. self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
  3615. def test_time(self):
  3616. r = self.get_record()
  3617. dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
  3618. # We use None to indicate we want the local timezone
  3619. # We're essentially converting a UTC time to local time
  3620. r.created = time.mktime(dt.astimezone(None).timetuple())
  3621. r.msecs = 123
  3622. f = logging.Formatter('%(asctime)s %(message)s')
  3623. f.converter = time.gmtime
  3624. self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
  3625. self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
  3626. f.format(r)
  3627. self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
  3628. def test_default_msec_format_none(self):
  3629. class NoMsecFormatter(logging.Formatter):
  3630. default_msec_format = None
  3631. default_time_format = '%d/%m/%Y %H:%M:%S'
  3632. r = self.get_record()
  3633. dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
  3634. r.created = time.mktime(dt.astimezone(None).timetuple())
  3635. f = NoMsecFormatter()
  3636. f.converter = time.gmtime
  3637. self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
  3638. def test_issue_89047(self):
  3639. f = logging.Formatter(fmt='{asctime}.{msecs:03.0f} {message}', style='{', datefmt="%Y-%m-%d %H:%M:%S")
  3640. for i in range(2500):
  3641. time.sleep(0.0004)
  3642. r = logging.makeLogRecord({'msg': 'Message %d' % (i + 1)})
  3643. s = f.format(r)
  3644. self.assertNotIn('.1000', s)
  3645. class TestBufferingFormatter(logging.BufferingFormatter):
  3646. def formatHeader(self, records):
  3647. return '[(%d)' % len(records)
  3648. def formatFooter(self, records):
  3649. return '(%d)]' % len(records)
  3650. class BufferingFormatterTest(unittest.TestCase):
  3651. def setUp(self):
  3652. self.records = [
  3653. logging.makeLogRecord({'msg': 'one'}),
  3654. logging.makeLogRecord({'msg': 'two'}),
  3655. ]
  3656. def test_default(self):
  3657. f = logging.BufferingFormatter()
  3658. self.assertEqual('', f.format([]))
  3659. self.assertEqual('onetwo', f.format(self.records))
  3660. def test_custom(self):
  3661. f = TestBufferingFormatter()
  3662. self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
  3663. lf = logging.Formatter('<%(message)s>')
  3664. f = TestBufferingFormatter(lf)
  3665. self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
  3666. class ExceptionTest(BaseTest):
  3667. def test_formatting(self):
  3668. r = self.root_logger
  3669. h = RecordingHandler()
  3670. r.addHandler(h)
  3671. try:
  3672. raise RuntimeError('deliberate mistake')
  3673. except:
  3674. logging.exception('failed', stack_info=True)
  3675. r.removeHandler(h)
  3676. h.close()
  3677. r = h.records[0]
  3678. self.assertTrue(r.exc_text.startswith('Traceback (most recent '
  3679. 'call last):\n'))
  3680. self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
  3681. 'deliberate mistake'))
  3682. self.assertTrue(r.stack_info.startswith('Stack (most recent '
  3683. 'call last):\n'))
  3684. self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
  3685. 'stack_info=True)'))
  3686. class LastResortTest(BaseTest):
  3687. def test_last_resort(self):
  3688. # Test the last resort handler
  3689. root = self.root_logger
  3690. root.removeHandler(self.root_hdlr)
  3691. old_lastresort = logging.lastResort
  3692. old_raise_exceptions = logging.raiseExceptions
  3693. try:
  3694. with support.captured_stderr() as stderr:
  3695. root.debug('This should not appear')
  3696. self.assertEqual(stderr.getvalue(), '')
  3697. root.warning('Final chance!')
  3698. self.assertEqual(stderr.getvalue(), 'Final chance!\n')
  3699. # No handlers and no last resort, so 'No handlers' message
  3700. logging.lastResort = None
  3701. with support.captured_stderr() as stderr:
  3702. root.warning('Final chance!')
  3703. msg = 'No handlers could be found for logger "root"\n'
  3704. self.assertEqual(stderr.getvalue(), msg)
  3705. # 'No handlers' message only printed once
  3706. with support.captured_stderr() as stderr:
  3707. root.warning('Final chance!')
  3708. self.assertEqual(stderr.getvalue(), '')
  3709. # If raiseExceptions is False, no message is printed
  3710. root.manager.emittedNoHandlerWarning = False
  3711. logging.raiseExceptions = False
  3712. with support.captured_stderr() as stderr:
  3713. root.warning('Final chance!')
  3714. self.assertEqual(stderr.getvalue(), '')
  3715. finally:
  3716. root.addHandler(self.root_hdlr)
  3717. logging.lastResort = old_lastresort
  3718. logging.raiseExceptions = old_raise_exceptions
  3719. class FakeHandler:
  3720. def __init__(self, identifier, called):
  3721. for method in ('acquire', 'flush', 'close', 'release'):
  3722. setattr(self, method, self.record_call(identifier, method, called))
  3723. def record_call(self, identifier, method_name, called):
  3724. def inner():
  3725. called.append('{} - {}'.format(identifier, method_name))
  3726. return inner
  3727. class RecordingHandler(logging.NullHandler):
  3728. def __init__(self, *args, **kwargs):
  3729. super(RecordingHandler, self).__init__(*args, **kwargs)
  3730. self.records = []
  3731. def handle(self, record):
  3732. """Keep track of all the emitted records."""
  3733. self.records.append(record)
  3734. class ShutdownTest(BaseTest):
  3735. """Test suite for the shutdown method."""
  3736. def setUp(self):
  3737. super(ShutdownTest, self).setUp()
  3738. self.called = []
  3739. raise_exceptions = logging.raiseExceptions
  3740. self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
  3741. def raise_error(self, error):
  3742. def inner():
  3743. raise error()
  3744. return inner
  3745. def test_no_failure(self):
  3746. # create some fake handlers
  3747. handler0 = FakeHandler(0, self.called)
  3748. handler1 = FakeHandler(1, self.called)
  3749. handler2 = FakeHandler(2, self.called)
  3750. # create live weakref to those handlers
  3751. handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
  3752. logging.shutdown(handlerList=list(handlers))
  3753. expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
  3754. '1 - acquire', '1 - flush', '1 - close', '1 - release',
  3755. '0 - acquire', '0 - flush', '0 - close', '0 - release']
  3756. self.assertEqual(expected, self.called)
  3757. def _test_with_failure_in_method(self, method, error):
  3758. handler = FakeHandler(0, self.called)
  3759. setattr(handler, method, self.raise_error(error))
  3760. handlers = [logging.weakref.ref(handler)]
  3761. logging.shutdown(handlerList=list(handlers))
  3762. self.assertEqual('0 - release', self.called[-1])
  3763. def test_with_ioerror_in_acquire(self):
  3764. self._test_with_failure_in_method('acquire', OSError)
  3765. def test_with_ioerror_in_flush(self):
  3766. self._test_with_failure_in_method('flush', OSError)
  3767. def test_with_ioerror_in_close(self):
  3768. self._test_with_failure_in_method('close', OSError)
  3769. def test_with_valueerror_in_acquire(self):
  3770. self._test_with_failure_in_method('acquire', ValueError)
  3771. def test_with_valueerror_in_flush(self):
  3772. self._test_with_failure_in_method('flush', ValueError)
  3773. def test_with_valueerror_in_close(self):
  3774. self._test_with_failure_in_method('close', ValueError)
  3775. def test_with_other_error_in_acquire_without_raise(self):
  3776. logging.raiseExceptions = False
  3777. self._test_with_failure_in_method('acquire', IndexError)
  3778. def test_with_other_error_in_flush_without_raise(self):
  3779. logging.raiseExceptions = False
  3780. self._test_with_failure_in_method('flush', IndexError)
  3781. def test_with_other_error_in_close_without_raise(self):
  3782. logging.raiseExceptions = False
  3783. self._test_with_failure_in_method('close', IndexError)
  3784. def test_with_other_error_in_acquire_with_raise(self):
  3785. logging.raiseExceptions = True
  3786. self.assertRaises(IndexError, self._test_with_failure_in_method,
  3787. 'acquire', IndexError)
  3788. def test_with_other_error_in_flush_with_raise(self):
  3789. logging.raiseExceptions = True
  3790. self.assertRaises(IndexError, self._test_with_failure_in_method,
  3791. 'flush', IndexError)
  3792. def test_with_other_error_in_close_with_raise(self):
  3793. logging.raiseExceptions = True
  3794. self.assertRaises(IndexError, self._test_with_failure_in_method,
  3795. 'close', IndexError)
  3796. class ModuleLevelMiscTest(BaseTest):
  3797. """Test suite for some module level methods."""
  3798. def test_disable(self):
  3799. old_disable = logging.root.manager.disable
  3800. # confirm our assumptions are correct
  3801. self.assertEqual(old_disable, 0)
  3802. self.addCleanup(logging.disable, old_disable)
  3803. logging.disable(83)
  3804. self.assertEqual(logging.root.manager.disable, 83)
  3805. self.assertRaises(ValueError, logging.disable, "doesnotexists")
  3806. class _NotAnIntOrString:
  3807. pass
  3808. self.assertRaises(TypeError, logging.disable, _NotAnIntOrString())
  3809. logging.disable("WARN")
  3810. # test the default value introduced in 3.7
  3811. # (Issue #28524)
  3812. logging.disable()
  3813. self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
  3814. def _test_log(self, method, level=None):
  3815. called = []
  3816. support.patch(self, logging, 'basicConfig',
  3817. lambda *a, **kw: called.append((a, kw)))
  3818. recording = RecordingHandler()
  3819. logging.root.addHandler(recording)
  3820. log_method = getattr(logging, method)
  3821. if level is not None:
  3822. log_method(level, "test me: %r", recording)
  3823. else:
  3824. log_method("test me: %r", recording)
  3825. self.assertEqual(len(recording.records), 1)
  3826. record = recording.records[0]
  3827. self.assertEqual(record.getMessage(), "test me: %r" % recording)
  3828. expected_level = level if level is not None else getattr(logging, method.upper())
  3829. self.assertEqual(record.levelno, expected_level)
  3830. # basicConfig was not called!
  3831. self.assertEqual(called, [])
  3832. def test_log(self):
  3833. self._test_log('log', logging.ERROR)
  3834. def test_debug(self):
  3835. self._test_log('debug')
  3836. def test_info(self):
  3837. self._test_log('info')
  3838. def test_warning(self):
  3839. self._test_log('warning')
  3840. def test_error(self):
  3841. self._test_log('error')
  3842. def test_critical(self):
  3843. self._test_log('critical')
  3844. def test_set_logger_class(self):
  3845. self.assertRaises(TypeError, logging.setLoggerClass, object)
  3846. class MyLogger(logging.Logger):
  3847. pass
  3848. logging.setLoggerClass(MyLogger)
  3849. self.assertEqual(logging.getLoggerClass(), MyLogger)
  3850. logging.setLoggerClass(logging.Logger)
  3851. self.assertEqual(logging.getLoggerClass(), logging.Logger)
  3852. def test_subclass_logger_cache(self):
  3853. # bpo-37258
  3854. message = []
  3855. class MyLogger(logging.getLoggerClass()):
  3856. def __init__(self, name='MyLogger', level=logging.NOTSET):
  3857. super().__init__(name, level)
  3858. message.append('initialized')
  3859. logging.setLoggerClass(MyLogger)
  3860. logger = logging.getLogger('just_some_logger')
  3861. self.assertEqual(message, ['initialized'])
  3862. stream = io.StringIO()
  3863. h = logging.StreamHandler(stream)
  3864. logger.addHandler(h)
  3865. try:
  3866. logger.setLevel(logging.DEBUG)
  3867. logger.debug("hello")
  3868. self.assertEqual(stream.getvalue().strip(), "hello")
  3869. stream.truncate(0)
  3870. stream.seek(0)
  3871. logger.setLevel(logging.INFO)
  3872. logger.debug("hello")
  3873. self.assertEqual(stream.getvalue(), "")
  3874. finally:
  3875. logger.removeHandler(h)
  3876. h.close()
  3877. logging.setLoggerClass(logging.Logger)
  3878. def test_logging_at_shutdown(self):
  3879. # bpo-20037: Doing text I/O late at interpreter shutdown must not crash
  3880. code = textwrap.dedent("""
  3881. import logging
  3882. class A:
  3883. def __del__(self):
  3884. try:
  3885. raise ValueError("some error")
  3886. except Exception:
  3887. logging.exception("exception in __del__")
  3888. a = A()
  3889. """)
  3890. rc, out, err = assert_python_ok("-c", code)
  3891. err = err.decode()
  3892. self.assertIn("exception in __del__", err)
  3893. self.assertIn("ValueError: some error", err)
  3894. def test_logging_at_shutdown_open(self):
  3895. # bpo-26789: FileHandler keeps a reference to the builtin open()
  3896. # function to be able to open or reopen the file during Python
  3897. # finalization.
  3898. filename = os_helper.TESTFN
  3899. self.addCleanup(os_helper.unlink, filename)
  3900. code = textwrap.dedent(f"""
  3901. import builtins
  3902. import logging
  3903. class A:
  3904. def __del__(self):
  3905. logging.error("log in __del__")
  3906. # basicConfig() opens the file, but logging.shutdown() closes
  3907. # it at Python exit. When A.__del__() is called,
  3908. # FileHandler._open() must be called again to re-open the file.
  3909. logging.basicConfig(filename={filename!r}, encoding="utf-8")
  3910. a = A()
  3911. # Simulate the Python finalization which removes the builtin
  3912. # open() function.
  3913. del builtins.open
  3914. """)
  3915. assert_python_ok("-c", code)
  3916. with open(filename, encoding="utf-8") as fp:
  3917. self.assertEqual(fp.read().rstrip(), "ERROR:root:log in __del__")
  3918. def test_recursion_error(self):
  3919. # Issue 36272
  3920. code = textwrap.dedent("""
  3921. import logging
  3922. def rec():
  3923. logging.error("foo")
  3924. rec()
  3925. rec()
  3926. """)
  3927. rc, out, err = assert_python_failure("-c", code)
  3928. err = err.decode()
  3929. self.assertNotIn("Cannot recover from stack overflow.", err)
  3930. self.assertEqual(rc, 1)
  3931. def test_get_level_names_mapping(self):
  3932. mapping = logging.getLevelNamesMapping()
  3933. self.assertEqual(logging._nameToLevel, mapping) # value is equivalent
  3934. self.assertIsNot(logging._nameToLevel, mapping) # but not the internal data
  3935. new_mapping = logging.getLevelNamesMapping() # another call -> another copy
  3936. self.assertIsNot(mapping, new_mapping) # verify not the same object as before
  3937. self.assertEqual(mapping, new_mapping) # but equivalent in value
  3938. class LogRecordTest(BaseTest):
  3939. def test_str_rep(self):
  3940. r = logging.makeLogRecord({})
  3941. s = str(r)
  3942. self.assertTrue(s.startswith('<LogRecord: '))
  3943. self.assertTrue(s.endswith('>'))
  3944. def test_dict_arg(self):
  3945. h = RecordingHandler()
  3946. r = logging.getLogger()
  3947. r.addHandler(h)
  3948. d = {'less' : 'more' }
  3949. logging.warning('less is %(less)s', d)
  3950. self.assertIs(h.records[0].args, d)
  3951. self.assertEqual(h.records[0].message, 'less is more')
  3952. r.removeHandler(h)
  3953. h.close()
  3954. @staticmethod # pickled as target of child process in the following test
  3955. def _extract_logrecord_process_name(key, logMultiprocessing, conn=None):
  3956. prev_logMultiprocessing = logging.logMultiprocessing
  3957. logging.logMultiprocessing = logMultiprocessing
  3958. try:
  3959. import multiprocessing as mp
  3960. name = mp.current_process().name
  3961. r1 = logging.makeLogRecord({'msg': f'msg1_{key}'})
  3962. # https://bugs.python.org/issue45128
  3963. with support.swap_item(sys.modules, 'multiprocessing', None):
  3964. r2 = logging.makeLogRecord({'msg': f'msg2_{key}'})
  3965. results = {'processName' : name,
  3966. 'r1.processName': r1.processName,
  3967. 'r2.processName': r2.processName,
  3968. }
  3969. finally:
  3970. logging.logMultiprocessing = prev_logMultiprocessing
  3971. if conn:
  3972. conn.send(results)
  3973. else:
  3974. return results
  3975. def test_multiprocessing(self):
  3976. support.skip_if_broken_multiprocessing_synchronize()
  3977. multiprocessing_imported = 'multiprocessing' in sys.modules
  3978. try:
  3979. # logMultiprocessing is True by default
  3980. self.assertEqual(logging.logMultiprocessing, True)
  3981. LOG_MULTI_PROCESSING = True
  3982. # When logMultiprocessing == True:
  3983. # In the main process processName = 'MainProcess'
  3984. r = logging.makeLogRecord({})
  3985. self.assertEqual(r.processName, 'MainProcess')
  3986. results = self._extract_logrecord_process_name(1, LOG_MULTI_PROCESSING)
  3987. self.assertEqual('MainProcess', results['processName'])
  3988. self.assertEqual('MainProcess', results['r1.processName'])
  3989. self.assertEqual('MainProcess', results['r2.processName'])
  3990. # In other processes, processName is correct when multiprocessing in imported,
  3991. # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762).
  3992. import multiprocessing
  3993. parent_conn, child_conn = multiprocessing.Pipe()
  3994. p = multiprocessing.Process(
  3995. target=self._extract_logrecord_process_name,
  3996. args=(2, LOG_MULTI_PROCESSING, child_conn,)
  3997. )
  3998. p.start()
  3999. results = parent_conn.recv()
  4000. self.assertNotEqual('MainProcess', results['processName'])
  4001. self.assertEqual(results['processName'], results['r1.processName'])
  4002. self.assertEqual('MainProcess', results['r2.processName'])
  4003. p.join()
  4004. finally:
  4005. if multiprocessing_imported:
  4006. import multiprocessing
  4007. def test_optional(self):
  4008. r = logging.makeLogRecord({})
  4009. NOT_NONE = self.assertIsNotNone
  4010. NOT_NONE(r.thread)
  4011. NOT_NONE(r.threadName)
  4012. NOT_NONE(r.process)
  4013. NOT_NONE(r.processName)
  4014. log_threads = logging.logThreads
  4015. log_processes = logging.logProcesses
  4016. log_multiprocessing = logging.logMultiprocessing
  4017. try:
  4018. logging.logThreads = False
  4019. logging.logProcesses = False
  4020. logging.logMultiprocessing = False
  4021. r = logging.makeLogRecord({})
  4022. NONE = self.assertIsNone
  4023. NONE(r.thread)
  4024. NONE(r.threadName)
  4025. NONE(r.process)
  4026. NONE(r.processName)
  4027. finally:
  4028. logging.logThreads = log_threads
  4029. logging.logProcesses = log_processes
  4030. logging.logMultiprocessing = log_multiprocessing
  4031. class BasicConfigTest(unittest.TestCase):
  4032. """Test suite for logging.basicConfig."""
  4033. def setUp(self):
  4034. super(BasicConfigTest, self).setUp()
  4035. self.handlers = logging.root.handlers
  4036. self.saved_handlers = logging._handlers.copy()
  4037. self.saved_handler_list = logging._handlerList[:]
  4038. self.original_logging_level = logging.root.level
  4039. self.addCleanup(self.cleanup)
  4040. logging.root.handlers = []
  4041. def tearDown(self):
  4042. for h in logging.root.handlers[:]:
  4043. logging.root.removeHandler(h)
  4044. h.close()
  4045. super(BasicConfigTest, self).tearDown()
  4046. def cleanup(self):
  4047. setattr(logging.root, 'handlers', self.handlers)
  4048. logging._handlers.clear()
  4049. logging._handlers.update(self.saved_handlers)
  4050. logging._handlerList[:] = self.saved_handler_list
  4051. logging.root.setLevel(self.original_logging_level)
  4052. def test_no_kwargs(self):
  4053. logging.basicConfig()
  4054. # handler defaults to a StreamHandler to sys.stderr
  4055. self.assertEqual(len(logging.root.handlers), 1)
  4056. handler = logging.root.handlers[0]
  4057. self.assertIsInstance(handler, logging.StreamHandler)
  4058. self.assertEqual(handler.stream, sys.stderr)
  4059. formatter = handler.formatter
  4060. # format defaults to logging.BASIC_FORMAT
  4061. self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
  4062. # datefmt defaults to None
  4063. self.assertIsNone(formatter.datefmt)
  4064. # style defaults to %
  4065. self.assertIsInstance(formatter._style, logging.PercentStyle)
  4066. # level is not explicitly set
  4067. self.assertEqual(logging.root.level, self.original_logging_level)
  4068. def test_strformatstyle(self):
  4069. with support.captured_stdout() as output:
  4070. logging.basicConfig(stream=sys.stdout, style="{")
  4071. logging.error("Log an error")
  4072. sys.stdout.seek(0)
  4073. self.assertEqual(output.getvalue().strip(),
  4074. "ERROR:root:Log an error")
  4075. def test_stringtemplatestyle(self):
  4076. with support.captured_stdout() as output:
  4077. logging.basicConfig(stream=sys.stdout, style="$")
  4078. logging.error("Log an error")
  4079. sys.stdout.seek(0)
  4080. self.assertEqual(output.getvalue().strip(),
  4081. "ERROR:root:Log an error")
  4082. def test_filename(self):
  4083. def cleanup(h1, h2, fn):
  4084. h1.close()
  4085. h2.close()
  4086. os.remove(fn)
  4087. logging.basicConfig(filename='test.log', encoding='utf-8')
  4088. self.assertEqual(len(logging.root.handlers), 1)
  4089. handler = logging.root.handlers[0]
  4090. self.assertIsInstance(handler, logging.FileHandler)
  4091. expected = logging.FileHandler('test.log', 'a', encoding='utf-8')
  4092. self.assertEqual(handler.stream.mode, expected.stream.mode)
  4093. self.assertEqual(handler.stream.name, expected.stream.name)
  4094. self.addCleanup(cleanup, handler, expected, 'test.log')
  4095. def test_filemode(self):
  4096. def cleanup(h1, h2, fn):
  4097. h1.close()
  4098. h2.close()
  4099. os.remove(fn)
  4100. logging.basicConfig(filename='test.log', filemode='wb')
  4101. handler = logging.root.handlers[0]
  4102. expected = logging.FileHandler('test.log', 'wb')
  4103. self.assertEqual(handler.stream.mode, expected.stream.mode)
  4104. self.addCleanup(cleanup, handler, expected, 'test.log')
  4105. def test_stream(self):
  4106. stream = io.StringIO()
  4107. self.addCleanup(stream.close)
  4108. logging.basicConfig(stream=stream)
  4109. self.assertEqual(len(logging.root.handlers), 1)
  4110. handler = logging.root.handlers[0]
  4111. self.assertIsInstance(handler, logging.StreamHandler)
  4112. self.assertEqual(handler.stream, stream)
  4113. def test_format(self):
  4114. logging.basicConfig(format='%(asctime)s - %(message)s')
  4115. formatter = logging.root.handlers[0].formatter
  4116. self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
  4117. def test_datefmt(self):
  4118. logging.basicConfig(datefmt='bar')
  4119. formatter = logging.root.handlers[0].formatter
  4120. self.assertEqual(formatter.datefmt, 'bar')
  4121. def test_style(self):
  4122. logging.basicConfig(style='$')
  4123. formatter = logging.root.handlers[0].formatter
  4124. self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
  4125. def test_level(self):
  4126. old_level = logging.root.level
  4127. self.addCleanup(logging.root.setLevel, old_level)
  4128. logging.basicConfig(level=57)
  4129. self.assertEqual(logging.root.level, 57)
  4130. # Test that second call has no effect
  4131. logging.basicConfig(level=58)
  4132. self.assertEqual(logging.root.level, 57)
  4133. def test_incompatible(self):
  4134. assertRaises = self.assertRaises
  4135. handlers = [logging.StreamHandler()]
  4136. stream = sys.stderr
  4137. assertRaises(ValueError, logging.basicConfig, filename='test.log',
  4138. stream=stream)
  4139. assertRaises(ValueError, logging.basicConfig, filename='test.log',
  4140. handlers=handlers)
  4141. assertRaises(ValueError, logging.basicConfig, stream=stream,
  4142. handlers=handlers)
  4143. # Issue 23207: test for invalid kwargs
  4144. assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
  4145. # Should pop both filename and filemode even if filename is None
  4146. logging.basicConfig(filename=None, filemode='a')
  4147. def test_handlers(self):
  4148. handlers = [
  4149. logging.StreamHandler(),
  4150. logging.StreamHandler(sys.stdout),
  4151. logging.StreamHandler(),
  4152. ]
  4153. f = logging.Formatter()
  4154. handlers[2].setFormatter(f)
  4155. logging.basicConfig(handlers=handlers)
  4156. self.assertIs(handlers[0], logging.root.handlers[0])
  4157. self.assertIs(handlers[1], logging.root.handlers[1])
  4158. self.assertIs(handlers[2], logging.root.handlers[2])
  4159. self.assertIsNotNone(handlers[0].formatter)
  4160. self.assertIsNotNone(handlers[1].formatter)
  4161. self.assertIs(handlers[2].formatter, f)
  4162. self.assertIs(handlers[0].formatter, handlers[1].formatter)
  4163. def test_force(self):
  4164. old_string_io = io.StringIO()
  4165. new_string_io = io.StringIO()
  4166. old_handlers = [logging.StreamHandler(old_string_io)]
  4167. new_handlers = [logging.StreamHandler(new_string_io)]
  4168. logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
  4169. logging.warning('warn')
  4170. logging.info('info')
  4171. logging.debug('debug')
  4172. self.assertEqual(len(logging.root.handlers), 1)
  4173. logging.basicConfig(level=logging.INFO, handlers=new_handlers,
  4174. force=True)
  4175. logging.warning('warn')
  4176. logging.info('info')
  4177. logging.debug('debug')
  4178. self.assertEqual(len(logging.root.handlers), 1)
  4179. self.assertEqual(old_string_io.getvalue().strip(),
  4180. 'WARNING:root:warn')
  4181. self.assertEqual(new_string_io.getvalue().strip(),
  4182. 'WARNING:root:warn\nINFO:root:info')
  4183. def test_encoding(self):
  4184. try:
  4185. encoding = 'utf-8'
  4186. logging.basicConfig(filename='test.log', encoding=encoding,
  4187. errors='strict',
  4188. format='%(message)s', level=logging.DEBUG)
  4189. self.assertEqual(len(logging.root.handlers), 1)
  4190. handler = logging.root.handlers[0]
  4191. self.assertIsInstance(handler, logging.FileHandler)
  4192. self.assertEqual(handler.encoding, encoding)
  4193. logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
  4194. finally:
  4195. handler.close()
  4196. with open('test.log', encoding='utf-8') as f:
  4197. data = f.read().strip()
  4198. os.remove('test.log')
  4199. self.assertEqual(data,
  4200. 'The Øresund Bridge joins Copenhagen to Malmö')
  4201. def test_encoding_errors(self):
  4202. try:
  4203. encoding = 'ascii'
  4204. logging.basicConfig(filename='test.log', encoding=encoding,
  4205. errors='ignore',
  4206. format='%(message)s', level=logging.DEBUG)
  4207. self.assertEqual(len(logging.root.handlers), 1)
  4208. handler = logging.root.handlers[0]
  4209. self.assertIsInstance(handler, logging.FileHandler)
  4210. self.assertEqual(handler.encoding, encoding)
  4211. logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
  4212. finally:
  4213. handler.close()
  4214. with open('test.log', encoding='utf-8') as f:
  4215. data = f.read().strip()
  4216. os.remove('test.log')
  4217. self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
  4218. def test_encoding_errors_default(self):
  4219. try:
  4220. encoding = 'ascii'
  4221. logging.basicConfig(filename='test.log', encoding=encoding,
  4222. format='%(message)s', level=logging.DEBUG)
  4223. self.assertEqual(len(logging.root.handlers), 1)
  4224. handler = logging.root.handlers[0]
  4225. self.assertIsInstance(handler, logging.FileHandler)
  4226. self.assertEqual(handler.encoding, encoding)
  4227. self.assertEqual(handler.errors, 'backslashreplace')
  4228. logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
  4229. finally:
  4230. handler.close()
  4231. with open('test.log', encoding='utf-8') as f:
  4232. data = f.read().strip()
  4233. os.remove('test.log')
  4234. self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
  4235. r'Bridge joins Copenhagen to Malm\xf6')
  4236. def test_encoding_errors_none(self):
  4237. # Specifying None should behave as 'strict'
  4238. try:
  4239. encoding = 'ascii'
  4240. logging.basicConfig(filename='test.log', encoding=encoding,
  4241. errors=None,
  4242. format='%(message)s', level=logging.DEBUG)
  4243. self.assertEqual(len(logging.root.handlers), 1)
  4244. handler = logging.root.handlers[0]
  4245. self.assertIsInstance(handler, logging.FileHandler)
  4246. self.assertEqual(handler.encoding, encoding)
  4247. self.assertIsNone(handler.errors)
  4248. message = []
  4249. def dummy_handle_error(record):
  4250. _, v, _ = sys.exc_info()
  4251. message.append(str(v))
  4252. handler.handleError = dummy_handle_error
  4253. logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
  4254. self.assertTrue(message)
  4255. self.assertIn("'ascii' codec can't encode "
  4256. "character '\\xd8' in position 4:", message[0])
  4257. finally:
  4258. handler.close()
  4259. with open('test.log', encoding='utf-8') as f:
  4260. data = f.read().strip()
  4261. os.remove('test.log')
  4262. # didn't write anything due to the encoding error
  4263. self.assertEqual(data, r'')
  4264. def _test_log(self, method, level=None):
  4265. # logging.root has no handlers so basicConfig should be called
  4266. called = []
  4267. old_basic_config = logging.basicConfig
  4268. def my_basic_config(*a, **kw):
  4269. old_basic_config()
  4270. old_level = logging.root.level
  4271. logging.root.setLevel(100) # avoid having messages in stderr
  4272. self.addCleanup(logging.root.setLevel, old_level)
  4273. called.append((a, kw))
  4274. support.patch(self, logging, 'basicConfig', my_basic_config)
  4275. log_method = getattr(logging, method)
  4276. if level is not None:
  4277. log_method(level, "test me")
  4278. else:
  4279. log_method("test me")
  4280. # basicConfig was called with no arguments
  4281. self.assertEqual(called, [((), {})])
  4282. def test_log(self):
  4283. self._test_log('log', logging.WARNING)
  4284. def test_debug(self):
  4285. self._test_log('debug')
  4286. def test_info(self):
  4287. self._test_log('info')
  4288. def test_warning(self):
  4289. self._test_log('warning')
  4290. def test_error(self):
  4291. self._test_log('error')
  4292. def test_critical(self):
  4293. self._test_log('critical')
  4294. class LoggerAdapterTest(unittest.TestCase):
  4295. def setUp(self):
  4296. super(LoggerAdapterTest, self).setUp()
  4297. old_handler_list = logging._handlerList[:]
  4298. self.recording = RecordingHandler()
  4299. self.logger = logging.root
  4300. self.logger.addHandler(self.recording)
  4301. self.addCleanup(self.logger.removeHandler, self.recording)
  4302. self.addCleanup(self.recording.close)
  4303. def cleanup():
  4304. logging._handlerList[:] = old_handler_list
  4305. self.addCleanup(cleanup)
  4306. self.addCleanup(logging.shutdown)
  4307. self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
  4308. def test_exception(self):
  4309. msg = 'testing exception: %r'
  4310. exc = None
  4311. try:
  4312. 1 / 0
  4313. except ZeroDivisionError as e:
  4314. exc = e
  4315. self.adapter.exception(msg, self.recording)
  4316. self.assertEqual(len(self.recording.records), 1)
  4317. record = self.recording.records[0]
  4318. self.assertEqual(record.levelno, logging.ERROR)
  4319. self.assertEqual(record.msg, msg)
  4320. self.assertEqual(record.args, (self.recording,))
  4321. self.assertEqual(record.exc_info,
  4322. (exc.__class__, exc, exc.__traceback__))
  4323. def test_exception_excinfo(self):
  4324. try:
  4325. 1 / 0
  4326. except ZeroDivisionError as e:
  4327. exc = e
  4328. self.adapter.exception('exc_info test', exc_info=exc)
  4329. self.assertEqual(len(self.recording.records), 1)
  4330. record = self.recording.records[0]
  4331. self.assertEqual(record.exc_info,
  4332. (exc.__class__, exc, exc.__traceback__))
  4333. def test_critical(self):
  4334. msg = 'critical test! %r'
  4335. self.adapter.critical(msg, self.recording)
  4336. self.assertEqual(len(self.recording.records), 1)
  4337. record = self.recording.records[0]
  4338. self.assertEqual(record.levelno, logging.CRITICAL)
  4339. self.assertEqual(record.msg, msg)
  4340. self.assertEqual(record.args, (self.recording,))
  4341. def test_is_enabled_for(self):
  4342. old_disable = self.adapter.logger.manager.disable
  4343. self.adapter.logger.manager.disable = 33
  4344. self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
  4345. old_disable)
  4346. self.assertFalse(self.adapter.isEnabledFor(32))
  4347. def test_has_handlers(self):
  4348. self.assertTrue(self.adapter.hasHandlers())
  4349. for handler in self.logger.handlers:
  4350. self.logger.removeHandler(handler)
  4351. self.assertFalse(self.logger.hasHandlers())
  4352. self.assertFalse(self.adapter.hasHandlers())
  4353. def test_nested(self):
  4354. class Adapter(logging.LoggerAdapter):
  4355. prefix = 'Adapter'
  4356. def process(self, msg, kwargs):
  4357. return f"{self.prefix} {msg}", kwargs
  4358. msg = 'Adapters can be nested, yo.'
  4359. adapter = Adapter(logger=self.logger, extra=None)
  4360. adapter_adapter = Adapter(logger=adapter, extra=None)
  4361. adapter_adapter.prefix = 'AdapterAdapter'
  4362. self.assertEqual(repr(adapter), repr(adapter_adapter))
  4363. adapter_adapter.log(logging.CRITICAL, msg, self.recording)
  4364. self.assertEqual(len(self.recording.records), 1)
  4365. record = self.recording.records[0]
  4366. self.assertEqual(record.levelno, logging.CRITICAL)
  4367. self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
  4368. self.assertEqual(record.args, (self.recording,))
  4369. orig_manager = adapter_adapter.manager
  4370. self.assertIs(adapter.manager, orig_manager)
  4371. self.assertIs(self.logger.manager, orig_manager)
  4372. temp_manager = object()
  4373. try:
  4374. adapter_adapter.manager = temp_manager
  4375. self.assertIs(adapter_adapter.manager, temp_manager)
  4376. self.assertIs(adapter.manager, temp_manager)
  4377. self.assertIs(self.logger.manager, temp_manager)
  4378. finally:
  4379. adapter_adapter.manager = orig_manager
  4380. self.assertIs(adapter_adapter.manager, orig_manager)
  4381. self.assertIs(adapter.manager, orig_manager)
  4382. self.assertIs(self.logger.manager, orig_manager)
  4383. class LoggerTest(BaseTest, AssertErrorMessage):
  4384. def setUp(self):
  4385. super(LoggerTest, self).setUp()
  4386. self.recording = RecordingHandler()
  4387. self.logger = logging.Logger(name='blah')
  4388. self.logger.addHandler(self.recording)
  4389. self.addCleanup(self.logger.removeHandler, self.recording)
  4390. self.addCleanup(self.recording.close)
  4391. self.addCleanup(logging.shutdown)
  4392. def test_set_invalid_level(self):
  4393. self.assert_error_message(
  4394. TypeError, 'Level not an integer or a valid string: None',
  4395. self.logger.setLevel, None)
  4396. self.assert_error_message(
  4397. TypeError, 'Level not an integer or a valid string: (0, 0)',
  4398. self.logger.setLevel, (0, 0))
  4399. def test_exception(self):
  4400. msg = 'testing exception: %r'
  4401. exc = None
  4402. try:
  4403. 1 / 0
  4404. except ZeroDivisionError as e:
  4405. exc = e
  4406. self.logger.exception(msg, self.recording)
  4407. self.assertEqual(len(self.recording.records), 1)
  4408. record = self.recording.records[0]
  4409. self.assertEqual(record.levelno, logging.ERROR)
  4410. self.assertEqual(record.msg, msg)
  4411. self.assertEqual(record.args, (self.recording,))
  4412. self.assertEqual(record.exc_info,
  4413. (exc.__class__, exc, exc.__traceback__))
  4414. def test_log_invalid_level_with_raise(self):
  4415. with support.swap_attr(logging, 'raiseExceptions', True):
  4416. self.assertRaises(TypeError, self.logger.log, '10', 'test message')
  4417. def test_log_invalid_level_no_raise(self):
  4418. with support.swap_attr(logging, 'raiseExceptions', False):
  4419. self.logger.log('10', 'test message') # no exception happens
  4420. def test_find_caller_with_stack_info(self):
  4421. called = []
  4422. support.patch(self, logging.traceback, 'print_stack',
  4423. lambda f, file: called.append(file.getvalue()))
  4424. self.logger.findCaller(stack_info=True)
  4425. self.assertEqual(len(called), 1)
  4426. self.assertEqual('Stack (most recent call last):\n', called[0])
  4427. def test_find_caller_with_stacklevel(self):
  4428. the_level = 1
  4429. trigger = self.logger.warning
  4430. def innermost():
  4431. trigger('test', stacklevel=the_level)
  4432. def inner():
  4433. innermost()
  4434. def outer():
  4435. inner()
  4436. records = self.recording.records
  4437. outer()
  4438. self.assertEqual(records[-1].funcName, 'innermost')
  4439. lineno = records[-1].lineno
  4440. the_level += 1
  4441. outer()
  4442. self.assertEqual(records[-1].funcName, 'inner')
  4443. self.assertGreater(records[-1].lineno, lineno)
  4444. lineno = records[-1].lineno
  4445. the_level += 1
  4446. outer()
  4447. self.assertEqual(records[-1].funcName, 'outer')
  4448. self.assertGreater(records[-1].lineno, lineno)
  4449. lineno = records[-1].lineno
  4450. root_logger = logging.getLogger()
  4451. root_logger.addHandler(self.recording)
  4452. trigger = logging.warning
  4453. outer()
  4454. self.assertEqual(records[-1].funcName, 'outer')
  4455. root_logger.removeHandler(self.recording)
  4456. trigger = self.logger.warning
  4457. the_level += 1
  4458. outer()
  4459. self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
  4460. self.assertGreater(records[-1].lineno, lineno)
  4461. def test_make_record_with_extra_overwrite(self):
  4462. name = 'my record'
  4463. level = 13
  4464. fn = lno = msg = args = exc_info = func = sinfo = None
  4465. rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
  4466. exc_info, func, sinfo)
  4467. for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
  4468. extra = {key: 'some value'}
  4469. self.assertRaises(KeyError, self.logger.makeRecord, name, level,
  4470. fn, lno, msg, args, exc_info,
  4471. extra=extra, sinfo=sinfo)
  4472. def test_make_record_with_extra_no_overwrite(self):
  4473. name = 'my record'
  4474. level = 13
  4475. fn = lno = msg = args = exc_info = func = sinfo = None
  4476. extra = {'valid_key': 'some value'}
  4477. result = self.logger.makeRecord(name, level, fn, lno, msg, args,
  4478. exc_info, extra=extra, sinfo=sinfo)
  4479. self.assertIn('valid_key', result.__dict__)
  4480. def test_has_handlers(self):
  4481. self.assertTrue(self.logger.hasHandlers())
  4482. for handler in self.logger.handlers:
  4483. self.logger.removeHandler(handler)
  4484. self.assertFalse(self.logger.hasHandlers())
  4485. def test_has_handlers_no_propagate(self):
  4486. child_logger = logging.getLogger('blah.child')
  4487. child_logger.propagate = False
  4488. self.assertFalse(child_logger.hasHandlers())
  4489. def test_is_enabled_for(self):
  4490. old_disable = self.logger.manager.disable
  4491. self.logger.manager.disable = 23
  4492. self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
  4493. self.assertFalse(self.logger.isEnabledFor(22))
  4494. def test_is_enabled_for_disabled_logger(self):
  4495. old_disabled = self.logger.disabled
  4496. old_disable = self.logger.manager.disable
  4497. self.logger.disabled = True
  4498. self.logger.manager.disable = 21
  4499. self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
  4500. self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
  4501. self.assertFalse(self.logger.isEnabledFor(22))
  4502. def test_root_logger_aliases(self):
  4503. root = logging.getLogger()
  4504. self.assertIs(root, logging.root)
  4505. self.assertIs(root, logging.getLogger(None))
  4506. self.assertIs(root, logging.getLogger(''))
  4507. self.assertIs(root, logging.getLogger('root'))
  4508. self.assertIs(root, logging.getLogger('foo').root)
  4509. self.assertIs(root, logging.getLogger('foo.bar').root)
  4510. self.assertIs(root, logging.getLogger('foo').parent)
  4511. self.assertIsNot(root, logging.getLogger('\0'))
  4512. self.assertIsNot(root, logging.getLogger('foo.bar').parent)
  4513. def test_invalid_names(self):
  4514. self.assertRaises(TypeError, logging.getLogger, any)
  4515. self.assertRaises(TypeError, logging.getLogger, b'foo')
  4516. def test_pickling(self):
  4517. for proto in range(pickle.HIGHEST_PROTOCOL + 1):
  4518. for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
  4519. logger = logging.getLogger(name)
  4520. s = pickle.dumps(logger, proto)
  4521. unpickled = pickle.loads(s)
  4522. self.assertIs(unpickled, logger)
  4523. def test_caching(self):
  4524. root = self.root_logger
  4525. logger1 = logging.getLogger("abc")
  4526. logger2 = logging.getLogger("abc.def")
  4527. # Set root logger level and ensure cache is empty
  4528. root.setLevel(logging.ERROR)
  4529. self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
  4530. self.assertEqual(logger2._cache, {})
  4531. # Ensure cache is populated and calls are consistent
  4532. self.assertTrue(logger2.isEnabledFor(logging.ERROR))
  4533. self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
  4534. self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
  4535. self.assertEqual(root._cache, {})
  4536. self.assertTrue(logger2.isEnabledFor(logging.ERROR))
  4537. # Ensure root cache gets populated
  4538. self.assertEqual(root._cache, {})
  4539. self.assertTrue(root.isEnabledFor(logging.ERROR))
  4540. self.assertEqual(root._cache, {logging.ERROR: True})
  4541. # Set parent logger level and ensure caches are emptied
  4542. logger1.setLevel(logging.CRITICAL)
  4543. self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
  4544. self.assertEqual(logger2._cache, {})
  4545. # Ensure logger2 uses parent logger's effective level
  4546. self.assertFalse(logger2.isEnabledFor(logging.ERROR))
  4547. # Set level to NOTSET and ensure caches are empty
  4548. logger2.setLevel(logging.NOTSET)
  4549. self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
  4550. self.assertEqual(logger2._cache, {})
  4551. self.assertEqual(logger1._cache, {})
  4552. self.assertEqual(root._cache, {})
  4553. # Verify logger2 follows parent and not root
  4554. self.assertFalse(logger2.isEnabledFor(logging.ERROR))
  4555. self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
  4556. self.assertFalse(logger1.isEnabledFor(logging.ERROR))
  4557. self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
  4558. self.assertTrue(root.isEnabledFor(logging.ERROR))
  4559. # Disable logging in manager and ensure caches are clear
  4560. logging.disable()
  4561. self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
  4562. self.assertEqual(logger2._cache, {})
  4563. self.assertEqual(logger1._cache, {})
  4564. self.assertEqual(root._cache, {})
  4565. # Ensure no loggers are enabled
  4566. self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
  4567. self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
  4568. self.assertFalse(root.isEnabledFor(logging.CRITICAL))
  4569. class BaseFileTest(BaseTest):
  4570. "Base class for handler tests that write log files"
  4571. def setUp(self):
  4572. BaseTest.setUp(self)
  4573. fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
  4574. os.close(fd)
  4575. self.rmfiles = []
  4576. def tearDown(self):
  4577. for fn in self.rmfiles:
  4578. os.unlink(fn)
  4579. if os.path.exists(self.fn):
  4580. os.unlink(self.fn)
  4581. BaseTest.tearDown(self)
  4582. def assertLogFile(self, filename):
  4583. "Assert a log file is there and register it for deletion"
  4584. self.assertTrue(os.path.exists(filename),
  4585. msg="Log file %r does not exist" % filename)
  4586. self.rmfiles.append(filename)
  4587. def next_rec(self):
  4588. return logging.LogRecord('n', logging.DEBUG, 'p', 1,
  4589. self.next_message(), None, None, None)
  4590. class FileHandlerTest(BaseFileTest):
  4591. def test_delay(self):
  4592. os.unlink(self.fn)
  4593. fh = logging.FileHandler(self.fn, encoding='utf-8', delay=True)
  4594. self.assertIsNone(fh.stream)
  4595. self.assertFalse(os.path.exists(self.fn))
  4596. fh.handle(logging.makeLogRecord({}))
  4597. self.assertIsNotNone(fh.stream)
  4598. self.assertTrue(os.path.exists(self.fn))
  4599. fh.close()
  4600. def test_emit_after_closing_in_write_mode(self):
  4601. # Issue #42378
  4602. os.unlink(self.fn)
  4603. fh = logging.FileHandler(self.fn, encoding='utf-8', mode='w')
  4604. fh.setFormatter(logging.Formatter('%(message)s'))
  4605. fh.emit(self.next_rec()) # '1'
  4606. fh.close()
  4607. fh.emit(self.next_rec()) # '2'
  4608. with open(self.fn) as fp:
  4609. self.assertEqual(fp.read().strip(), '1')
  4610. class RotatingFileHandlerTest(BaseFileTest):
  4611. @unittest.skipIf(support.is_wasi, "WASI does not have /dev/null.")
  4612. def test_should_not_rollover(self):
  4613. # If maxbytes is zero rollover never occurs
  4614. rh = logging.handlers.RotatingFileHandler(
  4615. self.fn, encoding="utf-8", maxBytes=0)
  4616. self.assertFalse(rh.shouldRollover(None))
  4617. rh.close()
  4618. # bpo-45401 - test with special file
  4619. # We set maxBytes to 1 so that rollover would normally happen, except
  4620. # for the check for regular files
  4621. rh = logging.handlers.RotatingFileHandler(
  4622. os.devnull, encoding="utf-8", maxBytes=1)
  4623. self.assertFalse(rh.shouldRollover(self.next_rec()))
  4624. rh.close()
  4625. def test_should_rollover(self):
  4626. rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8", maxBytes=1)
  4627. self.assertTrue(rh.shouldRollover(self.next_rec()))
  4628. rh.close()
  4629. def test_file_created(self):
  4630. # checks that the file is created and assumes it was created
  4631. # by us
  4632. rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8")
  4633. rh.emit(self.next_rec())
  4634. self.assertLogFile(self.fn)
  4635. rh.close()
  4636. def test_rollover_filenames(self):
  4637. def namer(name):
  4638. return name + ".test"
  4639. rh = logging.handlers.RotatingFileHandler(
  4640. self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
  4641. rh.namer = namer
  4642. rh.emit(self.next_rec())
  4643. self.assertLogFile(self.fn)
  4644. rh.emit(self.next_rec())
  4645. self.assertLogFile(namer(self.fn + ".1"))
  4646. rh.emit(self.next_rec())
  4647. self.assertLogFile(namer(self.fn + ".2"))
  4648. self.assertFalse(os.path.exists(namer(self.fn + ".3")))
  4649. rh.close()
  4650. def test_namer_rotator_inheritance(self):
  4651. class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
  4652. def namer(self, name):
  4653. return name + ".test"
  4654. def rotator(self, source, dest):
  4655. if os.path.exists(source):
  4656. os.replace(source, dest + ".rotated")
  4657. rh = HandlerWithNamerAndRotator(
  4658. self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
  4659. self.assertEqual(rh.namer(self.fn), self.fn + ".test")
  4660. rh.emit(self.next_rec())
  4661. self.assertLogFile(self.fn)
  4662. rh.emit(self.next_rec())
  4663. self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
  4664. self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
  4665. rh.close()
  4666. @support.requires_zlib()
  4667. def test_rotator(self):
  4668. def namer(name):
  4669. return name + ".gz"
  4670. def rotator(source, dest):
  4671. with open(source, "rb") as sf:
  4672. data = sf.read()
  4673. compressed = zlib.compress(data, 9)
  4674. with open(dest, "wb") as df:
  4675. df.write(compressed)
  4676. os.remove(source)
  4677. rh = logging.handlers.RotatingFileHandler(
  4678. self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
  4679. rh.rotator = rotator
  4680. rh.namer = namer
  4681. m1 = self.next_rec()
  4682. rh.emit(m1)
  4683. self.assertLogFile(self.fn)
  4684. m2 = self.next_rec()
  4685. rh.emit(m2)
  4686. fn = namer(self.fn + ".1")
  4687. self.assertLogFile(fn)
  4688. newline = os.linesep
  4689. with open(fn, "rb") as f:
  4690. compressed = f.read()
  4691. data = zlib.decompress(compressed)
  4692. self.assertEqual(data.decode("ascii"), m1.msg + newline)
  4693. rh.emit(self.next_rec())
  4694. fn = namer(self.fn + ".2")
  4695. self.assertLogFile(fn)
  4696. with open(fn, "rb") as f:
  4697. compressed = f.read()
  4698. data = zlib.decompress(compressed)
  4699. self.assertEqual(data.decode("ascii"), m1.msg + newline)
  4700. rh.emit(self.next_rec())
  4701. fn = namer(self.fn + ".2")
  4702. with open(fn, "rb") as f:
  4703. compressed = f.read()
  4704. data = zlib.decompress(compressed)
  4705. self.assertEqual(data.decode("ascii"), m2.msg + newline)
  4706. self.assertFalse(os.path.exists(namer(self.fn + ".3")))
  4707. rh.close()
  4708. class TimedRotatingFileHandlerTest(BaseFileTest):
  4709. @unittest.skipIf(support.is_wasi, "WASI does not have /dev/null.")
  4710. def test_should_not_rollover(self):
  4711. # See bpo-45401. Should only ever rollover regular files
  4712. fh = logging.handlers.TimedRotatingFileHandler(
  4713. os.devnull, 'S', encoding="utf-8", backupCount=1)
  4714. time.sleep(1.1) # a little over a second ...
  4715. r = logging.makeLogRecord({'msg': 'testing - device file'})
  4716. self.assertFalse(fh.shouldRollover(r))
  4717. fh.close()
  4718. # other test methods added below
  4719. def test_rollover(self):
  4720. fh = logging.handlers.TimedRotatingFileHandler(
  4721. self.fn, 'S', encoding="utf-8", backupCount=1)
  4722. fmt = logging.Formatter('%(asctime)s %(message)s')
  4723. fh.setFormatter(fmt)
  4724. r1 = logging.makeLogRecord({'msg': 'testing - initial'})
  4725. fh.emit(r1)
  4726. self.assertLogFile(self.fn)
  4727. time.sleep(1.1) # a little over a second ...
  4728. r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
  4729. fh.emit(r2)
  4730. fh.close()
  4731. # At this point, we should have a recent rotated file which we
  4732. # can test for the existence of. However, in practice, on some
  4733. # machines which run really slowly, we don't know how far back
  4734. # in time to go to look for the log file. So, we go back a fair
  4735. # bit, and stop as soon as we see a rotated file. In theory this
  4736. # could of course still fail, but the chances are lower.
  4737. found = False
  4738. now = datetime.datetime.now()
  4739. GO_BACK = 5 * 60 # seconds
  4740. for secs in range(GO_BACK):
  4741. prev = now - datetime.timedelta(seconds=secs)
  4742. fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
  4743. found = os.path.exists(fn)
  4744. if found:
  4745. self.rmfiles.append(fn)
  4746. break
  4747. msg = 'No rotated files found, went back %d seconds' % GO_BACK
  4748. if not found:
  4749. # print additional diagnostics
  4750. dn, fn = os.path.split(self.fn)
  4751. files = [f for f in os.listdir(dn) if f.startswith(fn)]
  4752. print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
  4753. print('The only matching files are: %s' % files, file=sys.stderr)
  4754. for f in files:
  4755. print('Contents of %s:' % f)
  4756. path = os.path.join(dn, f)
  4757. with open(path, 'r') as tf:
  4758. print(tf.read())
  4759. self.assertTrue(found, msg=msg)
  4760. def test_invalid(self):
  4761. assertRaises = self.assertRaises
  4762. assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
  4763. self.fn, 'X', encoding="utf-8", delay=True)
  4764. assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
  4765. self.fn, 'W', encoding="utf-8", delay=True)
  4766. assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
  4767. self.fn, 'W7', encoding="utf-8", delay=True)
  4768. def test_compute_rollover_daily_attime(self):
  4769. currentTime = 0
  4770. atTime = datetime.time(12, 0, 0)
  4771. rh = logging.handlers.TimedRotatingFileHandler(
  4772. self.fn, encoding="utf-8", when='MIDNIGHT', interval=1, backupCount=0,
  4773. utc=True, atTime=atTime)
  4774. try:
  4775. actual = rh.computeRollover(currentTime)
  4776. self.assertEqual(actual, currentTime + 12 * 60 * 60)
  4777. actual = rh.computeRollover(currentTime + 13 * 60 * 60)
  4778. self.assertEqual(actual, currentTime + 36 * 60 * 60)
  4779. finally:
  4780. rh.close()
  4781. #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
  4782. def test_compute_rollover_weekly_attime(self):
  4783. currentTime = int(time.time())
  4784. today = currentTime - currentTime % 86400
  4785. atTime = datetime.time(12, 0, 0)
  4786. wday = time.gmtime(today).tm_wday
  4787. for day in range(7):
  4788. rh = logging.handlers.TimedRotatingFileHandler(
  4789. self.fn, encoding="utf-8", when='W%d' % day, interval=1, backupCount=0,
  4790. utc=True, atTime=atTime)
  4791. try:
  4792. if wday > day:
  4793. # The rollover day has already passed this week, so we
  4794. # go over into next week
  4795. expected = (7 - wday + day)
  4796. else:
  4797. expected = (day - wday)
  4798. # At this point expected is in days from now, convert to seconds
  4799. expected *= 24 * 60 * 60
  4800. # Add in the rollover time
  4801. expected += 12 * 60 * 60
  4802. # Add in adjustment for today
  4803. expected += today
  4804. actual = rh.computeRollover(today)
  4805. if actual != expected:
  4806. print('failed in timezone: %d' % time.timezone)
  4807. print('local vars: %s' % locals())
  4808. self.assertEqual(actual, expected)
  4809. if day == wday:
  4810. # goes into following week
  4811. expected += 7 * 24 * 60 * 60
  4812. actual = rh.computeRollover(today + 13 * 60 * 60)
  4813. if actual != expected:
  4814. print('failed in timezone: %d' % time.timezone)
  4815. print('local vars: %s' % locals())
  4816. self.assertEqual(actual, expected)
  4817. finally:
  4818. rh.close()
  4819. def test_compute_files_to_delete(self):
  4820. # See bpo-46063 for background
  4821. wd = tempfile.mkdtemp(prefix='test_logging_')
  4822. self.addCleanup(shutil.rmtree, wd)
  4823. times = []
  4824. dt = datetime.datetime.now()
  4825. for i in range(10):
  4826. times.append(dt.strftime('%Y-%m-%d_%H-%M-%S'))
  4827. dt += datetime.timedelta(seconds=5)
  4828. prefixes = ('a.b', 'a.b.c', 'd.e', 'd.e.f')
  4829. files = []
  4830. rotators = []
  4831. for prefix in prefixes:
  4832. p = os.path.join(wd, '%s.log' % prefix)
  4833. rotator = logging.handlers.TimedRotatingFileHandler(p, when='s',
  4834. interval=5,
  4835. backupCount=7,
  4836. delay=True)
  4837. rotators.append(rotator)
  4838. if prefix.startswith('a.b'):
  4839. for t in times:
  4840. files.append('%s.log.%s' % (prefix, t))
  4841. else:
  4842. rotator.namer = lambda name: name.replace('.log', '') + '.log'
  4843. for t in times:
  4844. files.append('%s.%s.log' % (prefix, t))
  4845. # Create empty files
  4846. for fn in files:
  4847. p = os.path.join(wd, fn)
  4848. with open(p, 'wb') as f:
  4849. pass
  4850. # Now the checks that only the correct files are offered up for deletion
  4851. for i, prefix in enumerate(prefixes):
  4852. rotator = rotators[i]
  4853. candidates = rotator.getFilesToDelete()
  4854. self.assertEqual(len(candidates), 3)
  4855. if prefix.startswith('a.b'):
  4856. p = '%s.log.' % prefix
  4857. for c in candidates:
  4858. d, fn = os.path.split(c)
  4859. self.assertTrue(fn.startswith(p))
  4860. else:
  4861. for c in candidates:
  4862. d, fn = os.path.split(c)
  4863. self.assertTrue(fn.endswith('.log'))
  4864. self.assertTrue(fn.startswith(prefix + '.') and
  4865. fn[len(prefix) + 2].isdigit())
  4866. def secs(**kw):
  4867. return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
  4868. for when, exp in (('S', 1),
  4869. ('M', 60),
  4870. ('H', 60 * 60),
  4871. ('D', 60 * 60 * 24),
  4872. ('MIDNIGHT', 60 * 60 * 24),
  4873. # current time (epoch start) is a Thursday, W0 means Monday
  4874. ('W0', secs(days=4, hours=24)),
  4875. ):
  4876. def test_compute_rollover(self, when=when, exp=exp):
  4877. rh = logging.handlers.TimedRotatingFileHandler(
  4878. self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
  4879. currentTime = 0.0
  4880. actual = rh.computeRollover(currentTime)
  4881. if exp != actual:
  4882. # Failures occur on some systems for MIDNIGHT and W0.
  4883. # Print detailed calculation for MIDNIGHT so we can try to see
  4884. # what's going on
  4885. if when == 'MIDNIGHT':
  4886. try:
  4887. if rh.utc:
  4888. t = time.gmtime(currentTime)
  4889. else:
  4890. t = time.localtime(currentTime)
  4891. currentHour = t[3]
  4892. currentMinute = t[4]
  4893. currentSecond = t[5]
  4894. # r is the number of seconds left between now and midnight
  4895. r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
  4896. currentMinute) * 60 +
  4897. currentSecond)
  4898. result = currentTime + r
  4899. print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
  4900. print('currentHour: %s' % currentHour, file=sys.stderr)
  4901. print('currentMinute: %s' % currentMinute, file=sys.stderr)
  4902. print('currentSecond: %s' % currentSecond, file=sys.stderr)
  4903. print('r: %s' % r, file=sys.stderr)
  4904. print('result: %s' % result, file=sys.stderr)
  4905. except Exception as e:
  4906. print('exception in diagnostic code: %s' % e, file=sys.stderr)
  4907. self.assertEqual(exp, actual)
  4908. rh.close()
  4909. setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
  4910. @unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
  4911. class NTEventLogHandlerTest(BaseTest):
  4912. def test_basic(self):
  4913. logtype = 'Application'
  4914. elh = win32evtlog.OpenEventLog(None, logtype)
  4915. num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
  4916. try:
  4917. h = logging.handlers.NTEventLogHandler('test_logging')
  4918. except pywintypes.error as e:
  4919. if e.winerror == 5: # access denied
  4920. raise unittest.SkipTest('Insufficient privileges to run test')
  4921. raise
  4922. r = logging.makeLogRecord({'msg': 'Test Log Message'})
  4923. h.handle(r)
  4924. h.close()
  4925. # Now see if the event is recorded
  4926. self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
  4927. flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
  4928. win32evtlog.EVENTLOG_SEQUENTIAL_READ
  4929. found = False
  4930. GO_BACK = 100
  4931. events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
  4932. for e in events:
  4933. if e.SourceName != 'test_logging':
  4934. continue
  4935. msg = win32evtlogutil.SafeFormatMessage(e, logtype)
  4936. if msg != 'Test Log Message\r\n':
  4937. continue
  4938. found = True
  4939. break
  4940. msg = 'Record not found in event log, went back %d records' % GO_BACK
  4941. self.assertTrue(found, msg=msg)
  4942. class MiscTestCase(unittest.TestCase):
  4943. def test__all__(self):
  4944. not_exported = {
  4945. 'logThreads', 'logMultiprocessing', 'logProcesses', 'currentframe',
  4946. 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
  4947. 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', 'root',
  4948. 'threading'}
  4949. support.check__all__(self, logging, not_exported=not_exported)
  4950. # Set the locale to the platform-dependent default. I have no idea
  4951. # why the test does this, but in any case we save the current locale
  4952. # first and restore it at the end.
  4953. def setUpModule():
  4954. unittest.enterModuleContext(support.run_with_locale('LC_ALL', ''))
  4955. if __name__ == "__main__":
  4956. unittest.main()