|
@@ -12,162 +12,51 @@
|
|
|
"""
|
|
|
from __future__ import absolute_import
|
|
|
|
|
|
-import os
|
|
|
-import sys
|
|
|
+import logging
|
|
|
import time
|
|
|
import socket
|
|
|
-import warnings
|
|
|
|
|
|
from datetime import datetime
|
|
|
+from operator import itemgetter
|
|
|
|
|
|
-from .. import current_app
|
|
|
from .. import exceptions
|
|
|
-from .. import platforms
|
|
|
-from .. import registry
|
|
|
+from ..registry import tasks
|
|
|
from ..app import app_or_default
|
|
|
-from ..datastructures import ExceptionInfo
|
|
|
-from ..execute.trace import TaskTrace
|
|
|
-from ..utils import noop, kwdict, fun_takes_kwargs, truncate_text
|
|
|
-from ..utils.encoding import safe_repr, safe_str, default_encoding
|
|
|
+from ..execute.trace import build_tracer, trace_task, report_internal_error
|
|
|
+from ..platforms import set_mp_process_title as setps
|
|
|
+from ..utils import (noop, kwdict, fun_takes_kwargs,
|
|
|
+ cached_property, truncate_text)
|
|
|
+from ..utils.encoding import safe_repr, safe_str
|
|
|
from ..utils.timeutils import maybe_iso8601, timezone
|
|
|
-from ..utils.serialization import get_pickleable_exception
|
|
|
|
|
|
from . import state
|
|
|
|
|
|
-#: Keys to keep from the message delivery info. The values
|
|
|
-#: of these keys must be pickleable.
|
|
|
-WANTED_DELIVERY_INFO = ("exchange", "routing_key", "consumer_tag", )
|
|
|
+# Localize
|
|
|
+tz_to_local = timezone.to_local
|
|
|
+tz_or_local = timezone.tz_or_local
|
|
|
+tz_utc = timezone.utc
|
|
|
|
|
|
|
|
|
-class InvalidTaskError(Exception):
|
|
|
- """The task has invalid data or is not properly constructed."""
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
-if sys.version_info >= (3, 0):
|
|
|
-
|
|
|
- def default_encode(obj):
|
|
|
- return obj
|
|
|
-else:
|
|
|
-
|
|
|
- def default_encode(obj): # noqa
|
|
|
- return unicode(obj, default_encoding())
|
|
|
-
|
|
|
-
|
|
|
-class WorkerTaskTrace(TaskTrace):
|
|
|
- """Wraps the task in a jail, catches all exceptions, and
|
|
|
- saves the status and result of the task execution to the task
|
|
|
- meta backend.
|
|
|
-
|
|
|
- If the call was successful, it saves the result to the task result
|
|
|
- backend, and sets the task status to `"SUCCESS"`.
|
|
|
-
|
|
|
- If the call raises :exc:`~celery.exceptions.RetryTaskError`, it extracts
|
|
|
- the original exception, uses that as the result and sets the task status
|
|
|
- to `"RETRY"`.
|
|
|
-
|
|
|
- If the call results in an exception, it saves the exception as the task
|
|
|
- result, and sets the task status to `"FAILURE"`.
|
|
|
-
|
|
|
- :param task_name: The name of the task to execute.
|
|
|
- :param task_id: The unique id of the task.
|
|
|
- :param args: List of positional args to pass on to the function.
|
|
|
- :param kwargs: Keyword arguments mapping to pass on to the function.
|
|
|
-
|
|
|
- :keyword loader: Custom loader to use, if not specified the current app
|
|
|
- loader will be used.
|
|
|
- :keyword hostname: Custom hostname to use, if not specified the system
|
|
|
- hostname will be used.
|
|
|
-
|
|
|
- :returns: the evaluated functions return value on success, or
|
|
|
- the exception instance on failure.
|
|
|
-
|
|
|
- """
|
|
|
-
|
|
|
- #: Current loader.
|
|
|
- loader = None
|
|
|
-
|
|
|
- #: Hostname to report as.
|
|
|
- hostname = None
|
|
|
-
|
|
|
- def __init__(self, *args, **kwargs):
|
|
|
- self.loader = kwargs.get("loader") or current_app.loader
|
|
|
- self.hostname = kwargs.get("hostname") or socket.gethostname()
|
|
|
- super(WorkerTaskTrace, self).__init__(*args, **kwargs)
|
|
|
-
|
|
|
- self._store_errors = True
|
|
|
- if self.task.ignore_result:
|
|
|
- self._store_errors = self.task.store_errors_even_if_ignored
|
|
|
- self.super = super(WorkerTaskTrace, self)
|
|
|
-
|
|
|
- def execute_safe(self, *args, **kwargs):
|
|
|
- """Same as :meth:`execute`, but catches errors."""
|
|
|
- try:
|
|
|
- return self.execute(*args, **kwargs)
|
|
|
- except Exception, exc:
|
|
|
- _type, _value, _tb = sys.exc_info()
|
|
|
- _value = self.task.backend.prepare_exception(exc)
|
|
|
- exc_info = ExceptionInfo((_type, _value, _tb))
|
|
|
- warnings.warn("Exception outside body: %s: %s\n%s" % tuple(
|
|
|
- map(str, (exc.__class__, exc, exc_info.traceback))))
|
|
|
- return exc_info
|
|
|
-
|
|
|
- def execute(self):
|
|
|
- """Execute, trace and store the result of the task."""
|
|
|
- self.loader.on_task_init(self.task_id, self.task)
|
|
|
- if self.task.track_started:
|
|
|
- if not self.task.ignore_result:
|
|
|
- self.task.backend.mark_as_started(self.task_id,
|
|
|
- pid=os.getpid(),
|
|
|
- hostname=self.hostname)
|
|
|
- try:
|
|
|
- return super(WorkerTaskTrace, self).execute()
|
|
|
- finally:
|
|
|
- try:
|
|
|
- self.task.backend.process_cleanup()
|
|
|
- self.loader.on_process_cleanup()
|
|
|
- except (KeyboardInterrupt, SystemExit, MemoryError):
|
|
|
- raise
|
|
|
- except Exception, exc:
|
|
|
- logger = current_app.log.get_default_logger()
|
|
|
- logger.error("Process cleanup failed: %r", exc,
|
|
|
- exc_info=sys.exc_info())
|
|
|
-
|
|
|
- def handle_success(self, retval, *args):
|
|
|
- """Handle successful execution."""
|
|
|
- if not self.task.ignore_result:
|
|
|
- self.task.backend.mark_as_done(self.task_id, retval)
|
|
|
- return self.super.handle_success(retval, *args)
|
|
|
-
|
|
|
- def handle_retry(self, exc, type_, tb, strtb):
|
|
|
- """Handle retry exception."""
|
|
|
- message, orig_exc = exc.args
|
|
|
- if self._store_errors:
|
|
|
- self.task.backend.mark_as_retry(self.task_id, orig_exc, strtb)
|
|
|
- return self.super.handle_retry(exc, type_, tb, strtb)
|
|
|
-
|
|
|
- def handle_failure(self, exc, type_, tb, strtb):
|
|
|
- """Handle exception."""
|
|
|
- if self._store_errors:
|
|
|
- self.task.backend.mark_as_failure(self.task_id, exc, strtb)
|
|
|
- exc = get_pickleable_exception(exc)
|
|
|
- return self.super.handle_failure(exc, type_, tb, strtb)
|
|
|
-
|
|
|
-
|
|
|
-def execute_and_trace(task_name, *args, **kwargs):
|
|
|
+def execute_and_trace(name, uuid, args, kwargs, request=None, **opts):
|
|
|
"""This is a pickleable method used as a target when applying to pools.
|
|
|
|
|
|
It's the same as::
|
|
|
|
|
|
- >>> WorkerTaskTrace(task_name, *args, **kwargs).execute_safe()
|
|
|
+ >>> trace_task(name, *args, **kwargs)[0]
|
|
|
|
|
|
"""
|
|
|
- hostname = kwargs.get("hostname")
|
|
|
- platforms.set_mp_process_title("celeryd", task_name, hostname=hostname)
|
|
|
+ task = tasks[name]
|
|
|
try:
|
|
|
- return WorkerTaskTrace(task_name, *args, **kwargs).execute_safe()
|
|
|
- finally:
|
|
|
- platforms.set_mp_process_title("celeryd", "-idle-", hostname)
|
|
|
+ hostname = opts.get("hostname")
|
|
|
+ setps("celeryd", name, hostname, rate_limit=True)
|
|
|
+ try:
|
|
|
+ if task.__tracer__ is None:
|
|
|
+ task.__tracer__ = build_tracer(name, task, **opts)
|
|
|
+ return task.__tracer__(uuid, args, kwargs, request)[0]
|
|
|
+ finally:
|
|
|
+ setps("celeryd", "-idle-", hostname, rate_limit=True)
|
|
|
+ except Exception, exc:
|
|
|
+ return report_internal_error(task, exc)
|
|
|
|
|
|
|
|
|
class TaskRequest(object):
|
|
@@ -176,14 +65,14 @@ class TaskRequest(object):
|
|
|
#: Kind of task. Must be a name registered in the task registry.
|
|
|
name = None
|
|
|
|
|
|
- #: The task class (set by constructor using :attr:`task_name`).
|
|
|
+ #: The task class (set by constructor using :attr:`name`).
|
|
|
task = None
|
|
|
|
|
|
#: UUID of the task.
|
|
|
- task_id = None
|
|
|
+ id = None
|
|
|
|
|
|
#: UUID of the taskset that this task belongs to.
|
|
|
- taskset_id = None
|
|
|
+ taskset = None
|
|
|
|
|
|
#: List of positional arguments to apply to the task.
|
|
|
args = None
|
|
@@ -238,80 +127,83 @@ class TaskRequest(object):
|
|
|
_already_revoked = False
|
|
|
_terminate_on_ack = None
|
|
|
|
|
|
- def __init__(self, task_name, task_id, args, kwargs,
|
|
|
- on_ack=noop, retries=0, delivery_info=None, hostname=None,
|
|
|
+ def __init__(self, task, id, args=[], kwargs={},
|
|
|
+ on_ack=noop, retries=0, delivery_info={}, hostname=None,
|
|
|
logger=None, eventer=None, eta=None, expires=None, app=None,
|
|
|
- taskset_id=None, chord=None, utc=False, **opts):
|
|
|
- self.app = app_or_default(app)
|
|
|
- self.task_name = task_name
|
|
|
- self.task_id = task_id
|
|
|
- self.taskset_id = taskset_id
|
|
|
- self.retries = retries
|
|
|
+ taskset=None, chord=None, utc=False, connection_errors=None,
|
|
|
+ **opts):
|
|
|
+ try:
|
|
|
+ kwargs.items
|
|
|
+ except AttributeError:
|
|
|
+ raise exceptions.InvalidTaskError(
|
|
|
+ "Task keyword arguments is not a mapping")
|
|
|
+ self.app = app or app_or_default(app)
|
|
|
+ self.name = task
|
|
|
+ self.id = id
|
|
|
self.args = args
|
|
|
- self.kwargs = kwargs
|
|
|
- self.eta = eta
|
|
|
- self.expires = expires
|
|
|
+ self.kwargs = kwdict(kwargs)
|
|
|
+ self.taskset = taskset
|
|
|
+ self.retries = retries
|
|
|
self.chord = chord
|
|
|
self.on_ack = on_ack
|
|
|
self.delivery_info = {} if delivery_info is None else delivery_info
|
|
|
self.hostname = hostname or socket.gethostname()
|
|
|
self.logger = logger or self.app.log.get_default_logger()
|
|
|
self.eventer = eventer
|
|
|
+ self.connection_errors = connection_errors or ()
|
|
|
|
|
|
- self.task = registry.tasks[self.task_name]
|
|
|
- self._store_errors = True
|
|
|
- if self.task.ignore_result:
|
|
|
- self._store_errors = self.task.store_errors_even_if_ignored
|
|
|
+ task = self.task = tasks[task]
|
|
|
|
|
|
# timezone means the message is timezone-aware, and the only timezone
|
|
|
# supported at this point is UTC.
|
|
|
- self.tzlocal = timezone.tz_or_local(self.app.conf.CELERY_TIMEZONE)
|
|
|
- tz = timezone.utc if utc else self.tzlocal
|
|
|
- if self.eta is not None:
|
|
|
- self.eta = timezone.to_local(self.eta, self.tzlocal, tz)
|
|
|
- if self.expires is not None:
|
|
|
- self.expires = timezone.to_local(self.expires, self.tzlocal, tz)
|
|
|
+ if eta is not None:
|
|
|
+ tz = tz_utc if utc else self.tzlocal
|
|
|
+ self.eta = tz_to_local(maybe_iso8601(eta), self.tzlocal, tz)
|
|
|
+ if expires is not None:
|
|
|
+ tz = tz_utc if utc else self.tzlocal
|
|
|
+ self.expires = tz_to_local(maybe_iso8601(expires),
|
|
|
+ self.tzlocal, tz)
|
|
|
+
|
|
|
+ # shortcuts
|
|
|
+ self._does_debug = self.logger.isEnabledFor(logging.DEBUG)
|
|
|
+ self._does_info = self.logger.isEnabledFor(logging.INFO)
|
|
|
+
|
|
|
+ self.request_dict = {"hostname": self.hostname,
|
|
|
+ "id": id, "taskset": taskset,
|
|
|
+ "retries": retries, "is_eager": False,
|
|
|
+ "delivery_info": delivery_info, "chord": chord}
|
|
|
|
|
|
@classmethod
|
|
|
- def from_message(cls, message, body, on_ack=noop, **kw):
|
|
|
+ def from_message(cls, message, body, on_ack=noop, delivery_info={},
|
|
|
+ logger=None, hostname=None, eventer=None, app=None,
|
|
|
+ connection_errors=None):
|
|
|
"""Create request from a task message.
|
|
|
|
|
|
:raises UnknownTaskError: if the message does not describe a task,
|
|
|
the message is also rejected.
|
|
|
|
|
|
"""
|
|
|
- delivery_info = getattr(message, "delivery_info", {})
|
|
|
- delivery_info = dict((key, delivery_info.get(key))
|
|
|
- for key in WANTED_DELIVERY_INFO)
|
|
|
+ try:
|
|
|
+ D = message.delivery_info
|
|
|
+ delivery_info = {"exchange": D.get("exchange"),
|
|
|
+ "routing_key": D.get("routing_key")}
|
|
|
+ except (AttributeError, KeyError):
|
|
|
+ pass
|
|
|
|
|
|
- kwargs = body.get("kwargs", {})
|
|
|
- if not hasattr(kwargs, "items"):
|
|
|
- raise InvalidTaskError("Task keyword arguments is not a mapping.")
|
|
|
try:
|
|
|
- task_name = body["task"]
|
|
|
- task_id = body["id"]
|
|
|
- except KeyError, exc:
|
|
|
- raise InvalidTaskError(
|
|
|
- "Task message is missing required field %r" % (exc, ))
|
|
|
-
|
|
|
- return cls(task_name=task_name,
|
|
|
- task_id=task_id,
|
|
|
- taskset_id=body.get("taskset", None),
|
|
|
- args=body.get("args", []),
|
|
|
- kwargs=kwdict(kwargs),
|
|
|
- chord=body.get("chord"),
|
|
|
- retries=body.get("retries", 0),
|
|
|
- eta=maybe_iso8601(body.get("eta")),
|
|
|
- expires=maybe_iso8601(body.get("expires")),
|
|
|
- on_ack=on_ack,
|
|
|
- delivery_info=delivery_info,
|
|
|
- utc=body.get("utc", None),
|
|
|
- **kw)
|
|
|
+ return cls(on_ack=on_ack, logger=logger, eventer=eventer, app=app,
|
|
|
+ delivery_info=delivery_info, hostname=hostname,
|
|
|
+ connection_errors=connection_errors, **body)
|
|
|
+ except TypeError:
|
|
|
+ for f in ("task", "id"):
|
|
|
+ if f not in body:
|
|
|
+ raise exceptions.InvalidTaskError(
|
|
|
+ "Task message is missing required field %r" % (f, ))
|
|
|
|
|
|
def get_instance_attrs(self, loglevel, logfile):
|
|
|
return {"logfile": logfile, "loglevel": loglevel,
|
|
|
"hostname": self.hostname,
|
|
|
- "id": self.task_id, "taskset": self.taskset_id,
|
|
|
+ "id": self.id, "taskset": self.taskset,
|
|
|
"retries": self.retries, "is_eager": False,
|
|
|
"delivery_info": self.delivery_info, "chord": self.chord}
|
|
|
|
|
@@ -327,13 +219,11 @@ class TaskRequest(object):
|
|
|
in version 3.0.
|
|
|
|
|
|
"""
|
|
|
- if not self.task.accept_magic_kwargs:
|
|
|
- return self.kwargs
|
|
|
kwargs = dict(self.kwargs)
|
|
|
default_kwargs = {"logfile": logfile,
|
|
|
"loglevel": loglevel,
|
|
|
- "task_id": self.task_id,
|
|
|
- "task_name": self.task_name,
|
|
|
+ "task_id": self.id,
|
|
|
+ "task_name": self.name,
|
|
|
"task_retries": self.retries,
|
|
|
"task_is_eager": False,
|
|
|
"delivery_info": self.delivery_info}
|
|
@@ -356,13 +246,16 @@ class TaskRequest(object):
|
|
|
"""
|
|
|
if self.revoked():
|
|
|
return
|
|
|
+ request = self.request_dict
|
|
|
|
|
|
- args = self._get_tracer_args(loglevel, logfile)
|
|
|
- instance_attrs = self.get_instance_attrs(loglevel, logfile)
|
|
|
+ kwargs = self.kwargs
|
|
|
+ if self.task.accept_magic_kwargs:
|
|
|
+ kwargs = self.extend_with_default_kwargs(loglevel, logfile)
|
|
|
+ request.update({"loglevel": loglevel, "logfile": logfile})
|
|
|
result = pool.apply_async(execute_and_trace,
|
|
|
- args=args,
|
|
|
+ args=(self.name, self.id, self.args, kwargs),
|
|
|
kwargs={"hostname": self.hostname,
|
|
|
- "request": instance_attrs},
|
|
|
+ "request": request},
|
|
|
accept_callback=self.on_accepted,
|
|
|
timeout_callback=self.on_timeout,
|
|
|
callback=self.on_success,
|
|
@@ -372,7 +265,7 @@ class TaskRequest(object):
|
|
|
return result
|
|
|
|
|
|
def execute(self, loglevel=None, logfile=None):
|
|
|
- """Execute the task in a :class:`WorkerTaskTrace`.
|
|
|
+ """Execute the task in a :func:`~celery.execute.trace.trace_task`.
|
|
|
|
|
|
:keyword loglevel: The loglevel used by the task.
|
|
|
|
|
@@ -387,20 +280,19 @@ class TaskRequest(object):
|
|
|
self.acknowledge()
|
|
|
|
|
|
instance_attrs = self.get_instance_attrs(loglevel, logfile)
|
|
|
- tracer = WorkerTaskTrace(*self._get_tracer_args(loglevel, logfile),
|
|
|
- **{"hostname": self.hostname,
|
|
|
- "loader": self.app.loader,
|
|
|
- "request": instance_attrs})
|
|
|
- retval = tracer.execute()
|
|
|
+ retval, _ = trace_task(*self._get_tracer_args(loglevel, logfile, True),
|
|
|
+ **{"hostname": self.hostname,
|
|
|
+ "loader": self.app.loader,
|
|
|
+ "request": instance_attrs})
|
|
|
self.acknowledge()
|
|
|
return retval
|
|
|
|
|
|
def maybe_expire(self):
|
|
|
"""If expired, mark the task as revoked."""
|
|
|
if self.expires and datetime.now(self.tzlocal) > self.expires:
|
|
|
- state.revoked.add(self.task_id)
|
|
|
- if self._store_errors:
|
|
|
- self.task.backend.mark_as_revoked(self.task_id)
|
|
|
+ state.revoked.add(self.id)
|
|
|
+ if self.store_errors:
|
|
|
+ self.task.backend.mark_as_revoked(self.id)
|
|
|
|
|
|
def terminate(self, pool, signal=None):
|
|
|
if self.time_start:
|
|
@@ -414,10 +306,10 @@ class TaskRequest(object):
|
|
|
return True
|
|
|
if self.expires:
|
|
|
self.maybe_expire()
|
|
|
- if self.task_id in state.revoked:
|
|
|
+ if self.id in state.revoked:
|
|
|
self.logger.warn("Skipping revoked task: %s[%s]",
|
|
|
- self.task_name, self.task_id)
|
|
|
- self.send_event("task-revoked", uuid=self.task_id)
|
|
|
+ self.name, self.id)
|
|
|
+ self.send_event("task-revoked", uuid=self.id)
|
|
|
self.acknowledge()
|
|
|
self._already_revoked = True
|
|
|
return True
|
|
@@ -434,9 +326,10 @@ class TaskRequest(object):
|
|
|
state.task_accepted(self)
|
|
|
if not self.task.acks_late:
|
|
|
self.acknowledge()
|
|
|
- self.send_event("task-started", uuid=self.task_id, pid=pid)
|
|
|
- self.logger.debug("Task accepted: %s[%s] pid:%r",
|
|
|
- self.task_name, self.task_id, pid)
|
|
|
+ self.send_event("task-started", uuid=self.id, pid=pid)
|
|
|
+ if self._does_debug:
|
|
|
+ self.logger.debug("Task accepted: %s[%s] pid:%r",
|
|
|
+ self.name, self.id, pid)
|
|
|
if self._terminate_on_ack is not None:
|
|
|
_, pool, signal = self._terminate_on_ack
|
|
|
self.terminate(pool, signal)
|
|
@@ -446,15 +339,15 @@ class TaskRequest(object):
|
|
|
state.task_ready(self)
|
|
|
if soft:
|
|
|
self.logger.warning("Soft time limit (%ss) exceeded for %s[%s]",
|
|
|
- timeout, self.task_name, self.task_id)
|
|
|
+ timeout, self.name, self.id)
|
|
|
exc = exceptions.SoftTimeLimitExceeded(timeout)
|
|
|
else:
|
|
|
self.logger.error("Hard time limit (%ss) exceeded for %s[%s]",
|
|
|
- timeout, self.task_name, self.task_id)
|
|
|
+ timeout, self.name, self.id)
|
|
|
exc = exceptions.TimeLimitExceeded(timeout)
|
|
|
|
|
|
- if self._store_errors:
|
|
|
- self.task.backend.mark_as_failure(self.task_id, exc)
|
|
|
+ if self.store_errors:
|
|
|
+ self.task.backend.mark_as_failure(self.id, exc)
|
|
|
|
|
|
def on_success(self, ret_value):
|
|
|
"""Handler called if the task was successfully processed."""
|
|
@@ -464,26 +357,28 @@ class TaskRequest(object):
|
|
|
self.acknowledge()
|
|
|
|
|
|
runtime = self.time_start and (time.time() - self.time_start) or 0
|
|
|
- self.send_event("task-succeeded", uuid=self.task_id,
|
|
|
+ self.send_event("task-succeeded", uuid=self.id,
|
|
|
result=safe_repr(ret_value), runtime=runtime)
|
|
|
|
|
|
- self.logger.info(self.success_msg.strip(),
|
|
|
- {"id": self.task_id,
|
|
|
- "name": self.task_name,
|
|
|
- "return_value": self.repr_result(ret_value),
|
|
|
- "runtime": runtime})
|
|
|
+ if self._does_info:
|
|
|
+ self.logger.info(self.success_msg.strip(),
|
|
|
+ {"id": self.id,
|
|
|
+ "name": self.name,
|
|
|
+ "return_value": self.repr_result(ret_value),
|
|
|
+ "runtime": runtime})
|
|
|
|
|
|
def on_retry(self, exc_info):
|
|
|
"""Handler called if the task should be retried."""
|
|
|
- self.send_event("task-retried", uuid=self.task_id,
|
|
|
+ self.send_event("task-retried", uuid=self.id,
|
|
|
exception=safe_repr(exc_info.exception.exc),
|
|
|
traceback=safe_str(exc_info.traceback))
|
|
|
|
|
|
- self.logger.info(self.retry_msg.strip(),
|
|
|
- {"id": self.task_id,
|
|
|
- "name": self.task_name,
|
|
|
- "exc": safe_repr(exc_info.exception.exc)},
|
|
|
- exc_info=exc_info)
|
|
|
+ if self._does_info:
|
|
|
+ self.logger.info(self.retry_msg.strip(),
|
|
|
+ {"id": self.id,
|
|
|
+ "name": self.name,
|
|
|
+ "exc": safe_repr(exc_info.exception.exc)},
|
|
|
+ exc_info=exc_info)
|
|
|
|
|
|
def on_failure(self, exc_info):
|
|
|
"""Handler called if the task raised an exception."""
|
|
@@ -498,16 +393,16 @@ class TaskRequest(object):
|
|
|
# This is a special case as the process would not have had
|
|
|
# time to write the result.
|
|
|
if isinstance(exc_info.exception, exceptions.WorkerLostError) and \
|
|
|
- self._store_errors:
|
|
|
- self.task.backend.mark_as_failure(self.task_id, exc_info.exception)
|
|
|
+ self.store_errors:
|
|
|
+ self.task.backend.mark_as_failure(self.id, exc_info.exception)
|
|
|
|
|
|
- self.send_event("task-failed", uuid=self.task_id,
|
|
|
+ self.send_event("task-failed", uuid=self.id,
|
|
|
exception=safe_repr(exc_info.exception),
|
|
|
traceback=safe_str(exc_info.traceback))
|
|
|
|
|
|
context = {"hostname": self.hostname,
|
|
|
- "id": self.task_id,
|
|
|
- "name": self.task_name,
|
|
|
+ "id": self.id,
|
|
|
+ "name": self.name,
|
|
|
"exc": safe_repr(exc_info.exception),
|
|
|
"traceback": safe_str(exc_info.traceback),
|
|
|
"args": safe_repr(self.args),
|
|
@@ -515,17 +410,17 @@ class TaskRequest(object):
|
|
|
|
|
|
self.logger.error(self.error_msg.strip(), context,
|
|
|
exc_info=exc_info.exc_info,
|
|
|
- extra={"data": {"id": self.task_id,
|
|
|
- "name": self.task_name,
|
|
|
+ extra={"data": {"id": self.id,
|
|
|
+ "name": self.name,
|
|
|
"hostname": self.hostname}})
|
|
|
|
|
|
- task_obj = registry.tasks.get(self.task_name, object)
|
|
|
+ task_obj = tasks.get(self.name, object)
|
|
|
task_obj.send_error_email(context, exc_info.exception)
|
|
|
|
|
|
def acknowledge(self):
|
|
|
"""Acknowledge task."""
|
|
|
if not self.acknowledged:
|
|
|
- self.on_ack()
|
|
|
+ self.on_ack(self.logger, self.connection_errors)
|
|
|
self.acknowledged = True
|
|
|
|
|
|
def repr_result(self, result, maxlen=46):
|
|
@@ -534,8 +429,8 @@ class TaskRequest(object):
|
|
|
return truncate_text(safe_repr(result), maxlen)
|
|
|
|
|
|
def info(self, safe=False):
|
|
|
- return {"id": self.task_id,
|
|
|
- "name": self.task_name,
|
|
|
+ return {"id": self.id,
|
|
|
+ "name": self.name,
|
|
|
"args": self.args if safe else safe_repr(self.args),
|
|
|
"kwargs": self.kwargs if safe else safe_repr(self.kwargs),
|
|
|
"hostname": self.hostname,
|
|
@@ -546,8 +441,7 @@ class TaskRequest(object):
|
|
|
|
|
|
def shortinfo(self):
|
|
|
return "%s[%s]%s%s" % (
|
|
|
- self.task_name,
|
|
|
- self.task_id,
|
|
|
+ self.name, self.id,
|
|
|
" eta:[%s]" % (self.eta, ) if self.eta else "",
|
|
|
" expires:[%s]" % (self.expires, ) if self.expires else "")
|
|
|
__str__ = shortinfo
|
|
@@ -555,9 +449,43 @@ class TaskRequest(object):
|
|
|
def __repr__(self):
|
|
|
return '<%s: {name:"%s", id:"%s", args:"%s", kwargs:"%s"}>' % (
|
|
|
self.__class__.__name__,
|
|
|
- self.task_name, self.task_id, self.args, self.kwargs)
|
|
|
+ self.name, self.id, self.args, self.kwargs)
|
|
|
+
|
|
|
+ def _get_tracer_args(self, loglevel=None, logfile=None, use_real=False):
|
|
|
+ """Get the task trace args for this task."""
|
|
|
+ kwargs = self.kwargs
|
|
|
+ if self.task.accept_magic_kwargs:
|
|
|
+ kwargs = self.extend_with_default_kwargs(loglevel, logfile)
|
|
|
+ first = self.task if use_real else self.name
|
|
|
+ return first, self.id, self.args, kwargs
|
|
|
+
|
|
|
+ @cached_property
|
|
|
+ def tzlocal(self):
|
|
|
+ return tz_or_local(self.app.conf.CELERY_TIMEZONE)
|
|
|
+
|
|
|
+ @property
|
|
|
+ def store_errors(self):
|
|
|
+ return (not self.task.ignore_result
|
|
|
+ or self.task.store_errors_even_if_ignored)
|
|
|
+
|
|
|
+ def _compat_get_task_id(self):
|
|
|
+ return self.id
|
|
|
+
|
|
|
+ def _compat_set_task_id(self, value):
|
|
|
+ self.id = value
|
|
|
+
|
|
|
+ def _compat_get_task_name(self):
|
|
|
+ return self.name
|
|
|
+
|
|
|
+ def _compat_set_task_name(self, value):
|
|
|
+ self.name = value
|
|
|
+
|
|
|
+ def _compat_get_taskset_id(self):
|
|
|
+ return self.taskset
|
|
|
+
|
|
|
+ def _compat_set_taskset_id(self, value):
|
|
|
+ self.taskset = value
|
|
|
|
|
|
- def _get_tracer_args(self, loglevel=None, logfile=None):
|
|
|
- """Get the :class:`WorkerTaskTrace` tracer for this task."""
|
|
|
- task_func_kwargs = self.extend_with_default_kwargs(loglevel, logfile)
|
|
|
- return self.task_name, self.task_id, self.args, task_func_kwargs
|
|
|
+ task_id = property(_compat_get_task_id, _compat_set_task_id)
|
|
|
+ task_name = property(_compat_get_task_name, _compat_set_task_name)
|
|
|
+ taskset_id = property(_compat_get_taskset_id, _compat_set_taskset_id)
|