diff --git a/modules/company/view/cron_form.xml b/modules/company/view/cron_form.xml
index f37c188b6a..3550f93d4a 100644
--- a/modules/company/view/cron_form.xml
+++ b/modules/company/view/cron_form.xml
@@ -2,7 +2,9 @@
-
-
+
+
+
+
diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG
index 52ef617c72..e5988f94a6 100644
--- a/trytond/CHANGELOG
+++ b/trytond/CHANGELOG
@@ -1,3 +1,4 @@
+* Store last cron durations
* Use JSONB to store MultiSelection field by default
* Add cardinality option to Index Usage
* Allow configuring TinyMCE license key
diff --git a/trytond/doc/topics/configuration.rst b/trytond/doc/topics/configuration.rst
index f88ffc68eb..1d25232bd0 100644
--- a/trytond/doc/topics/configuration.rst
+++ b/trytond/doc/topics/configuration.rst
@@ -420,6 +420,20 @@ cleared.
Default: ``1000``
+.. _config-cron:
+
+cron
+----
+
+.. _config-cron.clean_days:
+
+clean_days
+~~~~~~~~~~
+
+The number of days after which scheduled task logs are removed.
+
+Default: ``30``
+
.. _config-queue:
queue
diff --git a/trytond/trytond/ir/__init__.py b/trytond/trytond/ir/__init__.py
index 8595c1e2c8..b8ddd055b1 100644
--- a/trytond/trytond/ir/__init__.py
+++ b/trytond/trytond/ir/__init__.py
@@ -58,6 +58,7 @@ def register():
avatar.Avatar,
avatar.AvatarCache,
cron.Cron,
+ cron.Log,
lang.Lang,
lang.LangConfigStart,
export.Export,
diff --git a/trytond/trytond/ir/cron.py b/trytond/trytond/ir/cron.py
index e78b8fd45e..d1c773cdfc 100644
--- a/trytond/trytond/ir/cron.py
+++ b/trytond/trytond/ir/cron.py
@@ -20,6 +20,7 @@ from trytond.tools import timezone as tz
from trytond.transaction import Transaction, TransactionError
from trytond.worker import run_task
+clean_days = config.getint('cron', 'clean_days', default=30)
logger = logging.getLogger(__name__)
@@ -76,8 +77,11 @@ class Cron(DeactivableMixin, ModelSQL, ModelView):
('ir.trigger|trigger_time', "Run On Time Triggers"),
('ir.queue|clean', "Clean Task Queue"),
('ir.error|clean', "Clean Errors"),
+ ('ir.cron.log|clean', "Clean Cron Logs"),
], "Method", required=True)
+ logs = fields.One2Many('ir.cron.log', 'cron', "Logs", readonly=True)
+
@classmethod
def __setup__(cls):
super(Cron, cls).__setup__()
@@ -176,6 +180,7 @@ class Cron(DeactivableMixin, ModelSQL, ModelView):
**transaction_extras) as transaction:
pool = Pool()
Error = pool.get('ir.error')
+ Log = pool.get('ir.cron.log')
table = cls.__table__()
database = transaction.database
cursor = transaction.connection.cursor()
@@ -206,6 +211,7 @@ class Cron(DeactivableMixin, ModelSQL, ModelView):
def duration():
return (time.monotonic() - started) * 1000
started = time.monotonic()
+ started_datetime = datetime.datetime.now()
name = '' % (task.id, db_name, task.method)
try:
if not database.has_select_for():
@@ -214,8 +220,13 @@ class Cron(DeactivableMixin, ModelSQL, ModelView):
task.run_once()
task.next_call = task.compute_next_call(now)
task.save()
+ Log(
+ cron=task,
+ started=started_datetime,
+ ended=datetime.datetime.now()).save()
logger.info("%s in %i ms", name, duration())
except Exception as e:
+ print(e)
transaction.rollback()
if isinstance(e, TransactionError):
e.fix(transaction_extras)
@@ -252,3 +263,31 @@ class Cron(DeactivableMixin, ModelSQL, ModelView):
except backend.DatabaseOperationalError:
transaction.rollback()
logger.info('cron finished for "%s"', db_name)
+
+
+class Log(ModelSQL, ModelView):
+ "Cron Log"
+ __name__ = 'ir.cron.log'
+
+ cron = fields.Many2One(
+ 'ir.cron', "Cron", ondelete='CASCADE', required=True)
+ started = fields.DateTime("Started", required=True)
+ ended = fields.DateTime("Ended", required=True)
+ duration = fields.Function(
+ fields.TimeDelta("Duration"), 'get_duration')
+
+ @classmethod
+ def __setup__(cls):
+ super().__setup__()
+ cls.__access__.add('cron')
+
+ def get_duration(self, name):
+ return self.ended - self.started
+
+ @classmethod
+ def clean(cls, date=None):
+ if date is None:
+ date = (
+ datetime.datetime.now() - datetime.timedelta(days=clean_days))
+ logs = cls.search([('create_date', '<', date)])
+ cls.delete(logs)
diff --git a/trytond/trytond/ir/cron.xml b/trytond/trytond/ir/cron.xml
index 4dc22fcd16..b85d9dbe5e 100644
--- a/trytond/trytond/ir/cron.xml
+++ b/trytond/trytond/ir/cron.xml
@@ -47,5 +47,31 @@ this repository contains the full copyright notices and license terms. -->
run_once
Run Once
+
+
+ ir.cron.log
+ tree
+ cron_log_list
+
+
+
+ ir.cron.log
+ form
+ cron_log_form
+
+
+
+ ir.cron.log
+ graph
+ cron_log_graph
+
+
+
+
+
+ ir.cron.log|clean
+
+ months
+
diff --git a/trytond/trytond/ir/view/cron_form.xml b/trytond/trytond/ir/view/cron_form.xml
index 479250a2b0..a438bd5626 100644
--- a/trytond/trytond/ir/view/cron_form.xml
+++ b/trytond/trytond/ir/view/cron_form.xml
@@ -24,6 +24,13 @@ this repository contains the full copyright notices and license terms. -->
+
+
+
+
+
+
+
diff --git a/trytond/trytond/ir/view/cron_log_form.xml b/trytond/trytond/ir/view/cron_log_form.xml
new file mode 100644
index 0000000000..8d6efb18e1
--- /dev/null
+++ b/trytond/trytond/ir/view/cron_log_form.xml
@@ -0,0 +1,16 @@
+
+
+
diff --git a/trytond/trytond/ir/view/cron_log_graph.xml b/trytond/trytond/ir/view/cron_log_graph.xml
new file mode 100644
index 0000000000..36ec4f0660
--- /dev/null
+++ b/trytond/trytond/ir/view/cron_log_graph.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/trytond/trytond/ir/view/cron_log_list.xml b/trytond/trytond/ir/view/cron_log_list.xml
new file mode 100644
index 0000000000..696b134b84
--- /dev/null
+++ b/trytond/trytond/ir/view/cron_log_list.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+