自定义 Celery的logging模块

为什么需要自定义 Celery 日志

默认的 Celery 日志配置虽然满足基本需求,但在以下情况下可能需要进行自定义:

  • 支持日志滚动:原生celery不支持日志滚动。
  • 更详细的日志信息:需要包含更多上下文信息,以便更好地理解任务的执行过程。
  • 日志格式调整:适应现有的日志管理系统,如 ELK(Elasticsearch, Logstash, Kibana)堆栈。
  • 多日志目标:将日志同时输出到多个地方,例如控制台、文件和远程日志服务器。
  • 日志级别控制:根据不同的模块或任务设定不同的日志级别。

Celery 日志为什么复杂

Celery的日志记录看起来不符合Python的风格,并且显得复杂。这不是Celery的问题,而是因为Python的日志系统默认不支持Celery所需的各种并发环境,如eventlets、greenlets、prefork和线程等。因此,Celery需要自定义日志记录。

Celery提供了一个特殊的函数get_task_logger,它在celery.utils.log中用于获取日志记录器。这个记录器会自动包含任务名称和唯一ID。Celery的文档建议使用get_task_logger。

在worker中应用日志,我更喜欢使用标准的logging.getLogger方法,因为这样可以保持代码库的清晰,并与Celery应用的上下文分离。

策略1:增强Celery日志记录器

Celery提供了一个after_setup_logger信号,该信号在Celery设置日志记录器后触发

(http://docs.celeryproject.org/en/latest/userguide/signals.html#after-setup-logger)。该信号传递了日志记录器对象(以及其他一些参数),你可以方便地向其添加自定义日志处理程序。

Celery 使用 Python 的标准 logging 模块进行日志记录。首先,需要获取 Celery 默认的日志记录器。

python 复制代码
import os
import logging
from celery import Celery
from celery.signals import after_setup_logger

@after_setup_logger.connect
def setup_loggers(logger, *args, **kwargs):
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    # FileHandler
    fh = logging.FileHandler('logs.log')
    fh.setFormatter(formatter)
    logger.addHandler(fh)

    # SysLogHandler
    slh = logging.handlers.SysLogHandler(address=('logsN.papertrailapp.com', '...'))
    slh.setFormatter(formatter)
    logger.addHandler(slh)

策略2:重写Celery根记录器

您可以通过连接setup_logging信号来阻止Celery配置任何记录器。这使您可以完全用自己的配置重写日志配置。

python 复制代码
import logging
import logging.config
from celery.signals import setup_logging

@setup_logging.connect
def setup_logging(**kwargs):
    logfile = kwargs.get("logfile")
    loglevel = kwargs.get("loglevel")
    loglevel_str = logging.getLevelName(loglevel)

    config = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'default': {
                'format': ' %(asctime)s - %(name)s - %(levelname)s - %(message)s',
                'datefmt': "%Y/%m/%d %H:%M:%S"
            }
        },
        'handlers': {
            'rotatingHandler': {
                'class': 'logging.handlers.RotatingFileHandler',
                'filename': logfile,
                'maxBytes': 2*1024*1024,
                'backupCount': 3,
                'formatter': 'default',
                'encoding': 'utf-8'
            },
            'timedHandler': {
                'class': 'logging.handlers.TimedRotatingFileHandler',
                'filename': logfile,
                'when': 'midnight',
                'interval': 1,
                'backupCount': 7,
                'formatter': 'default',
                'encoding': 'utf-8'
            },
        },
        'loggers': {
            'celery': {
                'handlers': ['timedHandler'],
                'level': loglevel_str,
                'propagate': False
            },
        },
        'root': {
            'handlers': ['rotatingHandler'],
            'level': loglevel_str,
            'propagate': False
        },
    }

    logging.config.dictConfig(config)

策略3:禁用Celery logger配置

另一种解决方案是让Celery设置其日志记录器但不使用它,并防止其劫持根日志记录器。

默认情况下,Celery会移除根日志记录器上任何先前配置的处理程序。如果你想自定义自己的日志处理程序而不被Celery干扰,可以通过设置worker_hijack_root_logger=False来禁用此行为。

这允许你重新控制根日志记录器并回退到标准的Python日志设置。

使用这种方法时要小心,因为你需要完全负责确保你的Python日志记录与Celery设置(eventlets、greenlets、prefork(子进程)、线程等)兼容。

python 复制代码
import os
import logging
from celery import Celery
from celery.signals import setup_logging

app = Celery('app')
app.conf.update({
    'worker_hijack_root_logger': False})
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger()

# StreamHandler
sh = logging.StreamHandler()
sh.setFormatter(formatter)
logger.addHandler(sh)

# FileHandler
fh = logging.FileHandler('logs.log')
fh.setFormatter(formatter)
logger.addHandler(fh)

# SysLogHandler
slh = logging.handlers.SysLogHandler(address=('logsN.papertrailapp.com', '...'))
slh.setFormatter(formatter)
logger.addHandler(slh)
相关推荐
m0_7335654618 小时前
mysql数据库执行全量备份影响业务_利用xtrabackup实现无锁备份
jvm·数据库·python
2401_8800714018 小时前
golang如何编写DNS查询工具_golang DNS查询工具编写大全
jvm·数据库·python
Vertira18 小时前
python 配置PostgreSQL 数据库
开发语言·python
m0_5913647318 小时前
JavaScript中Object-hasOwn作为现代安全检测方案
jvm·数据库·python
m0_6245785918 小时前
html标签怎么避免标签嵌套错误_div不能放在p内原因【详解】
jvm·数据库·python
2301_7693406718 小时前
怎样导出用于负载测试的样本数据_LIMIT限制数据量提取
jvm·数据库·python
2401_8504916519 小时前
c++如何通过文件映射mmap在多进程间实现高性能数据共享【进阶】
jvm·数据库·python
iuvtsrt19 小时前
PHP 中高效查找 CSV 行并获取前后指定偏移行的数据
jvm·数据库·python
m0_4636722019 小时前
MySQL从库出现大量锁等待怎么办_分析从库执行计划与锁日志
jvm·数据库·python
2301_8092047019 小时前
为 Go 语言 WaitGroup.Wait() 添加超时机制的实用方案
jvm·数据库·python