Python Logging - ปิดใช้งานการบันทึกจากโมดูลที่นำเข้า


100

ฉันใช้โมดูลการบันทึก Python และต้องการปิดใช้งานข้อความบันทึกที่พิมพ์โดยโมดูลของบุคคลที่สามที่ฉันนำเข้า ตัวอย่างเช่นฉันกำลังใช้สิ่งต่อไปนี้:

logger = logging.getLogger()
logger.setLevel(level=logging.DEBUG)
fh = logging.StreamHandler()
fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
fh.setFormatter(fh_formatter)
logger.addHandler(fh)

สิ่งนี้จะพิมพ์ข้อความการดีบักของฉันออกมาเมื่อฉันทำ logger.debug ("ข้อความของฉัน!") แต่ยังพิมพ์ข้อความดีบักจากโมดูลใด ๆ ที่ฉันนำเข้า (เช่นคำขอและสิ่งอื่น ๆ อีกมากมาย)

ฉันต้องการดูเฉพาะข้อความบันทึกจากโมดูลที่ฉันสนใจเป็นไปได้ไหมที่จะทำให้โมดูลการบันทึกทำสิ่งนี้

ตามหลักการแล้วฉันต้องการบอกให้คนตัดไม้พิมพ์ข้อความจาก "ModuleX, ModuleY" และไม่ต้องสนใจคนอื่น ๆ ทั้งหมด

ฉันดูสิ่งต่อไปนี้ แต่ฉันไม่ต้องการปิด / เปิดใช้งานการบันทึกก่อนการเรียกใช้ฟังก์ชันที่นำเข้าทุกครั้ง: การบันทึก - จะเพิกเฉยต่อบันทึกโมดูลที่นำเข้าได้อย่างไร

คำตอบ:


69

ปัญหาคือการเรียกgetLoggerโดยไม่มีอาร์กิวเมนต์จะส่งกลับroot logger ดังนั้นเมื่อคุณตั้งค่าระดับเป็นlogging.DEBUGคุณกำลังตั้งค่าระดับสำหรับโมดูลอื่น ๆ ที่ใช้ตัวบันทึกนั้นด้วย

คุณสามารถแก้ปัญหานี้ได้โดยไม่ใช้ root logger ในการดำเนินการนี้เพียงแค่ส่งชื่อเป็นอาร์กิวเมนต์ตัวอย่างเช่นชื่อโมดูลของคุณ:

logger = logging.getLogger('my_module_name')
# as before

สิ่งนี้จะสร้างคนตัดไม้ใหม่และจะไม่เปลี่ยนระดับการบันทึกสำหรับโมดูลอื่นโดยไม่ได้ตั้งใจ


เห็นได้ชัดว่าคุณต้องใช้logger.debugแทนlogging.debugเนื่องจากฟังก์ชันหลังเป็นฟังก์ชันอำนวยความสะดวกที่เรียกใช้debugเมธอดของ root logger

นี้ถูกกล่าวถึงในการบันทึกข้อมูลทุกประเภทกวดวิชา นอกจากนี้ยังช่วยให้คุณทราบว่าโมดูลใดเรียกใช้ข้อความบันทึกด้วยวิธีง่ายๆ


37
ฉันกำลังสร้างคนตัดไม้ด้วย__name__r แต่ฉันยังเห็นบันทึกจากโมดูลที่นำเข้า ฉันพยายามกำหนดค่าการบันทึกด้วยไฟล์คอนฟิกูเรชัน ini ฉันควรทำอย่างไร
Durga Swaroop

8
การสร้างคนตัดไม้ด้วย__name__ก็ไม่ได้ผลสำหรับฉัน อาจเป็นเพราะฉันใช้สคริปต์แบบสแตนด์อโลนไม่ใช่ "โมดูล"? สิ่งที่ได้ผลสำหรับฉันคือกำหนดค่าการบันทึกสำหรับโมดูลที่นำเข้า ( matpplotlibในกรณีของฉัน) ผ่านlogging.getLogger("matplotlib").setLevel(logging.WARNING)และสำหรับสคริปต์ของฉันผ่านlogging.basicConfig.
bli

1
ฉันแค่อยากจะเน้นค่าของบรรทัดของคุณ "แน่นอนคุณต้องใช้logger.debugแทนlogging.debug" เป็นข้อผิดพลาดง่าย ๆ ในการใช้การบันทึกแทนการใช้คนตัดไม้แต่มันใช้การกำหนดค่าที่ชาญฉลาดทั้งหมดที่คุณต้องการตั้งค่า ฉันใช้เวลาสองสามชั่วโมงสุดท้ายในการใช้ชีวิตนี้!
timdadev

@bli มีความแตกต่างอย่างมากระหว่างการบันทึกเมื่อพัฒนาไลบรารีกับเมื่อพัฒนาไฟล์ปฏิบัติการ โดยทั่วไป: หากคุณกำลังเขียนโมดูล / แพ็คเกจที่ตั้งใจจะนำเข้าคุณไม่ควรกำหนดค่าอะไรเลย โมดูลควรมีเฉพาะlogger = logging.getLogger('package.my_module')และคุณสามารถโทรlogger.debug/warningได้ไม่มีการกำหนดค่าระดับการบันทึกหรือตัวจัดการ เมื่อคุณเขียนแอปพลิเคชันไบนารีที่นั่นคุณควรกำหนดระดับสำหรับบันทึกต่างๆและตัวจัดการ ไลบรารีที่มีการกำหนดค่าการบันทึกจะเป็นปัญหาเสมอ
Bakuriu

ในกรณีของฉันแพ็กเกจที่ฉันนำเข้าใช้ root logger (via logging.info) มีวิธีใดในการปิดใช้งานบันทึกรูทจากแพ็คเกจนี้โดยเฉพาะหรือไม่?
IanS

45

หากคุณจะใช้loggingแพคเกจpython มันเป็นแบบแผนทั่วไปในการกำหนดคนตัดไม้ในทุกโมดูลที่ใช้มัน

logger = logging.getLogger(__name__)

แพ็คเกจหลามยอดนิยมจำนวนมากทำสิ่งนี้รวมถึงrequests. หากแพ็กเกจใช้หลักการนี้คุณสามารถเปิด / ปิดใช้งานการบันทึกได้โดยง่ายเนื่องจากชื่อคนตัดไม้จะเป็นชื่อเดียวกับแพ็กเกจ (หรือจะเป็นลูกของคนตัดไม้นั้น) คุณสามารถบันทึกเป็นไฟล์เดียวกับคนตัดไม้คนอื่น ๆ ของคุณได้

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

requests_logger = logging.getLogger('requests')
requests_logger.setLevel(logging.DEBUG)

handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
requests_logger.addHandler(handler)

15
โปรดทราบว่าเมื่อคุณพยายามกำหนดค่าคนตัดไม้ของคุณเช่นในบทช่วยสอนพื้นฐานอย่างเป็นทางการที่มีlogging.basicConfig(...)คนตัดไม้ทั้งหมดตอนนี้จะส่งออกไปยังlogging.lastResort(เริ่มต้นด้วย Python 3.2 ซึ่งเป็น stderr) หากไม่มีตัวจัดการหรือตัวจัดการที่คุณตั้งไว้ ดังนั้นอย่าใช้มันมิฉะนั้นคุณจะได้รับข้อความบันทึกทั้งหมดต่อไป
user136036

44

ไม่แน่ใจว่าสิ่งนี้เหมาะสมหรือไม่ที่จะโพสต์ แต่ฉันติดอยู่เป็นเวลานานและต้องการช่วยเหลือทุกคนที่มีปัญหาเดียวกันเนื่องจากฉันไม่พบที่อื่น!

ผมได้รับการบันทึกจากการแก้ปัญหา matplotlib แม้จะมีดังต่อไปนี้เอกสารตรงไปตรงสวยที่เข้าสู่ระบบการสอนที่ทันสมัย และการแก้ไขปัญหา ฉันเริ่มต้นคนตัดไม้ของฉันในmain()ไฟล์เดียวและนำเข้าฟังก์ชันเพื่อสร้างพล็อตจากไฟล์อื่น (ที่ฉันนำเข้า matplotlib)

สิ่งที่ได้ผลสำหรับฉันคือการตั้งค่าระดับของ matplotlib ก่อนที่จะนำเข้าแทนที่จะเป็นหลังจากที่ฉันมีสำหรับโมดูลอื่น ๆ ในไฟล์หลักของฉัน สิ่งนี้ดูเหมือนจะสวนทางกับฉันดังนั้นหากใครมีข้อมูลเชิงลึกเกี่ยวกับวิธีที่คุณสามารถตั้งค่าการกำหนดค่าสำหรับคนตัดไม้ที่ยังไม่ได้นำเข้าฉันก็อยากรู้ว่ามันทำงานอย่างไร ขอบคุณ!

ในไฟล์หลักของฉัน:

import logging
import requests
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logging.getLogger('requests').setLevel(logging.DEBUG)

def main():
  ...

ในplot.pyไฟล์ของฉัน:

import logging
logging.getLogger('matplotlib').setLevel(logging.WARNING)
import matplotlib.pyplot as plt

def generatePlot():
  ...

ฉันได้รับข้อผิดพลาด: วัตถุ 'Logger' ไม่มีแอตทริบิวต์ 'DEBUG' logger.DEBUGควรจะเป็นlogging.DEBUG
foxiris

ขอบคุณ! ช่วยได้จริง! ฉันตั้งค่าระดับการบันทึก matplotlib หลังจากการกำหนดค่าการบันทึกหลักของฉันและก่อนคำสั่งที่จะนำเข้า matplotlib แก้ไขแล้ว!
GPH

ฉันได้ตั้งการบันทึกสำหรับmatplotlibการWARNING หลังจากที่ผมได้นำเข้าโมดูลเนื่องจากการเพิ่มก่อนนำเข้าจะให้ผ้าสำลีข้อผิดพลาด มันยังคงใช้งานได้สำหรับฉัน ฉันใช้matplotlib==3.3.2ใน Python 3.7 ถ้ามันช่วยได้
สิ้นสุด - 2 - สิ้นสุด

9

@ บาคุริวอธิบายฟังก์ชันได้อย่างสวยงาม ในทางกลับกันคุณสามารถใช้getLogger()วิธีการดึงข้อมูลและกำหนดค่าใหม่ / ปิดใช้งานตัวตัดไม้ที่ไม่ต้องการได้

ฉันยังต้องการเพิ่มlogging.fileConfig()วิธีการยอมรับพารามิเตอร์ที่เรียกว่าdisable_existing_loggersซึ่งจะปิดใช้งานตัวบันทึกใด ๆ ที่กำหนดไว้ก่อนหน้านี้ (เช่นในโมดูลที่นำเข้า)


9

สิ่งนี้จะปิดใช้งานตัวบันทึกที่มีอยู่ทั้งหมดเช่นที่สร้างโดยโมดูลที่อิมพอร์ตในขณะที่ยังคงใช้ root logger (และไม่ต้องโหลดไฟล์ภายนอก)

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': True,
})

โปรดทราบว่าคุณต้องนำเข้าโมดูลทั้งหมดที่คุณไม่ต้องการเข้าสู่ระบบก่อน! มิฉะนั้นจะไม่ถือว่าเป็น "คนตัดไม้ที่มีอยู่" จากนั้นจะปิดใช้งานคนตัดไม้ทั้งหมดจากโมดูลเหล่านั้น ซึ่งอาจทำให้คุณพลาดข้อผิดพลาดที่สำคัญได้เช่นกัน!

สำหรับตัวอย่างโดยละเอียดเพิ่มเติมเกี่ยวกับตัวเลือกที่เกี่ยวข้องสำหรับการกำหนดค่าโปรดดูที่https://gist.github.com/st4lk/6287746และนี่คือตัวอย่าง (ใช้งานได้บางส่วน) โดยใช้ YAML สำหรับการกำหนดค่ากับcoloredlogไลบรารี


คำถามของคุณคืออะไร?
user1767754

1
งานนี้requestเช่น แต่มันจะไม่ทำงานเมื่อโมดูลที่นำเข้ามาตัดไม้สร้างของพวกเขาภายในชั้นเรียนของตนที่คุณต้องการเรียกต่อมาเช่นไม่เมื่อคุณเรียกAPScheduler BackgroundScheduler.BackgroundScheduler()ดูวิธีแก้ปัญหาได้ที่นี่: stackoverflow.com/a/48891485/2441026
user136036

สิ่งนี้ใช้ได้กับกรณีของฉันด้วยไฟล์การกำหนดค่า
yaml

4

คุณสามารถใช้สิ่งต่างๆเช่น:

logging.getLogger("imported_module").setLevel(logging.WARNING)
logging.getLogger("my_own_logger_name").setLevel(logging.DEBUG)

สิ่งนี้จะตั้งค่าระดับการบันทึกของโมดูลของฉันเองเป็น DEBUG ในขณะที่ป้องกันไม่ให้โมดูลที่นำเข้ามาใช้ระดับเดียวกัน

หมายเหตุ: "imported_module"สามารถแทนที่ได้ด้วยimported_module.__name__(ไม่มีเครื่องหมายคำพูด) และ"my_own_logger_name"สามารถแทนที่ได้__name__หากเป็นวิธีที่คุณต้องการ


1

ผมมีปัญหาเหมือนกัน. ฉันมีไฟล์ logging_config.py ซึ่งฉันนำเข้าในไฟล์ py อื่น ๆ ทั้งหมด ในไฟล์ logging_config.py ฉันตั้งค่าระดับการบันทึก root logger เป็น ERROR (โดยค่าเริ่มต้นคำเตือน):

logging.basicConfig(
    handlers=[
        RotatingFileHandler('logs.log',maxBytes=1000, backupCount=2),
        logging.StreamHandler(), #print to console
    ],
    level=logging.ERROR
)

ในโมดูลอื่น ๆ ฉันนำเข้า logging_config.py และประกาศตัวบันทึกใหม่และตั้งค่าระดับเป็นดีบัก:

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

ด้วยวิธีนี้ทุกอย่างที่ฉันเข้าสู่ระบบไฟล์ py ของฉันจะถูกบันทึก แต่สิ่งที่บันทึกไว้ในระดับดีบักและข้อมูลโดยโมดูลที่นำเข้าเช่น urllib, request, boto3 ฯลฯ จะไม่ถูกบันทึก หากมีข้อผิดพลาดบางอย่างในโมดูลการนำเข้าเหล่านั้นแสดงว่าถูกบันทึกเนื่องจากฉันตั้งค่าระดับ root loggers เป็น ERROR


0

สิ่งที่ต้องพิจารณาอีกประการหนึ่งคือคุณสมบัติการแพร่กระจายของคลาส Logger

ตัวอย่างเช่นไลบรารี py-suds สำหรับจัดการการโทรแบบสบู่แม้กระทั่งใส่ ERROR

logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)

บันทึกบันทึกเกี่ยวกับโมดูลที่เรียกว่า sxbasics.py creationg บันทึกจำนวนมาก

ป้อนคำอธิบายภาพที่นี่

เนื่องจากการแพร่กระจายของบันทึกเป็น True โดยค่าเริ่มต้นตั้งค่าเป็น False แทนฉันจึงกู้คืนบันทึก 514MB

import logging
logging.getLogger("suds").propagate = False
logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.