
* Change AsyncTask constructor to receive a dict task_info instead of adding more parameters * Handle WokException separately in order to add it in translatable form to user log Signed-off-by: Lucio Correia <luciojhc@linux.vnet.ibm.com> --- src/wok/asynctask.py | 71 ++++++++++++++++++++++++++++++++++++++++++---------- src/wok/i18n.py | 3 +++ src/wok/reqlogger.py | 20 +++++++++++++-- 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/wok/asynctask.py b/src/wok/asynctask.py index 82a91ec..5d32f5e 100644 --- a/src/wok/asynctask.py +++ b/src/wok/asynctask.py @@ -24,25 +24,45 @@ import threading import traceback -from wok.exception import OperationFailed -from wok.utils import get_next_task_id +from wok.exception import OperationFailed, WokException +from wok.reqlogger import RequestRecord +from wok.reqlogger import TASK_FAILED, TASK_STARTED, TASK_SUCCESS +from wok.utils import get_next_task_id, get_plugin_from_uri + + +METHOD_TASK = 'TASK' +MSG_FAILED = 'WOKASYNC0003L' +MSG_STARTED = 'WOKASYNC0001L' +MSG_SUCCESS = 'WOKASYNC0002L' def add_task(target_uri, fn, objstore, opaque=None): id = get_next_task_id() - AsyncTask(id, target_uri, fn, objstore, opaque) + task_info = { + 'id': id, + 'target_uri': target_uri, + 'fn': fn, + 'objstore': objstore, + 'plugin': get_plugin_from_uri(target_uri), + 'user': '', + 'ip': '', + } + AsyncTask(task_info, opaque) return id class AsyncTask(object): - def __init__(self, id, target_uri, fn, objstore, opaque=None): - if objstore is None: + def __init__(self, task_info, opaque=None): + if task_info['objstore'] is None: raise OperationFailed("WOKASYNC0001E") - self.id = str(id) - self.target_uri = target_uri - self.fn = fn - self.objstore = objstore + # task info + self.id = str(task_info['id']) + self.target_uri = task_info['target_uri'] + self.fn = task_info['fn'] + self.task_info = task_info + + # task context self.status = 'running' self.message = 'OK' self._save_helper() @@ -52,9 +72,29 @@ class AsyncTask(object): self.thread.setDaemon(True) self.thread.start() - def _status_cb(self, message, success=None): + # log async task start + self._log(MSG_STARTED, TASK_STARTED) + + def _log(self, msgCode, status, exception=None): + RequestRecord( + self.task_info, + exception, + app=self.task_info['plugin'], + msgCode=msgCode, + req=METHOD_TASK, + status=status, + user=self.task_info['user'], + ip=self.task_info['ip'] + ).log() + + def _status_cb(self, message, success=None, exception=None): if success is not None: - self.status = 'finished' if success else 'failed' + if success: + self.status = 'finished' + self._log(MSG_SUCCESS, TASK_SUCCESS) + else: + self.status = 'failed' + self._log(MSG_FAILED, TASK_FAILED, exception) if message.strip(): self.message = message @@ -65,7 +105,8 @@ class AsyncTask(object): for attr in ('id', 'target_uri', 'message', 'status'): obj[attr] = getattr(self, attr) try: - with self.objstore as session: + objstore = self.task_info['objstore'] + with objstore as session: session.store('task', self.id, obj) except Exception as e: raise OperationFailed('WOKASYNC0002E', {'err': e.message}) @@ -74,7 +115,11 @@ class AsyncTask(object): cherrypy.serving.request = self._cp_request try: self.fn(cb, opaque) + except WokException, e: + cherrypy.log.error_log.error("Error in async_task %s " % self.id) + cherrypy.log.error_log.error(traceback.format_exc()) + cb(e.message, success=False, exception=e) except Exception, e: cherrypy.log.error_log.error("Error in async_task %s " % self.id) cherrypy.log.error_log.error(traceback.format_exc()) - cb(e.message, False) + cb(e.message, success=False) diff --git a/src/wok/i18n.py b/src/wok/i18n.py index 33107ee..c271446 100644 --- a/src/wok/i18n.py +++ b/src/wok/i18n.py @@ -58,6 +58,9 @@ messages = { "WOKPROXY0001E": _("Unable to (re)start system's nginx.service. Details: '%(error)s'"), # These messages (ending with L) are for user log purposes + "WOKASYNC0001L": _("Started %(plugin)s task ID %(id)s: %(target_uri)s"), + "WOKASYNC0002L": _("Successfully completed %(plugin)s task ID %(id)s: %(target_uri)s"), + "WOKASYNC0003L": _("Failed to complete %(plugin)s task ID %(id)s: %(target_uri)s"), "WOKCOL0001L": _("Request made on collection"), "WOKRES0001L": _("Request made on resource"), "WOKROOT0001L": _("User '%(username)s' login"), diff --git a/src/wok/reqlogger.py b/src/wok/reqlogger.py index 9f1d2c8..c57b141 100644 --- a/src/wok/reqlogger.py +++ b/src/wok/reqlogger.py @@ -39,8 +39,8 @@ from wok.utils import remove_old_files FILTER_FIELDS = ['app', 'date', 'ip', 'req', 'status' 'user', 'time'] LOG_DOWNLOAD_URI = "/data/logs/%s" LOG_DOWNLOAD_TIMEOUT = 6 -LOG_FORMAT = "[%(date)s %(time)s %(zone)s] %(req)-6s %(status)s %(app)-11s " \ - "%(ip)-15s %(user)s: %(message)s\n" +LOG_FORMAT = "[%(date)s %(time)s %(zone)s] %(req)-6s %(status)-4s %(app)-11s" \ + " %(ip)-15s %(user)s: %(message)s\n" RECORD_TEMPLATE_DICT = { 'date': '', 'time': '', @@ -62,6 +62,16 @@ UNSAFE_REQUEST_PARAMETERS = ['password', 'passwd'] REQUEST_LOG_FILE = "wok-req.log" WOK_REQUEST_LOGGER = "wok_request_logger" +# Task logging +TASK_FAILED = 1002 +TASK_STARTED = 1000 +TASK_SUCCESS = 1001 +TASK_STATUSES = { + TASK_FAILED: 'FAIL', + TASK_STARTED: 'INIT', + TASK_SUCCESS: 'SUCC', +} + class RequestLogger(object): def __init__(self): @@ -106,6 +116,12 @@ class RequestParser(object): for record in sortedList: asciiRecord = RECORD_TEMPLATE_DICT asciiRecord.update(ascii_dict(record)) + + # log meaning of statuses 1000, 1001, 1002 for clarity + status = int(asciiRecord['status']) + if status in TASK_STATUSES.keys(): + asciiRecord['status'] = TASK_STATUSES[status] + fd.write(LOG_FORMAT % asciiRecord) fd.close() -- 1.9.1