9

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

其中许多例外仅旨在向用户显示。例如,django-graphql-jwt引发PermissionDeniedlogin_required装饰异常

问题是这会在测试/开发期间污染控制台输出,并在生产期间将有效错误记录到 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

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

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

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

4

1 回答 1

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'],
        },
    },
}

相关资源:

于 2020-11-16T18:49:11.230 回答