Как вызвать logging.setLogRecordFactory в Python 2? - PullRequest
1 голос
/ 06 мая 2019

Учитывая следующий простой пример:

import logging

class SmartLogRecord(logging.LogRecord):
    """ Dummy LogRecord example """

    def getMessage(self):
        return self.msg % self.args

logging.setLogRecordFactory(SmartLogRecord)

var = 'SmartLogRecord'
logging.warning('I am a %s', var)

Я могу запустить его на Python 3 и использовать свой собственный класс LogRecord, но Python 2 выдает ошибку:

linux@linux-PC$ python3 text.py
WARNING:root:I am a SmartLogRecord

linux@linux-PC$ python2 text.py
Traceback (most recent call last):
  File "text.py", line 9, in <module>
    logging.setLogRecordFactory(SmartLogRecord)
AttributeError: 'module' object has no attribute 'setLogRecordFactory'

linux@linux-PC$ python3 --version
Python 3.7.2

linux@linux-PC$ python2 --version
Python 2.7.16

1 Ответ

1 голос
/ 06 мая 2019

Вот setLogRecordFactory() в CPython 3.7 во всей его красе:

# https://github.com/python/cpython/blob/29500737d45cbca9604d9ce845fb2acc3f531401/Lib/logging/__init__.py#L386
_logRecordFactory = LogRecord

def setLogRecordFactory(factory):
    global _logRecordFactory
    _logRecordFactory = factory

def getLogRecordFactory():
    return _logRecordFactory

def makeLogRecord(dict):
    rv = _logRecordFactory(None, None, "", 0, "", (), None, None)
    rv.__dict__.update(dict)
    return rv

Где это вызывается в Called в Logger.makeRecord():

def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
               func=None, extra=None, sinfo=None):
    """
    A factory method which can be overridden in subclasses to create
    specialized LogRecords.
    """
    rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func,
                           sinfo)

Наоборот, в Python 2 это не вещь:

# https://github.com/python/cpython/blob/7c2c01f02a1821298a62dd16ecc3a12da663e14b/Lib/logging/__init__.py#L1261
def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
    """
    A factory method which can be overridden in subclasses to create
    specialized LogRecords.
    """
    rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)

Что вы можете сделать, это в основном заменить .makeRecord() связанный метод на Logger объекте. Другими словами:

>>> class A:
...     def f(self, a, b):
...         return a + b

>>> def new_f(self, a, b):
...     return a * b
... 
>>> A.f = new_f
>>> A().f(10, 20)
200

Это будет выглядеть так:

class MyLogRecord(logging.LogRecord):
    pass
    # override stuff here

def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        print "Using a MyLogRecord instance"
        rv = MyLogRecord(name, level, fn, lno, msg, args, exc_info, func)
        if extra is not None:
            for key in extra:
                if (key in ["message", "asctime"]) or (key in rv.__dict__):
                    raise KeyError("Attempt to overwrite %r in LogRecord" % key)
                rv.__dict__[key] = extra[key]
        return rv

logging.Logger.makeRecord = makeRecord

Иллюстрация:

>>> logging.error("hello")
Using a MyLogRecord instance
ERROR:root:hello

В этом случае вы просто подгоняете вещи под конкретную необходимость использования одного другого MyLogRecord класса. Если вы действительно хотите, вы можете написать свой setLogRecordFactory(), как показано выше, а затем использовать _logRecordFactory в своем методе замены .makeRecord(), как это делает Python 3.


Предположение / альтернативы (подкласс, не заменяйте)

Еще один комментарий: все это предполагает, что вы хотите повлиять на logger экземпляров, которые не являются "вашими", которые были созданы в другом месте. Если вы только хотите повлиять на регистраторы, которые определяет ваш собственный код, вы можете просто подкласс Logger вместо полной замены класса Logger, который принадлежит пространству имен модуля logging.__init__ .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...