Cách thêm loglevel tùy chỉnh vào cơ sở ghi nhật ký của Python


116

Tôi muốn có TRACE cấp loglevel (5) cho ứng dụng của mình, vì tôi không nghĩ rằng điều đó debug()là đủ. Ngoài ra, log(5, msg)không phải là những gì tôi muốn. Làm cách nào để thêm loglevel tùy chỉnh vào trình ghi Python?

Tôi có một mylogger.pynội dung sau:

import logging

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

Trong mã của tôi, tôi sử dụng nó theo cách sau:

class ExampleClass(object):
    from mylogger import log

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

Bây giờ tôi muốn gọi self.log.trace("foo bar")

Cảm ơn trước sự giúp đỡ của bạn.

Chỉnh sửa (ngày 8 tháng 12 năm 2016): Tôi đã thay đổi câu trả lời được chấp nhận thành pfa's , đó là IMHO, một giải pháp tuyệt vời dựa trên đề xuất rất tốt từ Eric S.

Câu trả lời:


171

@Eric S.

Câu trả lời của Eric S. là tuyệt vời, nhưng tôi đã học được bằng thử nghiệm rằng điều này sẽ luôn khiến các thông báo được ghi ở cấp gỡ lỗi mới được in ra - bất kể mức nhật ký được đặt là gì. Vì vậy, nếu bạn tạo một số cấp mới 9, nếu bạn gọi setLevel(50), các tin nhắn cấp thấp hơn sẽ được in sai.

Để ngăn điều đó xảy ra, bạn cần một dòng khác bên trong hàm "debugv" để kiểm tra xem cấp độ ghi nhật ký được đề cập có thực sự được bật hay không.

Đã sửa ví dụ kiểm tra xem mức ghi nhật ký có được bật hay không:

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

Nếu bạn nhìn vào các mã cho class Loggertrong logging.__init__.pycho Python 2.7, đây là những gì tất cả các chức năng nhật ký chuẩn làm (.critical, .debug, vv).

Tôi dường như không thể đăng trả lời câu trả lời của người khác vì thiếu danh tiếng ... hy vọng Eric sẽ cập nhật bài đăng của mình nếu anh ấy thấy điều này. =)


7
Đây là câu trả lời tốt hơn vì nó kiểm tra chính xác mức độ nhật ký.
Colonel Panic

2
Chắc chắn nhiều thông tin hơn câu trả lời hiện tại.
Mad Physicist

4
@pfa Còn việc thêm logging.DEBUG_LEVEL_NUM = 9để bạn có thể truy cập mức gỡ lỗi đó ở mọi nơi bạn nhập trình ghi nhật ký vào mã của mình?
edgarstack,

4
Chắc chắn thay vào đó DEBUG_LEVEL_NUM = 9bạn nên xác định logging.DEBUG_LEVEL_NUM = 9. Bằng cách này bạn sẽ có thể sử dụng log_instance.setLevel(logging.DEBUG_LEVEL_NUM)cùng một cách như bạn sử dụng bí quyết đúng logging.DEBUGhaylogging.INFO
MAQ

Câu trả lời này đã rất hữu ích. Cảm ơn pfa và EricS. Tôi muốn đề nghị rằng để hoàn thiện, hãy thêm hai câu lệnh nữa: logging.DEBUGV = DEBUG_LEVELV_NUMlogging.__all__ += ['DEBUGV'] Câu thứ hai không quá quan trọng nhưng câu thứ nhất là cần thiết nếu bạn có bất kỳ mã nào tự động điều chỉnh mức độ ghi nhật ký và bạn muốn có thể làm điều gì đó tương tự như vậy if verbose: logger.setLevel(logging.DEBUGV)'
Keith Hanlan 19/1218

63

Tôi đã chọn câu trả lời "tránh nhìn thấy lambda" và phải sửa đổi nơi log_at_my_log_level đang được thêm vào. Tôi cũng đã thấy vấn đề mà Paul đã làm "Tôi không nghĩ điều này hiệu quả. Bạn không cần trình ghi nhật ký như đối số đầu tiên trong log_at_my_log_level?" Điều này đã làm việc cho tôi

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 nữa. Một cách tiếp cận thanh lịch và nó hoạt động hoàn hảo. Một lưu ý quan trọng: Bạn chỉ cần thực hiện việc này một lần, trong một mô-đun duy nhất và nó sẽ hoạt động cho tất cả các mô-đun . Bạn thậm chí không phải nhập mô-đun "thiết lập". Vì vậy, quăng này trong một gói của __init__.pyvà được hạnh phúc: D
MestreLion

4
@ Eric S. Bạn nên hãy xem câu trả lời này: stackoverflow.com/a/13638084/600110
Sam Mussmann

1
Tôi đồng ý với @SamMussmann. Tôi đã bỏ lỡ câu trả lời đó vì đây là câu trả lời được bình chọn hàng đầu.
Colonel Panic

@Eric S. Tại sao bạn cần args mà không có *? Nếu tôi làm điều đó, tôi nhận được TypeError: not all arguments converted during string formattingnhưng nó hoạt động tốt với *. (Python 3.4.3). Đó có phải là sự cố phiên bản python hay tôi đang thiếu thứ gì đó?
Peter

Câu trả lời này không phù hợp với tôi. Cố gắng thực hiện 'logging.debugv' thì gặp lỗiAttributeError: module 'logging' has no attribute 'debugv'
Alex

51

Kết hợp tất cả các câu trả lời hiện có với một loạt kinh nghiệm sử dụng, tôi nghĩ rằng tôi đã đưa ra một danh sách tất cả những việc cần phải làm để đảm bảo sử dụng cấp độ mới hoàn toàn liền mạch. Các bước bên dưới giả định rằng bạn đang thêm một cấp độ mới TRACEvới giá trị logging.DEBUG - 5 == 5:

  1. logging.addLevelName(logging.DEBUG - 5, 'TRACE') cần được gọi để có được cấp độ mới được đăng ký nội bộ để nó có thể được tham chiếu theo tên.
  2. Các cấp độ mới cần phải được thêm vào như là một thuộc tính để loggingbản thân cho phù hợp: logging.TRACE = logging.DEBUG - 5.
  3. Một phương thức được gọi tracecần được thêm vào loggingmô-đun. Nó nên cư xử giống như debug, infovv
  4. Một phương thức được gọi tracecần được thêm vào lớp trình ghi nhật ký hiện được cấu hình. Vì điều này không được đảm bảo 100% logging.Logger, hãy sử dụng logging.getLoggerClass()thay thế.

Tất cả các bước được minh họa trong phương pháp dưới đây:

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)

Sắp xếp các câu trả lời theo Oldest, và bạn sẽ đánh giá cao rằng đây là câu trả lời hay nhất trong số đó!
Serge Stroobandt

Cảm ơn. Tôi đã thực hiện khá nhiều công việc tập hợp một số thứ như thế này cùng nhau và QA này rất hữu ích, vì vậy tôi đã cố gắng thêm một số thứ vào.
Mad Physicist

1
@PeterDolan. Hãy cho tôi biết nếu bạn gặp khó khăn với điều này. Trong hộp công cụ cá nhân của tôi, tôi có một phiên bản mở rộng cho phép bạn định cấu hình cách xử lý các định nghĩa mức xung đột. Điều đó xuất hiện với tôi một lần vì tôi muốn thêm một cấp độ TRACE, và một trong những thành phần của nhân sư cũng vậy.
Mad Physicist,

1
Việc thiếu dấu sao ở phía trước argstrong quá trình logForLeveltriển khai có phải là cố ý / bắt buộc không?
Chris L. Barnes

1
@Tunisia. Nó không cố ý. Cảm ơn vì đã bắt được.
Mad Physicist

40

Câu hỏi này khá cũ, nhưng tôi vừa giải quyết cùng một chủ đề và tìm ra một cách tương tự như những câu đã được đề cập và có vẻ gọn gàng hơn một chút đối với tôi. Điều này đã được thử nghiệm trên 3.4, vì vậy tôi không chắc liệu các phương pháp được sử dụng có tồn tại trong các phiên bản cũ hơn hay không:

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
Đây là câu trả lời tốt nhất cho IMHO, vì nó tránh được sự chắp vá của khỉ. Những gì getsetLoggerClasschính xác làm gì và tại sao chúng cần thiết?
Marco Sulla

3
@MarcoSulla Chúng được ghi lại như một phần của mô-đun ghi nhật ký của Python. Tôi giả sử rằng phân lớp con động được sử dụng trong trường hợp ai đó muốn có llogger của riêng họ khi sử dụng thư viện này. MyLogger này sau đó sẽ trở thành một lớp con của lớp tôi, kết hợp cả hai.
CrackerJack9

Điều này rất giống với giải pháp được trình bày trong cuộc thảo luận này về việc có thêm một TRACEcấp vào thư viện ghi nhật ký mặc định hay không. +1
IMP1

18

Ai đã bắt đầu thực hành xấu khi sử dụng các phương pháp nội bộ ( self._log) và tại sao mỗi câu trả lời lại dựa trên điều đó ?! Thay vào đó, giải pháp pythonic sẽ được sử dụng self.logđể bạn không phải gây rối với bất kỳ nội dung bên trong nào:

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
Sử dụng _log () thay vì log () là cần thiết để tránh giới thiệu thêm một cấp trong ngăn xếp cuộc gọi. Nếu log () được sử dụng, việc giới thiệu thêm khung ngăn xếp sẽ khiến một số thuộc tính LogRecord (funcName, lineno, tên tệp, tên đường dẫn, ...) trỏ đến hàm gỡ lỗi thay vì trình gọi thực. Đây có thể không phải là kết quả mong muốn.
rivy

5
Vì khi nào việc gọi các phương thức nội bộ của riêng một lớp không được phép? Chỉ vì hàm được định nghĩa bên ngoài lớp không có nghĩa là nó là một phương thức bên ngoài.
OozeMeister

3
Phương pháp này không chỉ thay đổi dấu vết ngăn xếp một cách không cần thiết, mà còn không kiểm tra xem cấp chính xác đang được ghi.
Mad Physicist

Tôi cảm thấy, những gì @schlamar nói là đúng, nhưng lý do phản đối lại có số phiếu bầu như nhau. Vậy dùng cái gì?
Sumit Murari

1
Tại sao một phương thức không sử dụng một phương thức nội bộ?
Gringo Suave

9

Tôi thấy việc tạo một thuộc tính mới cho đối tượng trình ghi chuyển hàm log () sẽ dễ dàng hơn. Tôi nghĩ rằng mô-đun ghi nhật ký cung cấp addLevelName () và log () vì lý do này. Do đó không cần lớp con hoặc phương thức mới.

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

hiện nay

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

sẽ hoạt động như mong đợi.


Điều này sẽ không có một điểm nhấn hiệu suất nhỏ so với phân loại phụ? Với cách tiếp cận này, mỗi khi một số yêu cầu trình ghi nhật ký, họ sẽ phải thực hiện lệnh gọi setattr. Bạn có thể sẽ kết hợp những thứ này lại với nhau trong một lớp tùy chỉnh nhưng tuy nhiên, setattr đó phải được gọi trên mọi trình ghi nhật ký được tạo, phải không?
Matthew Lund

@Zbigniew bên dưới cho biết điều này không hoạt động, mà tôi nghĩ là do trình ghi nhật ký của bạn cần thực hiện lệnh gọi của nó _log, không phải log.
Marqueed ngày

9

Mặc dù chúng tôi đã có rất nhiều câu trả lời đúng, nhưng theo ý kiến ​​của tôi, điều sau đây là khó hiểu hơn:

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)

Nếu bạn muốn sử dụng mypytrên mã của mình, bạn nên thêm # type: ignoređể ngăn cảnh báo thêm thuộc tính.


1
Nó trông rất tuyệt, nhưng dòng cuối cùng là khó hiểu. Có nên không logging.trace = partial(logging.log, logging.TRACE) # type: ignore?
Sergey Nudnov

@SergeyNudnov cảm ơn bạn đã chỉ ra, tôi đã sửa nó. Đó là lỗi từ phía tôi, tôi chỉ sao chép từ mã của mình và dường như đã làm hỏng việc dọn dẹp.
DerWeh

8

Tôi nghĩ rằng bạn sẽ phải phân Loggerloại lớp và thêm một phương thức được gọi là tracevề cơ bản gọi Logger.logvới cấp thấp hơn DEBUG. Tôi chưa thử điều này nhưng đây là những gì tài liệu chỉ ra .


3
Và có thể bạn sẽ muốn thay thế logging.getLoggerđể trả về lớp con của mình thay vì lớp tích hợp sẵn.
S.Lott

4
@ S.Lott - Trên thực tế (ít nhất là với phiên bản Python hiện tại, có thể nó không phải là trường hợp của năm 2010) bạn phải sử dụng setLoggerClass(MyClass)và sau đó gọi getLogger()như bình thường ...
mac

IMO, cho đến nay đây là câu trả lời hay nhất (và hầu hết các Pythonic) và nếu tôi có thể cho nó nhiều +1, thì tôi đã làm như vậy. Nó đơn giản để thực thi, tuy nhiên mã mẫu sẽ rất hay. :-D
Doug R.

@ DougR.Cảm ơn nhưng như tôi đã nói, tôi chưa thử. :)
Noufal Ibrahim

6

Mẹo để tạo một trình ghi nhật ký tùy chỉnh:

  1. Không sử dụng _log, sử dụng log(bạn không cần phải kiểm traisEnabledFor )
  2. mô-đun ghi nhật ký phải là một phiên bản tạo của trình ghi nhật ký tùy chỉnh vì nó thực hiện một số phép thuật trong getLogger , vì vậy bạn sẽ cần thiết lập lớp quasetLoggerClass
  3. Bạn không cần phải xác định __init__cho trình ghi nhật ký, lớp nếu bạn không lưu trữ bất kỳ thứ gì
# Lower than debug which is 10
TRACE = 5
class MyLogger(logging.Logger):
    def trace(self, msg, *args, **kwargs):
        self.log(TRACE, msg, *args, **kwargs)

Khi gọi trình ghi này, hãy sử dụng trình ghi này setLoggerClass(MyLogger)để làm trình ghi mặc định từgetLogger

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

Bạn sẽ cần phải setFormatter, setHandlersetLevel(TRACE)trên handlervà trên logbản thân để thực sự se dấu vết ở mức độ thấp này


3

Điều này đã làm việc cho tôi:

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.')

Sự cố lambda / funcName đã được khắc phục với logger._log như @marqueed đã chỉ ra. Tôi nghĩ rằng sử dụng lambda trông gọn gàng hơn một chút, nhưng hạn chế là nó không thể lấy các đối số từ khóa. Bản thân tôi chưa bao giờ sử dụng nó nên không có vấn đề gì.

  LƯU Ý thiết lập: trường học cho mùa hè! anh bạn
  Thiết lập FATAL: không tìm thấy tệp.

2

Theo kinh nghiệm của tôi, đây là giải pháp đầy đủ cho vấn đề của op ... để tránh xem "lambda" là hàm mà thông báo được phát ra, hãy đi sâu hơn:

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

Tôi chưa bao giờ thử làm việc với một lớp trình ghi nhật ký độc lập, nhưng tôi nghĩ ý tưởng cơ bản là giống nhau (sử dụng _log).


Tôi không nghĩ rằng điều này hiệu quả. Bạn không cần loggernhư lập luận đầu tiên trong log_at_my_log_level?
Paul

Có, tôi nghĩ rằng bạn có thể sẽ. Câu trả lời này được điều chỉnh từ mã giải quyết một vấn đề hơi khác.
marqueed

2

Thêm vào ví dụ về Mad Physicists để có được tên tệp và số dòng chính xác:

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

1

dựa trên câu trả lời được ghim, tôi đã viết một phương pháp nhỏ để tự động tạo các cấp ghi nhật ký mới

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))

cấu hình có thể smth như vậy:

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

0

Để thay thế cho việc thêm một phương thức bổ sung vào lớp Logger, tôi khuyên bạn nên sử dụng Logger.log(level, msg)phương thức này.

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

Tôi bối rối; với python 3.5, ít nhất, nó chỉ hoạt động:

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")
    

đầu ra:

GỢI Ý: root: y1

TRACE: root: y2


1
Điều này không cho phép bạn làm điều logger.trace('hi')mà tôi tin là mục tiêu chính
Cuối cùng

-3

Trong trường hợp bất kỳ ai muốn một cách tự động để thêm cấp độ ghi nhật ký mới vào mô-đun ghi nhật ký (hoặc bản sao của nó) động, tôi đã tạo hàm này, mở rộng câu trả lời của @ 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
Đánh giá bên trong thực thi. Chà.
Mad Physicist

2
..... không biết những gì khiến tôi làm điều này .... sau nhiều tháng tôi sẽ vui vẻ trao đổi tuyên bố này với một setattrthay ...
Vasilis Lemonidis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.