|
@@ -17,6 +17,7 @@ import threading
|
|
|
from collections import deque
|
|
|
from contextlib import contextmanager
|
|
|
from copy import copy
|
|
|
+from operator import itemgetter
|
|
|
|
|
|
from kombu import Exchange, Queue, Producer
|
|
|
from kombu.mixins import ConsumerMixin
|
|
@@ -24,9 +25,12 @@ from kombu.utils import cached_property
|
|
|
|
|
|
from celery.app import app_or_default
|
|
|
from celery.utils import uuid
|
|
|
+from celery.utils.timeutils import adjust_timestamp, utcoffset
|
|
|
|
|
|
event_exchange = Exchange('celeryev', type='topic')
|
|
|
|
|
|
+_TZGETTER = itemgetter('utcoffset', 'timestamp')
|
|
|
+
|
|
|
|
|
|
def get_exchange(conn):
|
|
|
ex = copy(event_exchange)
|
|
@@ -48,8 +52,8 @@ def Event(type, _fields=None, **fields):
|
|
|
return event
|
|
|
|
|
|
|
|
|
-def domain(type):
|
|
|
- return type.split('-', 1)
|
|
|
+def group_from(type):
|
|
|
+ return type.split('-', 1)[0]
|
|
|
|
|
|
|
|
|
class EventDispatcher(object):
|
|
@@ -60,9 +64,10 @@ class EventDispatcher(object):
|
|
|
:keyword hostname: Hostname to identify ourselves as,
|
|
|
by default uses the hostname returned by :func:`socket.gethostname`.
|
|
|
|
|
|
- :keyword domains: List of domains to send events for. :meth:`send` will
|
|
|
- ignore send requests to domains not in this list.
|
|
|
- If this is :const:`None`, all events will be sent.
|
|
|
+ :keyword groups: List of groups to send events for. :meth:`send` will
|
|
|
+ ignore send requests to groups not in this list.
|
|
|
+ If this is :const:`None`, all events will be sent. Example groups
|
|
|
+ include ``"task"`` and ``"worker"``.
|
|
|
|
|
|
:keyword enabled: Set to :const:`False` to not actually publish any events,
|
|
|
making :meth:`send` a noop operation.
|
|
@@ -81,7 +86,7 @@ class EventDispatcher(object):
|
|
|
|
|
|
def __init__(self, connection=None, hostname=None, enabled=True,
|
|
|
channel=None, buffer_while_offline=True, app=None,
|
|
|
- serializer=None, domains=None):
|
|
|
+ serializer=None, groups=None):
|
|
|
self.app = app_or_default(app or self.app)
|
|
|
self.connection = connection
|
|
|
self.channel = channel
|
|
@@ -93,8 +98,7 @@ class EventDispatcher(object):
|
|
|
self.serializer = serializer or self.app.conf.CELERY_EVENT_SERIALIZER
|
|
|
self.on_enabled = set()
|
|
|
self.on_disabled = set()
|
|
|
- self.tzoffset = [-time.timezone, -time.altzone]
|
|
|
- self.domains = set(domains or [])
|
|
|
+ self.groups = set(groups or [])
|
|
|
if not connection and channel:
|
|
|
self.connection = channel.connection.client
|
|
|
self.enabled = enabled
|
|
@@ -102,6 +106,7 @@ class EventDispatcher(object):
|
|
|
self.enabled = False
|
|
|
if self.enabled:
|
|
|
self.enable()
|
|
|
+ self.headers = {'hostname': self.hostname}
|
|
|
|
|
|
def __enter__(self):
|
|
|
return self
|
|
@@ -138,17 +143,18 @@ class EventDispatcher(object):
|
|
|
|
|
|
"""
|
|
|
if self.enabled:
|
|
|
- domains = self.domains
|
|
|
- if domains and domain(type) not in domains:
|
|
|
+ groups = self.groups
|
|
|
+ if groups and group_from(type) not in groups:
|
|
|
return
|
|
|
|
|
|
with self.mutex:
|
|
|
event = Event(type, hostname=self.hostname,
|
|
|
clock=self.app.clock.forward(),
|
|
|
- tzoffset=self.tzoffset, **fields)
|
|
|
+ utcoffset=utcoffset(), **fields)
|
|
|
try:
|
|
|
self.publisher.publish(event,
|
|
|
- routing_key=type.replace('-', '.'))
|
|
|
+ routing_key=type.replace('-', '.'),
|
|
|
+ headers=self.headers)
|
|
|
except Exception as exc:
|
|
|
if not self.buffer_while_offline:
|
|
|
raise
|
|
@@ -196,6 +202,7 @@ class EventReceiver(ConsumerMixin):
|
|
|
routing_key=self.routing_key,
|
|
|
auto_delete=True,
|
|
|
durable=False)
|
|
|
+ self.adjust_clock = self.app.clock.adjust
|
|
|
|
|
|
def get_exchange(self):
|
|
|
return get_exchange(self.connection)
|
|
@@ -232,12 +239,24 @@ class EventReceiver(ConsumerMixin):
|
|
|
connection=self.connection,
|
|
|
channel=channel)
|
|
|
|
|
|
- def _receive(self, body, message):
|
|
|
- type = body.pop('type').lower()
|
|
|
+ def event_from_message(self, body, localize=True):
|
|
|
+ type = body.get('type', '').lower()
|
|
|
clock = body.get('clock')
|
|
|
if clock:
|
|
|
- self.app.clock.adjust(clock)
|
|
|
- self.process(type, Event(type, body))
|
|
|
+ self.adjust_clock(body.get('clock') or 0)
|
|
|
+ if localize:
|
|
|
+ try:
|
|
|
+ offset, timestamp = _TZGETTER(body)
|
|
|
+ except KeyError:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ body['timestamp'] = adjust_timestamp(timestamp, offset)
|
|
|
+ from datetime import datetime
|
|
|
+ print('TS: %s' % (datetime.fromtimestamp(body['timestamp']), ))
|
|
|
+ return type, Event(type, body)
|
|
|
+
|
|
|
+ def _receive(self, body, message):
|
|
|
+ self.process(*self.event_from_message(body))
|
|
|
|
|
|
|
|
|
class Events(object):
|