一尘不染

在Django中优雅地设置Python日志记录

django

我还没有找到对自己满意的Django设置Python日志记录的方法。我的要求很简单:

  • 不同事件的不同日志处理程序-也就是说,我希望能够登录到不同的文件
  • 轻松访问我模块中的记录器。该模块应该可以轻松找到其记录器。
  • 应该容易适用于命令行模块。系统的一部分是独立的命令行或守护进程。这些模块应易于使用日志记录。
    我当前的设置是使用logging.conf文件并在我登录的每个模块中记录设置。感觉不对。

你是否有喜欢的日志记录设置?请详细说明:如何设置配置(logging.conf在代码中使用或设置),在何处/何时启动记录器,以及如何在模块中访问它们等等。


阅读 611

收藏
2020-03-28

共2个答案

一尘不染

到目前为止,我发现的最好方法是在settings.py中初始化日志记录设置-其他地方。你可以使用配置文件,也可以按程序逐步进行配置-这仅取决于你的要求。关键是,我通常使用级别和有时记录日志的方式将我想要的处理程序添加到根记录器中。过滤器将我想要的事件获取到适当的文件,控制台,系统日志等。你当然可以将处理程序添加到任何其他记录器中同样,但是根据我的经验,通常不需要这样做。

在每个模块中,我使用

logger = logging.getLogger(__name__)

并将其用于记录模块中的事件(并且,如果我想进一步区分),请使用记录器,该记录器是上面创建的记录器的子级。

如果我的应用程序可能会在未配置settings.py中配置日志记录的站点中使用,请在以下位置定义NullHandler:

#someutils.py

class NullHandler(logging.Handler):
    def emit(self, record):
        pass

null_handler = NullHandler()

并确保将其实例添加到我的应用程序中使用日志记录的模块中创建的所有记录器中。(注意:NullHandler已经在Python 3.1的日志记录包中,并且将在Python 2.7中使用。)

logger = logging.getLogger(__name__)
logger.addHandler(someutils.null_handler)

这样做是为了确保你的模块在未配置settings.py日志记录的站点中正常运行,并且不会收到任何令人讨厌的“找不到XYZ记录程序的处理程序”消息(这些警告可能会配置错误的日志记录)。

这样做可以满足你规定的要求:

  • 你可以像当前一样为不同的事件设置不同的日志处理程序。
  • 轻松访问模块中的记录器-使用getLogger(__name__)
  • 容易适用于命令行模块-它们也可以导入settings.py
2020-03-28
一尘不染

我知道这已经是一个解决的答案,但是按照django> = 1.3,有一个新的日志记录设置。

从旧到新并不是自动的,所以我想在这里写下来。

当然,请查看django文档以了解更多信息。

这是基本的配置文件,默认情况下是使用django-admin createproject v1.3创建的-里程可能会随着最新的django版本而变化:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        }
    }
}

该结构基于标准的Python日志dictConfig,该命令规定了以下块:

  • formatters -相应的值将是一个dict,其中每个键是一个格式化程序ID,每个值是一个描述如何配置相应的Formatter实例的dict。
  • filters-相应的值将是一个dict,其中每个键是一个过滤器ID,每个值是一个描述如何配置相应的Filter实例的dict。
  • handlers-相应的值将是一个dict,其中每个键是一个处理程序ID,每个值是一个描述如何配置相应的Handler实例的dict。每个处理程序具有以下键:

  • class(必填)。这是处理程序类的完全限定名称。

  • level(可选的)。处理程序的级别。
  • formatter(可选的)。此处理程序的格式化程序的ID。
  • filters(可选的)。此处理程序的过滤器ID的列表。

我通常至少这样做:

  • 添加一个.log文件
  • 配置我的应用程序以写入此日志

转换为:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'null': {
            'level':'DEBUG',
            'class':'django.utils.log.NullHandler',
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        # I always add this handler to facilitate separating loggings
        'log_file':{
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(VAR_ROOT, 'logs/django.log'),
            'maxBytes': '16777216', # 16megabytes
            'formatter': 'verbose'
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'apps': { # I keep all my of apps under 'apps' folder, but you can also add them one by one, and this depends on how your virtualenv/paths are set
            'handlers': ['log_file'],
            'level': 'INFO',
            'propagate': True,
        },
    },
    # you can also shortcut 'loggers' and just configure logging for EVERYTHING at once
    'root': {
        'handlers': ['console', 'mail_admins'],
        'level': 'INFO'
    },
}

编辑

请参阅现在始终记录请求异常和故障单#16288:

我更新了上面的示例conf,以明确包含针对mail_admins的正确过滤器,以便默认情况下,当debug为True时,不发送电子邮件。

你应该添加一个过滤器:

'filters': {
    'require_debug_false': {
        '()': 'django.utils.log.RequireDebugFalse'
    }
},

并将其应用于mail_admins处理程序:

    'mail_admins': {
        'level': 'ERROR',
        'filters': ['require_debug_false'],
        'class': 'django.utils.log.AdminEmailHandler',
        'include_html': True,
    }

否则,django.core.handers.base.handle_uncaught_exception如果settings.DEBUG为True,则不会将错误传递给django.request记录器。

如果你在Django 1.5中不执行此操作,则会得到一个

DeprecationWarning:你没有在'mail_admins'日志记录处理程序上定义过滤器:添加隐式的debug-false-only过滤器
但是在django 1.4和django 1.5中,一切仍然可以正常工作。

结束编辑

该conf受到django doc中示例conf的强烈启发,但添加了日志文件部分。

我经常还会执行以下操作:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

...
    'level': LOG_LEVEL
...

然后在我的python代码中,我总是添加一个NullHandler,以防万一没有定义任何日志配置。这样可以避免未指定任何处理程序的警告。对于不一定只在Django(ref)中调用的库特别有用

import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
class NullHandler(logging.Handler): #exists in python 3.1
    def emit(self, record):
        pass
nullhandler = logger.addHandler(NullHandler())

# here you can also add some local logger should you want: to stdout with streamhandler, or to a local file...

[…]

logger.warning('etc.etc.')

希望这可以帮助!

2020-03-28