@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -58,6 +58,7 @@ def register():
|
||||
avatar.Avatar,
|
||||
avatar.AvatarCache,
|
||||
cron.Cron,
|
||||
cron.Log,
|
||||
lang.Lang,
|
||||
lang.LangConfigStart,
|
||||
export.Export,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user