diff --git a/auditlog/README.rst b/auditlog/README.rst index 70d38a77d..79a23a257 100644 --- a/auditlog/README.rst +++ b/auditlog/README.rst @@ -1,5 +1,10 @@ -Track user operation on data models -=================================== +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================================= +Audit Log - Track user operations +================================= This module allows the administrator to log user operations performed on data models such as ``create``, ``read``, ``write`` and ``delete``. @@ -11,12 +16,14 @@ Go to `Reporting / Audit / Rules` to subscribe rules. A rule defines which operations to log for a given data model. Then, check logs in the `Reporting / Audit / Logs` menu. -During installation, it will migrate any existing data from the `audittrail` -module (rules and logs). - -For further information, please visit: +A scheduled action exists to delete logs older than 6 months (180 days) +automatically but is not enabled by default. +To activate it and/or change the delay, go to the +`Configuration / Technical / Automation / Scheduled Actions` menu and edit the +`Auto-vacuum audit logs` entry. - * https://www.odoo.com/forum/help-1 +During installation, a one-time script will migrate any existing data from the +`audittrail` module (rules and logs). Known issues / Roadmap ====================== diff --git a/auditlog/__manifest__.py b/auditlog/__manifest__.py index 2f658faaa..e24de936c 100644 --- a/auditlog/__manifest__.py +++ b/auditlog/__manifest__.py @@ -21,7 +21,7 @@ { 'name': "Audit Log", - 'version': "8.0.1.2.0", + 'version': "8.0.1.3.0", 'author': "ABF OSIELL,Odoo Community Association (OCA)", 'license': "AGPL-3", 'website': "http://www.osiell.com", @@ -31,6 +31,7 @@ ], 'data': [ 'security/ir.model.access.csv', + 'data/ir_cron.xml', 'views/auditlog_view.xml', 'views/http_session_view.xml', 'views/http_request_view.xml', diff --git a/auditlog/data/ir_cron.xml b/auditlog/data/ir_cron.xml new file mode 100644 index 000000000..d0f77a082 --- /dev/null +++ b/auditlog/data/ir_cron.xml @@ -0,0 +1,18 @@ + + + + + + Auto-vacuum audit logs + 1 + days + -1 + + + auditlog.autovacuum + autovacuum + (180,) + + + + diff --git a/auditlog/models/__init__.py b/auditlog/models/__init__.py index e71197ade..12345359e 100644 --- a/auditlog/models/__init__.py +++ b/auditlog/models/__init__.py @@ -23,3 +23,4 @@ from . import rule from . import http_session from . import http_request from . import log +from . import autovacuum diff --git a/auditlog/models/autovacuum.py b/auditlog/models/autovacuum.py new file mode 100644 index 000000000..078c7ef8f --- /dev/null +++ b/auditlog/models/autovacuum.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# © 2016 ABF OSIELL SARL, Sebastien Alix (http://osiell.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging +from datetime import datetime, timedelta + +from openerp import models, fields, api + + +_logger = logging.getLogger(__name__) + + +class AuditlogAutovacuum(models.TransientModel): + _name = 'auditlog.autovacuum' + _description = "Auditlog - Delete old logs" + + @api.model + def autovacuum(self, days): + """Delete all logs older than ``days``. This includes: + - CRUD logs (create, read, write, unlink) + - HTTP requests + - HTTP user sessions + + Called from a cron. + """ + days = (days > 0) and int(days) or 0 + deadline = datetime.now() - timedelta(days=days) + data_models = ( + 'auditlog.log', + 'auditlog.http.request', + 'auditlog.http.session', + ) + for data_model in data_models: + records = self.env[data_model].search( + [('create_date', '<=', fields.Datetime.to_string(deadline))]) + nb_records = len(records) + records.unlink() + _logger.info( + u"AUTOVACUUM - %s '%s' records deleted", + nb_records, data_model) + return True diff --git a/auditlog/models/log.py b/auditlog/models/log.py index 908923465..2493e462e 100644 --- a/auditlog/models/log.py +++ b/auditlog/models/log.py @@ -18,7 +18,6 @@ # along with this program. If not, see . # ############################################################################## - from openerp import models, fields @@ -54,7 +53,7 @@ class AuditlogLogLine(models.Model): field_id = fields.Many2one( 'ir.model.fields', ondelete='cascade', string=u"Field", required=True) log_id = fields.Many2one( - 'auditlog.log', string=u"Log", ondelete='cascade') + 'auditlog.log', string=u"Log", ondelete='cascade', index=True) old_value = fields.Text(u"Old Value") new_value = fields.Text(u"New Value") old_value_text = fields.Text(u"Old value Text") diff --git a/auditlog/tests/__init__.py b/auditlog/tests/__init__.py index a688b88a7..94c86efac 100644 --- a/auditlog/tests/__init__.py +++ b/auditlog/tests/__init__.py @@ -19,3 +19,4 @@ # ############################################################################## from . import test_auditlog +from . import test_autovacuum diff --git a/auditlog/tests/test_autovacuum.py b/auditlog/tests/test_autovacuum.py new file mode 100644 index 000000000..4c017e968 --- /dev/null +++ b/auditlog/tests/test_autovacuum.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# © 2016 ABF OSIELL SARL, Sebastien Alix (http://osiell.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import time + +from openerp.tests.common import TransactionCase + + +class TestAuditlogAutovacuum(TransactionCase): + + def setUp(self): + super(TestAuditlogAutovacuum, self).setUp() + self.groups_model_id = self.env.ref('base.model_res_groups').id + self.groups_rule = self.env['auditlog.rule'].create({ + 'name': 'testrule for groups', + 'model_id': self.groups_model_id, + 'log_read': True, + 'log_create': True, + 'log_write': True, + 'log_unlink': True, + 'state': 'subscribed', + 'log_type': 'full', + }) + + def tearDown(self): + self.groups_rule.unlink() + super(TestAuditlogAutovacuum, self).tearDown() + + def test_autovacuum(self): + log_model = self.env['auditlog.log'] + autovacuum_model = self.env['auditlog.autovacuum'] + group = self.env['res.groups'].create({ + 'name': 'testgroup1', + }) + nb_logs = log_model.search_count([ + ('model_id', '=', self.groups_model_id), + ('res_id', '=', group.id), + ]) + self.assertGreater(nb_logs, 0) + # Milliseconds are ignored by autovacuum, waiting 1s ensure that + # the logs generated will be processed by the vacuum + time.sleep(1) + autovacuum_model.autovacuum(days=0) + nb_logs = log_model.search_count([ + ('model_id', '=', self.groups_model_id), + ('res_id', '=', group.id), + ]) + self.assertEqual(nb_logs, 0)