自定义 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)
相关推荐
q567315237 分钟前
如何在 Canopy 中导入 scikit-learn
开发语言·python
菜狗woc11 分钟前
作业二.自定义数据集使用scikit-learn中的包实现线性回归方法对其进行拟合
开发语言·python·机器学习
汤姆和佩琦14 分钟前
2025-2-3-sklearn学习(50) & (51) 完结篇 零落成泥碾作尘,只有香如故。
人工智能·python·学习·机器学习·sklearn
江无行者38 分钟前
python 如何生成requirements.txt
python
测试大圣41 分钟前
2025最新软件测试面试大全
自动化测试·软件测试·python·测试工具·面试·职场和发展·测试用例
Damon小智1 小时前
使用Pygame制作“圣诞树”
python·pygame
曾彪彪1 小时前
Python pika消费Rabbit MQ数据,慢消费引起的connection reset问题
python·消息中间件·rabbit mq
htuhxf3 小时前
TfidfVectorizer
python·自然语言处理·nlp·tf-idf·文本特征
xiaocang6688883 小时前
如何使用Python调用大语言模型的API接口?
开发语言·python·语言模型
几道之旅4 小时前
windows下玩转vllm:vllm简介
人工智能·python