Django 中 Python 日志记录的优雅设置

发布于 2024-08-08 14:49:15 字数 384 浏览 11 评论 0原文

我还没有找到一种让我满意的使用 Django 设置 Python 日志记录的方法。我的要求相当简单:

  • 不同的事件有不同的日志处理程序 - 也就是说,我希望能够记录到不同的文件
  • 轻松访问模块中的记录器。该模块应该能够轻松找到其记录器。
  • 应该很容易适用于命令行模块。系统的某些部分是独立的命令行或守护进程。这些模块应该可以轻松使用日志记录。

我当前的设置是使用 logging.conf 文件并在我登录的每个模块中设置日志记录。感觉不对。

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

I have yet to find a way of setting up Python logging with Django that I'm happy with. My requirements are fairly simple:

  • Different log handlers for different events - that is, I want to be able to log to different files
  • Easy access to loggers in my modules. The module should be able to find its logger with little effort.
  • Should be easily applicable to command-line modules. Parts of the system are stand-alone command line or daemon processes. Logging should be easily usable with these modules.

My current setup is to use a logging.conf file and setup logging in each module I log from. It doesn't feel right.

Do you have a logging setup that you like? Please detail it: how do you setup the configuration (do you use logging.conf or set it up in code), where/when do you initiate the loggers, and how do you get access to them in your modules, etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

江南烟雨〆相思醉 2024-08-15 14:49:15

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

从旧到新的转变并不是自动的,所以我想我会把它写在这里。

当然,查看 django 文档 以获得一些信息更多的。

这是基本的conf,默认使用 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,规定以下块:

  • 格式化程序 - 相应的值将是一个字典,其中每个键都是一个格式化程序 ID,每个值都是一个描述如何配置相应格式化程序实例的字典。


  • filters - 相应的值将是一个字典,其中每个键都是一个过滤器 ID,每个值都是一个描述如何配置相应 Filter 实例的字典。

  • handlers - 相应的值将是一个字典,其中每个键都是一个处理程序 ID,每个值都是一个描述如何配置相应处理程序实例的字典。每个处理程序都有以下键:

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

  • 级别(可选)。处理程序的级别。

  • 格式化程序(可选)。此处理程序的格式化程序的 ID。

  • 过滤器(可选)。此处理程序的过滤器 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'
    },
}

编辑

请参阅现在始终记录请求异常Ticket #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,
    }

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

如果你不在 Django 1.5 中这样做,你会得到一个

DeprecationWarning:您没有在“mail_admins”日志处理程序上定义过滤器:添加隐式仅调试错误过滤器

,但在 django 1.4 和 django 1.5 中仍然可以正常工作。

** 结束编辑 **

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

我还经常执行以下操作:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

...
    'level': LOG_LEVEL
...

然后在我的 python 代码中,我总是添加一个 NullHandler,以防没有定义任何日志记录conf。这可以避免未指定处理程序的警告。对于不一定仅在 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.')

I know this is a solved answer already, but as per django >= 1.3 there's a new logging setting.

Moving from old to new is not automatic, so I thought i'll write it down here.

And of course checkout the django doc for some more.

This is the basic conf, created by default with django-admin createproject v1.3 - mileage might change with latest django versions:

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

This structure is based upon the standard Python logging dictConfig, that dictates the following blocks:

  • formatters - the corresponding value will be a dict in which each key is a formatter id and each value is a dict describing how to configure the corresponding Formatter instance.

  • filters - the corresponding value will be a dict in which each key is a filter id and each value is a dict describing how to configure the corresponding Filter instance.

  • handlers - the corresponding value will be a dict in which each key is a handler id and each value is a dict describing how to configure the corresponding Handler instance. Each handler has the following keys:

  • class (mandatory). This is the fully qualified name of the handler class.

  • level (optional). The level of the handler.

  • formatter (optional). The id of the formatter for this handler.

  • filters (optional). A list of ids of the filters for this handler.

I usually do at least this:

  • add a .log file
  • configure my apps to write to this log

Which translates into:

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

edit

See request exceptions are now always logged and Ticket #16288:

I updated the above sample conf to explicitly include the correct filter for mail_admins so that, by default, emails are not sent when debug is True.

You should add a filter:

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

and apply it to the mail_admins handler:

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

Otherwise the django.core.handers.base.handle_uncaught_exception doesn't pass errors to the 'django.request' logger if settings.DEBUG is True.

If you don't do this in Django 1.5 you'll get a

DeprecationWarning: You have no filters defined on the 'mail_admins' logging handler: adding implicit debug-false-only filter

but things will still work correctly BOTH in django 1.4 and django 1.5.

** end edit **

That conf is strongly inspired by the sample conf in the django doc, but adding the log file part.

I often also do the following:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

...
    'level': LOG_LEVEL
...

Then in my python code I always add a NullHandler in case no logging conf is defined whatsoever. This avoid warnings for no Handler specified. Especially useful for libs that are not necessarily called only in 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.')
三生殊途 2024-08-15 14:49:15

到目前为止,我发现的最好方法是在 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

更新:请注意,从版本 1.3 开始,Django 现在包含 支持用于记录

The best way I've found so far is to initialize logging setup in settings.py - nowhere else. You can either use a configuration file or do it programmatically step-by-step - it just depends on your requirements. The key thing is that I usually add the handlers I want to the root logger, using levels and sometimes logging.Filters to get the events I want to the appropriate files, console, syslogs etc. You can of course add handlers to any other loggers too, but there isn't commonly a need for this in my experience.

In each module, I define a logger using

logger = logging.getLogger(__name__)

and use that for logging events in the module (and, if I want to differentiate further) use a logger which is a child of the logger created above.

If my app is going to be potentially used in a site which doesn't configure logging in settings.py, I define a NullHandler somewhere as follows:

#someutils.py

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

null_handler = NullHandler()

and ensure that an instance of it is added to all loggers created in the modules in my apps which use logging. (Note: NullHandler is already in the logging package for Python 3.1, and will be in Python 2.7.) So:

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

This is done to ensure that your modules play nicely in a site which doesn't configure logging in settings.py, and that you don't get any annoying "No handlers could be found for logger X.Y.Z" messages (which are warnings about potentially misconfigured logging).

Doing it this way meets your stated requirements:

  • You can set up different log handlers for different events, as you currently do.
  • Easy access to loggers in your modules - use getLogger(__name__).
  • Easily applicable to command-line modules - they also import settings.py.

Update: Note that as of version 1.3, Django now incorporates support for logging.

冷清清 2024-08-15 14:49:15

我们使用 logging.ini 文件初始化顶级 urls.py 中的日志记录。

logging.ini 的位置在 settings.py 中提供,但仅此而已。

每个模块然后做

logger = logging.getLogger(__name__)

为了区分测试、开发和生产实例,我们有不同的logging.ini 文件。在大多数情况下,我们有一个“控制台日志”,仅将错误发送到 stderr。我们有一个“应用程序日志”,它使用转到日志目录的常规滚动日志文件。

We initialize logging in the top-level urls.py by using a logging.ini file.

The location of the logging.ini is provided in settings.py, but that's all.

Each module then does

logger = logging.getLogger(__name__)

To distinguish testing, development and production instances, we have different logging.ini files. For the most part, we have a "console log" that goes to stderr with Errors only. We have an "application log" that uses a regular rolling log file that goes to a logs directory.

你没皮卡萌 2024-08-15 14:49:15

我目前正在使用我自己创建的日志系统。它使用 CSV 格式进行日志记录。

django-csvlog

这个项目仍然没有完整的文档,但我正在努力。

I am currently using a logging system, which I created myself. It uses CSV format for logging.

django-csvlog

This project still doesn't have full documentation, but I am working on it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文