【问题标题】:python: logging: can we added multiple filters to the logger and which one is consideredpython: logging: 我们可以向记录器添加多个过滤器吗?考虑哪个过滤器
【发布时间】:2020-01-07 17:54:48
【问题描述】:

我试图了解 Python 日志记录中的多个过滤器(一个在配置中定义,另一个在代码中定义)如何工作。

我正在开发一个 Django 项目,下面是我在 settings.py 中的记录器配置

我的目标是随时switch onswitch off 记录器。所以使用过滤器我试图通过返回 False (0) 来关闭记录器

1) 在开始时关闭记录器

class StartFilter(object):
    def filter(self, record):
        """
        Determine if the specified record is to be logged.

        Is the specified record to be logged? Returns 0 for no, nonzero for
        yes. If deemed appropriate, the record may be modified in-place.
        """
        return 0

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(funcName)s() %(pathname)s[:%(lineno)s] %(name)s \n%(message)s'
        }
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'formatter': 'verbose',
            'class': 'logging.StreamHandler',
        },
    },
    'filters': {
        'myfilter': {
            '()': StartFilter,
        }
    },
    'loggers': {
        'log_testing': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
            'filters': ['myfilter']
        },
    }
}

我已将过滤器添加到记录器。 'filters': ['myfilter']

2) 在 vi​​ews.py 文件中打开和关闭记录器,我希望在该文件中看到记录

# to switch on logger
class LoggerGateStart(object):
    def filter(self, record):
        """
        Determine if the specified record is to be logged.

        Is the specified record to be logged? Returns 0 for no, nonzero for
        yes. If deemed appropriate, the record may be modified in-place.
        """
        return 1

# to switch off logger
class LoggerGateStop(object):
    def filter(self, record):
        """
        Determine if the specified record is to be logged.

        Is the specified record to be logged? Returns 0 for no, nonzero for
        yes. If deemed appropriate, the record may be modified in-place.
        """
        return 0

import logging
logger = logging.getLogger("log_testing")


...
logging.debug("Some text Before)  # i dont want this to be logged
...



gatestart = LoggerGateStart()
logger_database.addFilter(gatestart)

...
logging.debug("Some text)  # i want this to be logged
...

gatestop = LoggerGateStop()
logger_database.addFilter(gatestop)

...
logging.debug("Some text after")  # i dont want this to be logged even 
if it exist
...

我发现它不是这样工作的。它只考虑 StartFilter 而不考虑 LoggerGateStart 或 LoggerGateStop 并且不将任何日志打印到控制台

我该怎么做

我根据 Gabriel C 的回答使用的回答

我的目标是使用 django django.db.backends 记录 sql。但它的问题是它会记录所有的sql。我只想在代码的特定部分或任何我想查看 sql 的地方记录 sql。所以下面的方法我可以做到。

在 settings.py 中记录配置:

# Filter class to stop or start logging for "django.db.backends"
class LoggerGate:
    def __init__(self, state='closed'):
        # We found that the settings.py runs twice and the filters are created twice. So we have to keep only one. So we delete all the previous filters before we create the new one
        import logging
        logger_database = logging.getLogger("django.db.backends")
        try:
            for filter in logger_database.filters:
                logger_database.removeFilter(filter)
        except Exception as e:
            pass
        self.state = state

    def open(self):
        self.state = 'open'

    def close(self):
        self.state = 'closed'

    def filter(self, record):
        """
        Determine if the specified record is to be logged.

        Is the specified record to be logged? Returns 0/False for no, nonzero/True for
        yes. If deemed appropriate, the record may be modified in-place.
        """
        return self.state == 'open'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'sql': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
        }
    },
    'filters': {
        'myfilter': {
            '()': LoggerGate,
        }
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['sql'],
            'level': 'DEBUG',
            'propagate': False,
            'filters': ['myfilter']
        }
    }
}

然后在views.py中

import logging
logger = logging.getLogger(__name__)
logger_database = logging.getLogger("django.db.backends")

def test1(request):


    logger_database.filters[0].open()
    #Will allow priting of sql satatements from here

    from django import db
    user_set = User.objects.all()

    for user in user_set: # Here sql is executed and is printed to console
        pass
    #Will stop priting of sql satatements after this
    logger_database.filters[0].close()

    from django import db
    user_set = User.objects.all()

    for user in user_set:  # Here sql is executed and is not printed to console
        pass

    now = datetime.datetime.now()
    html = "<html><body>Internal purpose</body></html>"
    return HttpResponse(html)

如果想以格式化和彩色的方式打印 sql,请在 settings.py 中使用它

# SQL formatter to be used for the handler used in logging "django.db.backends"
class SQLFormatter(logging.Formatter):
    def format(self, record):

        # Check if Pygments is available for coloring 
        try:
            import pygments
            from pygments.lexers import SqlLexer
            from pygments.formatters import TerminalTrueColorFormatter
        except ImportError:
            pygments = None

        # Check if sqlparse is available for indentation
        try:
            import sqlparse
        except ImportError:
            sqlparse = None

        # Remove leading and trailing whitespaces
        sql = record.sql.strip()

        if sqlparse:
            # Indent the SQL query
            sql = sqlparse.format(sql, reindent=True)

        if pygments:
            # Highlight the SQL query
            sql = pygments.highlight(
                sql,
                SqlLexer(),
                #TerminalTrueColorFormatter(style='monokai')
                TerminalTrueColorFormatter()
            )

        # Set the record's statement to the formatted query
        record.statement = sql
        return super(SQLFormatter, self).format(record)




# Filter class to stop or start logging for "django.db.backends"
class LoggerGate:
    def __init__(self, state='closed'):
        # We found that the settings.py runs twice and the filters are created twice. So we have to keep only one. So we delete all the previous filters before we create the new one
        import logging
        logger_database = logging.getLogger("django.db.backends")
        try:
            for filter in logger_database.filters:
                logger_database.removeFilter(filter)
        except Exception as e:
            pass
        self.state = state

    def open(self):
        self.state = 'open'

    def close(self):
        self.state = 'closed'

    def filter(self, record):
        """
        Determine if the specified record is to be logged.

        Is the specified record to be logged? Returns 0/False for no, nonzero/True for
        yes. If deemed appropriate, the record may be modified in-place.
        """
        return self.state == 'open'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'sql': {
            '()': SQLFormatter,
            'format': '[%(duration).3f] %(statement)s',
        }
    },
    'handlers': {
        'sql': {
            'class': 'logging.StreamHandler',
            'formatter': 'sql',
            'level': 'DEBUG',
        }
    },
    'filters': {
        'myfilter': {
            '()': LoggerGate,
        }
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['sql'],
            'level': 'DEBUG',
            'propagate': False,
            'filters': ['myfilter']
        }
    }
}

【问题讨论】:

    标签: python django logging


    【解决方案1】:

    只创建一个过滤器并使用它的实例来控制是否应该接受日志。

    from logging import getLogger
    
    class LoggerGate(object):
    
        def __init__(self):
            self.started = False
    
        def start(self):
            self.started = True
    
        def stop(self):
            self.started = False
    
        def filter(self, record):
            """
            Determine if the specified record is to be logged.
    
            Returns True is this LoggerGate is started, False otherwise.
            """
            return self.started
    
    
    logger_database = getLogger("log_testing")
    logger_gate = LoggerGate()
    logger_database.addFilter(logger_gate)
    
    logger_database.critical('this is not logged')
    logger_gate.start()
    logger_database.critical('this is logged')
    logger_gate.stop()
    logger_database.critical('this is not logged')
    

    【讨论】:

    • 因为有时在到达 LoggerGate 的这一点之前可能还有更多的日志条目。并假设在此文件中的代码之前它还将执行一些其他代码,其中也可能有一些日志条目。一般 settings.py 在开始时执行。有什么办法可以在后面的地方获取settings.py中使用的实例
    • 您可以将 LoggerGate 的实例保存在不同的模块中并导入它,例如: from utils import logger_gate 。或者您可以将 LoggerGate 中的所有内容都设置为类方法并传递 LoggerGate 而不是实例。
    • 我不建议这样做,但您也可以循环记录器过滤器并在那里找到实例:for filter in logger_database.filters: if isinstance(filter, LoggerGate): filter.start()跨度>
    • 我以不同的方式得到了同样的答案。 stackoverflow.com/a/57497507/2897115。但是后来我想知道当我混合多个过滤器时会发生什么,因为我无法找到实例。但是for filter in logger_database.filters: if isinstance(filter, LoggerGate): filter.start() 看起来让事情变得更加清晰。以及 Sraw 对当某人为 False 时为什么其他过滤器不起作用的解释 = filter works in seq, record is passed to each filter one by one. And will stop at the one returns a zero. So as StartFilter returns 0, it will directly drop all records.
    • 我想把所有东西都放在settings.py中。我将尝试使用过滤器实例。谢谢
    【解决方案2】:

    Gabriel C 给出了一个很好的解决方案。更详细地说,filter 是按顺序工作的,这意味着记录会被一个一个地传递给每个过滤器。并会停在一个返回一个零。所以当你的StartFilter返回0时,它会直接删除所有记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多