Store last cron durations

Closes #13445
This commit is contained in:
Cédric Krier
2024-08-09 10:40:18 +02:00
parent 4fe02eee89
commit b14bcea3c1
10 changed files with 130 additions and 2 deletions
+4 -2
View File
@@ -2,7 +2,9 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form" position="inside">
<field name="companies" colspan="4"/>
<xpath expr="//notebook/page[@name='logs']" position="before">
<page name="companies">
<field name="companies" colspan="4"/>
</page>
</xpath>
</data>
+1
View File
@@ -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
+14
View File
@@ -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
+1
View File
@@ -58,6 +58,7 @@ def register():
avatar.Avatar,
avatar.AvatarCache,
cron.Cron,
cron.Log,
lang.Lang,
lang.LangConfigStart,
export.Export,
+39
View File
@@ -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 = '<Cron %s@%s %s>' % (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)
+26
View File
@@ -47,5 +47,31 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">run_once</field>
<field name="string">Run Once</field>
</record>
<record model="ir.ui.view" id="cron_log_view_list">
<field name="model">ir.cron.log</field>
<field name="type">tree</field>
<field name="name">cron_log_list</field>
</record>
<record model="ir.ui.view" id="cron_log_view_form">
<field name="model">ir.cron.log</field>
<field name="type">form</field>
<field name="name">cron_log_form</field>
</record>
<record model="ir.ui.view" id="cron_log_view_graph">
<field name="model">ir.cron.log</field>
<field name="type">graph</field>
<field name="name">cron_log_graph</field>
</record>
</data>
<data noupdate="1">
<record model="ir.cron" id="cron_cron_log_clean">
<field name="method">ir.cron.log|clean</field>
<field name="interval_number" eval="1"/>
<field name="interval_type">months</field>
</record>
</data>
</tryton>
+7
View File
@@ -24,6 +24,13 @@ this repository contains the full copyright notices and license terms. -->
<label name="timezone"/>
<field name="timezone"/>
</group>
<notebook colspan="4">
<page name="logs">
<field name="logs" colspan="4" mode="tree,graph,form"/>
</page>
</notebook>
<label name="next_call"/>
<field name="next_call"/>
<button name="run_once" colspan="2"/>
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form col="2">
<label name="cron"/>
<field name="cron"/>
<label name="started"/>
<field name="started"/>
<label name="ended"/>
<field name="ended"/>
<label name="duration"/>
<field name="duration"/>
</form>
@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<graph type="line">
<x>
<field name="started"/>
</x>
<y>
<field name="duration" fill="1"/>
</y>
</graph>
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree>
<field name="cron" expand="2"/>
<field name="started" widget="date"/>
<field name="started" widget="time"/>
<field name="ended" widget="date"/>
<field name="ended" widget="time"/>
<field name="duration" expand="1"/>
</tree>