วิธีเพิ่ม loglevel ที่กำหนดเองให้กับระบบบันทึกของ Python


116

ฉันต้องการ loglevel TRACE (5) สำหรับแอปพลิเคชันของฉันเพราะฉันคิดว่าdebug()มันไม่เพียงพอ นอกจากlog(5, msg)นี้ไม่ใช่สิ่งที่ฉันต้องการ ฉันจะเพิ่ม loglevel ที่กำหนดเองลงใน Python logger ได้อย่างไร

ฉันmylogger.pyมีเนื้อหาดังต่อไปนี้:

import logging

@property
def log(obj):
    myLogger = logging.getLogger(obj.__class__.__name__)
    return myLogger

ในรหัสของฉันฉันใช้ในลักษณะต่อไปนี้:

class ExampleClass(object):
    from mylogger import log

    def __init__(self):
        '''The constructor with the logger'''
        self.log.debug("Init runs")

ตอนนี้ฉันต้องการโทร self.log.trace("foo bar")

ขอบคุณล่วงหน้าสำหรับความช่วยเหลือของ.

แก้ไข (8 ธันวาคม 2016): ผมเปลี่ยนคำตอบที่ได้รับการยอมรับไปของ PFAซึ่งเป็น IMHO, ทางออกที่ดีขึ้นอยู่กับข้อเสนอที่ดีมากจากเอริคเอส

คำตอบ:


171

@ เอริกเอส.

คำตอบของ Eric S. นั้นยอดเยี่ยมมาก แต่ฉันได้เรียนรู้จากการทดลองว่าสิ่งนี้จะทำให้ข้อความที่บันทึกในระดับการดีบักใหม่ถูกพิมพ์เสมอไม่ว่าจะตั้งค่าระดับการบันทึกไว้ที่ระดับใดก็ตาม ดังนั้นถ้าคุณทำให้จำนวนระดับใหม่ของ9ถ้าคุณโทรsetLevel(50)ในระดับที่ต่ำกว่าข้อความจะไม่สมควรได้รับการพิมพ์

เพื่อป้องกันไม่ให้เกิดขึ้นคุณต้องมีบรรทัดอื่นในฟังก์ชัน "debugv" เพื่อตรวจสอบว่าระดับการบันทึกที่เป็นปัญหาถูกเปิดใช้งานจริงหรือไม่

ตัวอย่างคงที่ที่ตรวจสอบว่าระดับการบันทึกถูกเปิดใช้งานหรือไม่:

import logging
DEBUG_LEVELV_NUM = 9 
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
    if self.isEnabledFor(DEBUG_LEVELV_NUM):
        # Yes, logger takes its '*args' as 'args'.
        self._log(DEBUG_LEVELV_NUM, message, args, **kws) 
logging.Logger.debugv = debugv

หากคุณดูโค้ดสำหรับclass Loggerในlogging.__init__.pyPython 2.7 นี่คือสิ่งที่ฟังก์ชันบันทึกมาตรฐานทั้งหมดทำ (.critical, .debug ฯลฯ )

เห็นได้ชัดว่าฉันไม่สามารถโพสต์คำตอบสำหรับคำตอบของคนอื่นได้เนื่องจากไม่มีชื่อเสียง ... หวังว่า Eric จะอัปเดตโพสต์ของเขาหากเขาเห็นสิ่งนี้ =)


7
นี่เป็นคำตอบที่ดีกว่าเนื่องจากตรวจสอบระดับการบันทึกอย่างถูกต้อง
พันเอก Panic

2
ให้ข้อมูลมากกว่าคำตอบปัจจุบันอย่างแน่นอน
Mad Physicist

4
@pfa แล้วการเพิ่มlogging.DEBUG_LEVEL_NUM = 9เพื่อให้คุณสามารถเข้าถึงระดับดีบักนั้นได้ทุกที่ที่คุณนำเข้าคนตัดไม้ในรหัสของคุณ
edgarstack

4
แน่นอนแทนคุณควรกำหนดDEBUG_LEVEL_NUM = 9 logging.DEBUG_LEVEL_NUM = 9วิธีนี้คุณจะสามารถใช้log_instance.setLevel(logging.DEBUG_LEVEL_NUM)แบบเดียวกับที่คุณใช้ right know logging.DEBUGหรือlogging.INFO
maQ

คำตอบนี้มีประโยชน์มาก ขอบคุณ pfa และ EricS ฉันอยากจะแนะนำว่าเพื่อความสมบูรณ์ให้เพิ่มอีกสองคำสั่ง: logging.DEBUGV = DEBUG_LEVELV_NUMและlogging.__all__ += ['DEBUGV'] ข้อที่สองไม่สำคัญมาก แต่อย่างแรกมีความจำเป็นหากคุณมีรหัสใด ๆ ที่ปรับระดับการบันทึกแบบไดนามิกและคุณต้องการที่จะสามารถทำสิ่งต่างๆเช่นif verbose: logger.setLevel(logging.DEBUGV)`
คี ธ Hanlan

63

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

import logging
DEBUG_LEVELV_NUM = 9 
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
    # Yes, logger takes its '*args' as 'args'.
    self._log(DEBUG_LEVELV_NUM, message, args, **kws) 
logging.Logger.debugv = debugv

7
+1 ด้วยครับ. แนวทางที่สง่างามและทำงานได้อย่างสมบูรณ์แบบ หมายเหตุสำคัญ: คุณจะต้องทำเช่นนี้ครั้งเดียวในโมดูลเดียวและมันจะทำงานสำหรับโมดูลทั้งหมด คุณไม่จำเป็นต้องนำเข้าโมดูล "การตั้งค่า" ดังนั้นโยนสิ่งนี้ในแพ็คเกจ__init__.pyและมีความสุข: D
MestreLion

4
@Eric S. คุณควรดูคำตอบนี้: stackoverflow.com/a/13638084/600110
Sam Mussmann

1
ฉันเห็นด้วยกับ @SamMussmann ฉันพลาดคำตอบนั้นเพราะนี่เป็นคำตอบที่ได้รับการโหวตสูงสุด
พันเอก Panic

@ เอริกเอสทำไมคุณถึงต้องการ args โดยไม่ต้อง *? ถ้าฉันทำเช่นนั้นฉันจะได้รับTypeError: not all arguments converted during string formattingแต่มันก็ใช้ได้ดีกับ * (Python 3.4.3) มันเป็นปัญหาเวอร์ชัน python หรือฉันยังขาดอะไรไป?
ปีเตอร์

คำตอบนี้ใช้ไม่ได้กับฉัน การพยายามทำ 'logging.debugv' ทำให้เกิดข้อผิดพลาดAttributeError: module 'logging' has no attribute 'debugv'
Alex

51

เมื่อรวมคำตอบที่มีอยู่ทั้งหมดเข้ากับประสบการณ์การใช้งานมากมายฉันคิดว่าฉันได้สร้างรายการสิ่งที่ต้องทำทั้งหมดเพื่อให้แน่ใจว่าการใช้งานระดับใหม่จะราบรื่นอย่างสมบูรณ์ ขั้นตอนด้านล่างนี้ถือว่าคุณกำลังเพิ่มระดับใหม่TRACEด้วยค่าlogging.DEBUG - 5 == 5 :

  1. logging.addLevelName(logging.DEBUG - 5, 'TRACE') จำเป็นต้องเรียกใช้เพื่อรับระดับใหม่ที่ลงทะเบียนภายในเพื่อให้สามารถอ้างอิงด้วยชื่อได้
  2. ระดับใหม่จะต้องมีการเพิ่มเป็นแอตทริบิวต์เพื่อตัวเองเพื่อความมั่นคง:logginglogging.TRACE = logging.DEBUG - 5
  3. traceต้องเพิ่มเมธอดที่เรียกว่าลงในloggingโมดูล มันควรประพฤติเช่นเดียวdebug, infoฯลฯ
  4. traceต้องเพิ่มเมธอดที่เรียกว่าไปยังคลาส logger ที่กำหนดค่าไว้ในปัจจุบัน เนื่องจากไม่รับประกัน 100% ให้logging.Loggerใช้logging.getLoggerClass()แทน

ขั้นตอนทั้งหมดแสดงในวิธีการด้านล่าง:

def addLoggingLevel(levelName, levelNum, methodName=None):
    """
    Comprehensively adds a new logging level to the `logging` module and the
    currently configured logging class.

    `levelName` becomes an attribute of the `logging` module with the value
    `levelNum`. `methodName` becomes a convenience method for both `logging`
    itself and the class returned by `logging.getLoggerClass()` (usually just
    `logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
    used.

    To avoid accidental clobberings of existing attributes, this method will
    raise an `AttributeError` if the level name is already an attribute of the
    `logging` module or if the method name is already present 

    Example
    -------
    >>> addLoggingLevel('TRACE', logging.DEBUG - 5)
    >>> logging.getLogger(__name__).setLevel("TRACE")
    >>> logging.getLogger(__name__).trace('that worked')
    >>> logging.trace('so did this')
    >>> logging.TRACE
    5

    """
    if not methodName:
        methodName = levelName.lower()

    if hasattr(logging, levelName):
       raise AttributeError('{} already defined in logging module'.format(levelName))
    if hasattr(logging, methodName):
       raise AttributeError('{} already defined in logging module'.format(methodName))
    if hasattr(logging.getLoggerClass(), methodName):
       raise AttributeError('{} already defined in logger class'.format(methodName))

    # This method was inspired by the answers to Stack Overflow post
    # http://stackoverflow.com/q/2183233/2988730, especially
    # http://stackoverflow.com/a/13638084/2988730
    def logForLevel(self, message, *args, **kwargs):
        if self.isEnabledFor(levelNum):
            self._log(levelNum, message, args, **kwargs)
    def logToRoot(message, *args, **kwargs):
        logging.log(levelNum, message, *args, **kwargs)

    logging.addLevelName(levelNum, levelName)
    setattr(logging, levelName, levelNum)
    setattr(logging.getLoggerClass(), methodName, logForLevel)
    setattr(logging, methodName, logToRoot)

เรียงคำตอบตามOldestและคุณจะประทับใจว่านี่คือคำตอบที่ดีที่สุดของพวกเขาทั้งหมด!
Serge Stroobandt

ขอบคุณ ฉันได้ทำงานหลายอย่างด้วยกันและ QA นี้มีประโยชน์มากดังนั้นฉันจึงพยายามเพิ่มบางอย่างเข้าไป
บ้าฟิสิกส์

1
@PeterDolan โปรดแจ้งให้เราทราบหากคุณประสบปัญหานี้ ในกล่องเครื่องมือส่วนตัวของฉันฉันมีเวอร์ชันเพิ่มเติมที่ให้คุณกำหนดค่าวิธีจัดการกับคำจำกัดความระดับที่ขัดแย้งกัน ที่เกิดขึ้นสำหรับฉันครั้งเดียวเพราะฉันต้องการเพิ่มระดับ TRACE และหนึ่งในองค์ประกอบของสฟิงซ์ก็เช่นกัน
Mad Physicist

1
การไม่มีเครื่องหมายดอกจันอยู่ข้างหน้าargsในการlogForLevelดำเนินการโดยเจตนา / จำเป็นหรือไม่
Chris L. Barnes

1
@Tunisia มันไม่ได้ตั้งใจ ขอบคุณสำหรับการจับ
Mad Physicist

40

คำถามนี้ค่อนข้างเก่า แต่ฉันเพิ่งจัดการกับหัวข้อเดียวกันและพบวิธีที่คล้ายกับคำถามที่กล่าวไปแล้วซึ่งดูสะอาดกว่าสำหรับฉันเล็กน้อย สิ่งนี้ได้รับการทดสอบบน 3.4 ดังนั้นฉันไม่แน่ใจว่าวิธีการที่ใช้มีอยู่ในเวอร์ชันเก่าหรือไม่:

from logging import getLoggerClass, addLevelName, setLoggerClass, NOTSET

VERBOSE = 5

class MyLogger(getLoggerClass()):
    def __init__(self, name, level=NOTSET):
        super().__init__(name, level)

        addLevelName(VERBOSE, "VERBOSE")

    def verbose(self, msg, *args, **kwargs):
        if self.isEnabledFor(VERBOSE):
            self._log(VERBOSE, msg, args, **kwargs)

setLoggerClass(MyLogger)

1
นี่คือคำตอบที่ดีที่สุดของ IMHO เนื่องจากหลีกเลี่ยงการปะลิง อะไรgetและsetLoggerClassตรงทำและทำไมพวกเขามีความจำเป็น?
Marco Sulla

3
@MarcoSulla เอกสารเหล่านี้เป็นส่วนหนึ่งของโมดูลการบันทึกของ Python ฉันคิดว่าการแบ่งคลาสย่อยแบบไดนามิกใช้ในกรณีที่มีคนต้องการ llogger ของตัวเองในขณะที่ใช้ไลบรารีนี้ MyLogger นี้จะกลายเป็นคลาสย่อยของคลาสของฉันโดยรวมทั้งสองเข้าด้วยกัน
CrackerJack9

สิ่งนี้คล้ายกับโซลูชันที่นำเสนอในการสนทนานี้มากว่าจะเพิ่มTRACEระดับให้กับไลบรารีการบันทึกเริ่มต้นหรือไม่ +1
IMP1

18

ใครเป็นผู้เริ่มต้นการใช้วิธีการภายในที่ไม่ดี ( self._log) และทำไมคำตอบแต่ละข้อจึงเป็นไปตามนั้น! วิธีแก้ปัญหา pythonic จะใช้self.logแทนดังนั้นคุณไม่ต้องยุ่งกับสิ่งภายในใด ๆ :

import logging

SUBDEBUG = 5
logging.addLevelName(SUBDEBUG, 'SUBDEBUG')

def subdebug(self, message, *args, **kws):
    self.log(SUBDEBUG, message, *args, **kws) 
logging.Logger.subdebug = subdebug

logging.basicConfig()
l = logging.getLogger()
l.setLevel(SUBDEBUG)
l.subdebug('test')
l.setLevel(logging.DEBUG)
l.subdebug('test')

18
จำเป็นต้องใช้ _log () แทน log () เพื่อหลีกเลี่ยงการเพิ่มระดับพิเศษใน call stack หากใช้ log () การแนะนำของสแต็กเฟรมพิเศษจะทำให้แอตทริบิวต์ LogRecord หลายตัว (funcName, lineno, filename, pathname, ... ) ชี้ไปที่ฟังก์ชัน debug แทนที่จะเป็นตัวเรียกใช้จริง นี่อาจไม่ใช่ผลลัพธ์ที่ต้องการ
rivy

5
ตั้งแต่เมื่อใดที่ไม่อนุญาตให้เรียกใช้เมธอดภายในของคลาส เพียงเพราะฟังก์ชันถูกกำหนดไว้ภายนอกคลาสไม่ได้หมายความว่าเป็นวิธีการภายนอก
OozeMeister

3
วิธีนี้ไม่เพียง แต่แก้ไขการติดตามสแต็กโดยไม่จำเป็น แต่ยังไม่ได้ตรวจสอบว่ากำลังบันทึกระดับที่ถูกต้อง
Mad Physicist

ฉันรู้สึกว่าสิ่งที่ @schlamar พูดนั้นถูกต้อง แต่เหตุผลที่โต้แย้งได้รับคะแนนโหวตเท่ากัน แล้วจะใช้อะไรดีล่ะ?
Sumit Murari

1
เหตุใดวิธีการหนึ่งจึงไม่ใช้วิธีการภายใน?
Gringo Suave

9

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

import logging

@property
def log(obj):
    logging.addLevelName(5, 'TRACE')
    myLogger = logging.getLogger(obj.__class__.__name__)
    setattr(myLogger, 'trace', lambda *args: myLogger.log(5, *args))
    return myLogger

ตอนนี้

mylogger.trace('This is a trace message')

ควรทำงานตามที่คาดไว้


นี่จะไม่มีผลงานตีเล็กน้อยเทียบกับคลาสย่อยหรือไม่? ด้วยวิธีนี้ทุกครั้งที่บางคนขอคนตัดไม้พวกเขาจะต้องโทรหา setattr คุณอาจรวมสิ่งเหล่านี้เข้าด้วยกันในคลาสที่กำหนดเอง แต่อย่างไรก็ตาม setattr นั้นจะต้องถูกเรียกใช้กับคนตัดไม้ทุกคนที่สร้างขึ้นใช่ไหม?
Matthew Lund

@Zbigniew ด้านล่างแสดงให้เห็นนี้ไม่ได้ทำงานซึ่งผมคิดว่าเป็นเพราะความต้องการของคนตัดไม้ของคุณเพื่อให้การเรียกร้องที่จะไม่_log log
ประดับเครื่องหมาย

9

ในขณะที่เรามีคำตอบที่ถูกต้องมากมายแล้วสิ่งต่อไปนี้อยู่ในความคิดของฉัน pythonic มากกว่า:

import logging

from functools import partial, partialmethod

logging.TRACE = 5
logging.addLevelName(logging.TRACE, 'TRACE')
logging.Logger.trace = partialmethod(logging.Logger.log, logging.TRACE)
logging.trace = partial(logging.log, logging.TRACE)

หากคุณต้องการใช้mypyกับรหัสของคุณขอแนะนำให้เพิ่ม# type: ignoreเพื่อระงับคำเตือนจากการเพิ่มแอตทริบิวต์


1
มันดูดี แต่บรรทัดสุดท้ายทำให้สับสน ควรlogging.trace = partial(logging.log, logging.TRACE) # type: ignoreหรือไม่?
Sergey Nudnov

@SergeyNudnov ขอบคุณที่ชี้ให้เห็นฉันแก้ไขแล้ว เป็นความผิดพลาดจากด้านข้างของฉันฉันเพิ่งคัดลอกจากรหัสของฉันและดูเหมือนจะทำให้การทำความสะอาดยุ่งเหยิง
DerWeh

8

ฉันคิดว่าคุณจะต้องซับคลาสLoggerเรียนและเพิ่มวิธีการที่เรียกว่าtraceซึ่งโดยทั่วไปเรียกร้องที่มีระดับต่ำกว่าLogger.log DEBUGฉันไม่ได้พยายามนี้ แต่นี่คือสิ่งที่เอกสารบ่งชี้


3
และคุณอาจต้องการแทนที่logging.getLoggerเพื่อส่งคืนคลาสย่อยของคุณแทนคลาสในตัว
ล็อต

4
@ S.Lott - จริงๆแล้ว (อย่างน้อยก็กับ Python เวอร์ชันปัจจุบันอาจจะไม่ใช่กรณีย้อนกลับไปในปี 2010) คุณต้องใช้setLoggerClass(MyClass)แล้วโทรgetLogger()ตามปกติ ...
mac

IMO นี่เป็นคำตอบที่ดีที่สุด (และ Pythonic ส่วนใหญ่) และถ้าฉันสามารถให้ +1 ได้หลายตัวฉันก็จะทำได้ มันง่ายที่จะดำเนินการ แต่โค้ดตัวอย่างก็น่าจะดี :-D
Doug R.

@ DougR ขอบคุณ แต่อย่างที่บอกยังไม่ได้ลอง :)
Noufal Ibrahim

6

เคล็ดลับในการสร้างคนตัดไม้แบบกำหนดเอง:

  1. ไม่ใช้_logใช้log(ไม่ต้องตรวจสอบisEnabledFor)
  2. โมดูลการบันทึกควรเป็นอินสแตนซ์ที่สร้างขึ้นของคนตัดไม้ที่กำหนดเองเนื่องจากมันทำเวทมนตร์บางอย่างgetLoggerดังนั้นคุณจะต้องตั้งค่าคลาสผ่านsetLoggerClass
  3. คุณไม่จำเป็นต้องกำหนด__init__ให้กับคนตัดไม้คลาสถ้าคุณไม่ได้จัดเก็บอะไรเลย
# Lower than debug which is 10
TRACE = 5
class MyLogger(logging.Logger):
    def trace(self, msg, *args, **kwargs):
        self.log(TRACE, msg, *args, **kwargs)

เมื่อเรียกคนตัดไม้นี้setLoggerClass(MyLogger)ให้ใช้เพื่อกำหนดให้เป็นคนตัดไม้เริ่มต้นจากgetLogger

logging.setLoggerClass(MyLogger)
log = logging.getLogger(__name__)
# ...
log.trace("something specific")

คุณจะต้องsetFormatter, setHandlerและsetLevel(TRACE)บนhandlerและบนlogตัวเองจริง se นี้ร่องรอยระดับต่ำ


3

สิ่งนี้ใช้ได้ผลสำหรับฉัน:

import logging
logging.basicConfig(
    format='  %(levelname)-8.8s %(funcName)s: %(message)s',
)
logging.NOTE = 32  # positive yet important
logging.addLevelName(logging.NOTE, 'NOTE')      # new level
logging.addLevelName(logging.CRITICAL, 'FATAL') # rename existing

log = logging.getLogger(__name__)
log.note = lambda msg, *args: log._log(logging.NOTE, msg, args)
log.note('school\'s out for summer! %s', 'dude')
log.fatal('file not found.')

ปัญหา lambda / funcName ได้รับการแก้ไขด้วย logger._log ตามที่ @marqueed ระบุ ฉันคิดว่าการใช้แลมบ์ดาดูสะอาดกว่าเล็กน้อย แต่ข้อเสียคือไม่สามารถใช้อาร์กิวเมนต์คำหลักได้ ฉันไม่เคยใช้ที่ตัวเองดังนั้นจึงไม่มี biggie

  การตั้งค่าหมายเหตุ: โรงเรียนปิดเทอมฤดูร้อน! เพื่อน
  ตั้งค่า FATAL: ไม่พบไฟล์

2

จากประสบการณ์ของฉันนี่เป็นวิธีแก้ปัญหาทั้งหมดของ op ... เพื่อหลีกเลี่ยงการมองว่า "lambda" เป็นฟังก์ชันที่ส่งข้อความไปให้ลึกลงไป:

MY_LEVEL_NUM = 25
logging.addLevelName(MY_LEVEL_NUM, "MY_LEVEL_NAME")
def log_at_my_log_level(self, message, *args, **kws):
    # Yes, logger takes its '*args' as 'args'.
    self._log(MY_LEVEL_NUM, message, args, **kws)
logger.log_at_my_log_level = log_at_my_log_level

ฉันไม่เคยลองทำงานกับคลาสคนตัดไม้แบบสแตนด์อโลน แต่ฉันคิดว่าแนวคิดพื้นฐานเหมือนกัน (ใช้ _log)


ฉันไม่คิดว่าจะได้ผล คุณไม่จำเป็นต้องloggerเป็นอาร์กิวเมนต์แรกในlog_at_my_log_level?
พอล

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

2

นอกเหนือจากตัวอย่าง Mad Physicists เพื่อให้ได้ชื่อไฟล์และหมายเลขบรรทัดที่ถูกต้อง:

def logToRoot(message, *args, **kwargs):
    if logging.root.isEnabledFor(levelNum):
        logging.root._log(levelNum, message, args, **kwargs)

1

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

def set_custom_logging_levels(config={}):
    """
        Assign custom levels for logging
            config: is a dict, like
            {
                'EVENT_NAME': EVENT_LEVEL_NUM,
            }
        EVENT_LEVEL_NUM can't be like already has logging module
        logging.DEBUG       = 10
        logging.INFO        = 20
        logging.WARNING     = 30
        logging.ERROR       = 40
        logging.CRITICAL    = 50
    """
    assert isinstance(config, dict), "Configuration must be a dict"

    def get_level_func(level_name, level_num):
        def _blank(self, message, *args, **kws):
            if self.isEnabledFor(level_num):
                # Yes, logger takes its '*args' as 'args'.
                self._log(level_num, message, args, **kws) 
        _blank.__name__ = level_name.lower()
        return _blank

    for level_name, level_num in config.items():
        logging.addLevelName(level_num, level_name.upper())
        setattr(logging.Logger, level_name.lower(), get_level_func(level_name, level_num))

config อาจ smth เช่นนั้น:

new_log_levels = {
    # level_num is in logging.INFO section, that's why it 21, 22, etc..
    "FOO":      21,
    "BAR":      22,
}

0

ทางเลือกในการเพิ่มเมธอดพิเศษในคลาส Logger ฉันขอแนะนำให้ใช้Logger.log(level, msg)เมธอดนี้

import logging

TRACE = 5
logging.addLevelName(TRACE, 'TRACE')
FORMAT = '%(levelname)s:%(name)s:%(lineno)d:%(message)s'


logging.basicConfig(format=FORMAT)
l = logging.getLogger()
l.setLevel(TRACE)
l.log(TRACE, 'trace message')
l.setLevel(logging.DEBUG)
l.log(TRACE, 'disabled trace message')

0

ฉันสับสน; ด้วย python 3.5 อย่างน้อยก็ใช้งานได้:

import logging


TRACE = 5
"""more detail than debug"""

logging.basicConfig()
logging.addLevelName(TRACE,"TRACE")
logger = logging.getLogger('')
logger.debug("n")
logger.setLevel(logging.DEBUG)
logger.debug("y1")
logger.log(TRACE,"n")
logger.setLevel(TRACE)
logger.log(TRACE,"y2")
    

เอาท์พุท:

DEBUG: ราก: y1

TRACE: ราก: y2


1
สิ่งนี้ไม่ยอมให้คุณทำในสิ่งlogger.trace('hi')ที่ฉันเชื่อว่าเป็นเป้าหมายหลัก
Ultimation

-3

ในกรณีที่ใครก็ตามต้องการวิธีอัตโนมัติในการเพิ่มระดับการบันทึกใหม่ให้กับโมดูลการบันทึก (หรือสำเนา) แบบไดนามิกฉันได้สร้างฟังก์ชันนี้โดยขยายคำตอบของ @ pfa:

def add_level(log_name,custom_log_module=None,log_num=None,
                log_call=None,
                   lower_than=None, higher_than=None, same_as=None,
              verbose=True):
    '''
    Function to dynamically add a new log level to a given custom logging module.
    <custom_log_module>: the logging module. If not provided, then a copy of
        <logging> module is used
    <log_name>: the logging level name
    <log_num>: the logging level num. If not provided, then function checks
        <lower_than>,<higher_than> and <same_as>, at the order mentioned.
        One of those three parameters must hold a string of an already existent
        logging level name.
    In case a level is overwritten and <verbose> is True, then a message in WARNING
        level of the custom logging module is established.
    '''
    if custom_log_module is None:
        import imp
        custom_log_module = imp.load_module('custom_log_module',
                                            *imp.find_module('logging'))
    log_name = log_name.upper()
    def cust_log(par, message, *args, **kws):
        # Yes, logger takes its '*args' as 'args'.
        if par.isEnabledFor(log_num):
            par._log(log_num, message, args, **kws)
    available_level_nums = [key for key in custom_log_module._levelNames
                            if isinstance(key,int)]

    available_levels = {key:custom_log_module._levelNames[key]
                             for key in custom_log_module._levelNames
                            if isinstance(key,str)}
    if log_num is None:
        try:
            if lower_than is not None:
                log_num = available_levels[lower_than]-1
            elif higher_than is not None:
                log_num = available_levels[higher_than]+1
            elif same_as is not None:
                log_num = available_levels[higher_than]
            else:
                raise Exception('Infomation about the '+
                                'log_num should be provided')
        except KeyError:
            raise Exception('Non existent logging level name')
    if log_num in available_level_nums and verbose:
        custom_log_module.warn('Changing ' +
                                  custom_log_module._levelNames[log_num] +
                                  ' to '+log_name)
    custom_log_module.addLevelName(log_num, log_name)

    if log_call is None:
        log_call = log_name.lower()

    setattr(custom_log_module.Logger, log_call, cust_log)
    return custom_log_module

1
ประเมินภายในผู้บริหาร ว้าว.
Mad Physicist

2
..... ไม่รู้ว่าอะไรทำให้ฉันทำแบบนี้ .... ผ่านไปหลายเดือนฉันจะเปลี่ยนคำพูดนี้กับ a setattrแทนอย่างมีความสุข...
Vasilis Lemonidis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.