【问题标题】:How can exceptions in graphene-django be conditionally logged?如何有条件地记录 graphene-django 中的异常?
【发布时间】:2020-01-29 14:31:14
【问题描述】:

每当引发异常时,它们都会记录在控制台中(如果使用了 Sentry,也会记录在其中)。

其中许多例外只是为了向用户显示。例如,django-graphql-jwt raises the PermissionDenied exception 代表 login_required decorator

问题是这会在测试/开发期间污染控制台输出,并在生产期间将有效错误记录到 Sentry。对于上述示例等例外情况,仅用于向用户显示,没有记录。

作为一种解决方法,我尝试编写中间件来捕获任何抛出的异常:

class ExceptionFilterMiddleware:
    IGNORED_EXCEPTIONS = (
        # Local exceptions
        ValidationException,
        # Third-party exceptions
        JSONWebTokenExpired,
        PermissionDenied,
    )

    def on_error(self, error):
        if not isinstance(error, self.IGNORED_EXCEPTIONS):
            return error

    def resolve(self, next, *args, **kwargs):
        return next(*args, **kwargs).catch(self.on_error)

但如果异常被捕获或未返回,则它不再填充查询/变异输出中的errors 字段。因此,所有错误都会被记录,无法有条件地记录异常。

这意味着唯一的解决方案是创建一个日志过滤器,如下所示:

def skip_valid_exceptions(record):
    """
    Skip exceptions for errors only intended to be displayed to the API user.
    """
    skip: bool = False

    if record.exc_info:
        exc_type, exc_value = record.exc_info[:2]
        skip = isinstance(exc_value, valid_exceptions)

    return not skip

但这也不起作用,因为每当 Graphene 抛出错误时,record.exc_info 就是 None,因此无法根据异常类型有条件地过滤掉异常。

有解决办法吗?这似乎是一个常见问题,但我找不到任何解决方案。

或者,我不能使用异常向 API 用户显示错误,但这意味着将错误放入查询结果的 data.errors 字段而不是 errors。这是一个标准,需要适配前端逻辑(比如 Apollo 的错误处理),并不理想。这也意味着不能使用第三方库(如 django-graphql-jwt)中抛出异常的任何功能。

【问题讨论】:

  • 您找到解决方案了吗?我现在自己处理这个问题
  • @JensAstrup 这是很久以前的事了,但是 IIRC 我认为我没有找到任何解决方案。 graphene-django 对此的处理相当差。我在 GitHub 上打开了一个问题,但什么也没发生:github.com/graphql-python/graphene-django/issues/780

标签: django graphene-python graphene-django


【解决方案1】:

试试这个。首先确保预期的异常是GraphQLError 或其后代。

然后像这样创建一个日志过滤器:

import logging
from graphql import GraphQLError

class GraphQLLogFilter(logging.Filter):
    """
    Filter GraphQL errors that are intentional.

    Any exceptions of type GraphQLError that are raised on purpose 
    to generate errors for GraphQL responses will be silenced from logs. 
    Other exceptions will be displayed so they can be tracked down.
    """
    def filter(self, record):
        if record.exc_info:
            etype, _, _ = record.exc_info
            if etype == GraphQLError:
                return None
        if record.stack_info and 'GraphQLError' in record.stack_info:
            return None
        if record.msg and 'GraphQLError' in record.msg:
            return None

        return True

在您的设置中使用:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    # Prevent graphql exception from displaying in console
    'filters': {
        'graphql_log_filter': {
            '()': GraphQLLogFilter,
        }
    },
    'loggers': {
        'graphql.execution.utils': {
            'level': 'WARNING',
            'handlers': ['console'],
            'filters': ['graphql_log_filter'],
        },
    },
}

相关资源:

【讨论】:

    猜你喜欢
    • 2014-05-16
    • 2015-05-12
    • 2019-10-17
    • 1970-01-01
    • 2013-05-22
    • 2017-01-30
    • 2014-02-28
    • 1970-01-01
    • 2013-05-15
    相关资源
    最近更新 更多