使用示例代码在celery 4.1.1中跟踪任务。每个工人都在奔跑:
import logging
from jaeger_client import Config
import opentracing
def get_tracer(service="Vienna"):
config = Config(
config={
'sampler': {
'type': 'const',
'param': 1,
},
'logging': True,
},
service_name=service,
)
return config.initialize_tracer() or opentracing.global_tracer()
当我第一次启动celery并运行任务时,每个工作人员都会得到一个工作的示踪剂,并且每个输出都有日志输出:
[2019-07-04 19:17:00,527: INFO/ForkPoolWorker-2] Initializing Jaeger Tracer with UDP reporter
[2019-07-04 19:17:00,546: INFO/ForkPoolWorker-2] opentracing.tracer initialized to <jaeger_client.tracer.Tracer object at 0x7f804d079c10>[app_name=SocketIOTask]
在初始值之后运行的任何任务都会获取全局跟踪器
来自Config.initialze_tracer
(返回None
)和日志警告Jaeger tracer already initialized, skipping
。
在控制台上观看tcpdump时,表明没有发送UDP数据包,我想我正在得到一个未初始化的默认跟踪程序,它正在使用noop记录器。
我已经仔细研究过opentracing和jaeger_client中的代码,但找不到解决这一问题的规范方法。
答案 0 :(得分:1)
Celery 默认分叉多个进程 (--pool=prefork
)。问题是 jaeger_client 在单独的线程中执行实际的消息发送。该线程在分叉之前创建,在子进程中不可用。因此,从子流程中记录日志不起作用。
最明显的解决方案是使用--pool=solo
、-pool=threads
等。但是在CPU密集型任务的情况下,我们仍然需要使用--pool=prefork
。在这种情况下,我们会遇到另一个问题。
一方面,jaeger_client 旨在用作单例。它打开文件描述符但从不关闭它们(即使 close()
比关闭更刷新)。另一方面,我们需要为每个 celery 进程创建一个单独的跟踪器。
为了解决这个问题,我使用了以下解决方法:
tracer = None
tracer_owner_pid = 0
def get_jaeger_tracer():
global tracer_owner_pid
global tracer
pid = os.getpid()
if tracer_owner_pid == pid:
return tracer
logging.getLogger('').handlers = []
logging.basicConfig(format='%(message)s', level=logging.DEBUG)
config = Config(
config={
'sampler': {
'type': 'const',
'param': 1,
},
'logging': True,
},
service_name='foo',
)
tracer = config.new_tracer()
tracer_owner_pid = pid
return tracer
答案 1 :(得分:0)
from jaeger_client import Config
def get_tracer(service="Vienna"):
config = Config(
config={
'sampler': {
'type': 'const',
'param': 1,
},
'logging': True,
},
service_name=service,
)
tracer = config.initialize_tracer()
if tracer is None:
Config._initialized = False
tracer = config.initialize_tracer()
return tracer