[ADD] ir_actions.py: logger log in server action in context
This PR is mainly meant for the support. Note that the `stack_info` is purposefully accessible to add some stack trace in the logs. Example of support tickets where it can be useful: A certain field of a particular model change it's value with no particular pattern and way to reproduce. With this commit, we can now create an automated actions on the model update trigger to dump the current stack that will lead us on the action that did trigger it. closes odoo/odoo#75320 Signed-off-by: Julien Castiaux <juc@odoo.com>
This commit is contained in:
@@ -17,6 +17,32 @@ import logging
|
||||
from pytz import timezone
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
_server_action_logger = _logger.getChild("server_action_safe_eval")
|
||||
|
||||
|
||||
class LoggerProxy:
|
||||
""" Proxy of the `_logger` element in order to be used in server actions.
|
||||
We purposefully restrict its method as it will be executed in `safe_eval`.
|
||||
"""
|
||||
@staticmethod
|
||||
def log(level, message, *args, stack_info=False, exc_info=False):
|
||||
_server_action_logger.log(level, message, *args, stack_info=stack_info, exc_info=exc_info)
|
||||
|
||||
@staticmethod
|
||||
def info(message, *args, stack_info=False, exc_info=False):
|
||||
_server_action_logger.info(message, *args, stack_info=stack_info, exc_info=exc_info)
|
||||
|
||||
@staticmethod
|
||||
def warning(message, *args, stack_info=False, exc_info=False):
|
||||
_server_action_logger.warning(message, *args, stack_info=stack_info, exc_info=exc_info)
|
||||
|
||||
@staticmethod
|
||||
def error(message, *args, stack_info=False, exc_info=False):
|
||||
_server_action_logger.error(message, *args, stack_info=stack_info, exc_info=exc_info)
|
||||
|
||||
@staticmethod
|
||||
def exception(message, *args, stack_info=False, exc_info=True):
|
||||
_server_action_logger.exception(message, *args, stack_info=stack_info, exc_info=exc_info)
|
||||
|
||||
|
||||
class IrActions(models.Model):
|
||||
@@ -610,6 +636,7 @@ class IrActionsServer(models.Model):
|
||||
'records': records,
|
||||
# helpers
|
||||
'log': log,
|
||||
'_logger': LoggerProxy,
|
||||
})
|
||||
return eval_context
|
||||
|
||||
|
||||
@@ -59,8 +59,40 @@ class TestServerActionsBase(common.TransactionCase):
|
||||
'code': 'record.write({"comment": "%s"})' % self.comment_html,
|
||||
})
|
||||
|
||||
server_action_model = Model.search([('model', '=', 'ir.actions.server')])
|
||||
self.test_server_action = self.env['ir.actions.server'].create({
|
||||
'name': 'TestDummyServerAction',
|
||||
'model_id': server_action_model.id,
|
||||
'state': 'code',
|
||||
'code':
|
||||
"""
|
||||
_logger.log(10, "This is a %s debug %s", "test", "log")
|
||||
_logger.info("This is a %s info %s", "test", "log")
|
||||
_logger.warning("This is a %s warning %s", "test", "log")
|
||||
_logger.error("This is a %s error %s", "test", "log")
|
||||
try:
|
||||
0/0
|
||||
except:
|
||||
_logger.exception("This is a %s exception %s", "test", "log")
|
||||
""",
|
||||
})
|
||||
|
||||
|
||||
class TestServerActions(TestServerActionsBase):
|
||||
def test_00_server_action(self):
|
||||
with self.assertLogs('odoo.addons.base.models.ir_actions.server_action_safe_eval',
|
||||
level='DEBUG') as log_catcher:
|
||||
self.test_server_action.run()
|
||||
self.assertEqual(log_catcher.output, [
|
||||
'DEBUG:odoo.addons.base.models.ir_actions.server_action_safe_eval:This is a test debug log',
|
||||
'INFO:odoo.addons.base.models.ir_actions.server_action_safe_eval:This is a test info log',
|
||||
'WARNING:odoo.addons.base.models.ir_actions.server_action_safe_eval:This is a test warning log',
|
||||
'ERROR:odoo.addons.base.models.ir_actions.server_action_safe_eval:This is a test error log',
|
||||
"""ERROR:odoo.addons.base.models.ir_actions.server_action_safe_eval:This is a test exception log
|
||||
Traceback (most recent call last):
|
||||
File "ir.actions.server(%d,)", line 6, in <module>
|
||||
ZeroDivisionError: division by zero""" % self.test_server_action.id
|
||||
])
|
||||
|
||||
def test_00_action(self):
|
||||
self.action.with_context(self.context).run()
|
||||
|
||||
@@ -331,13 +331,16 @@
|
||||
<h3>Help with Python expressions</h3>
|
||||
<p>Various fields may use Python code or Python expressions. The following variables can be used:</p>
|
||||
<ul>
|
||||
<li><code>env</code>: Odoo Environment on which the action is triggered</li>
|
||||
<li><code>model</code>: Odoo Model of the record on which the action is triggered; is a void recordset</li>
|
||||
<li><code>env</code>: environment on which the action is triggered</li>
|
||||
<li><code>model</code>: model of the record on which the action is triggered; is a void recordset</li>
|
||||
<li><code>record</code>: record on which the action is triggered; may be be void</li>
|
||||
<li><code>records</code>: recordset of all records on which the action is triggered in multi mode; may be void</li>
|
||||
<li><code>time</code>, <code>datetime</code>, <code>dateutil</code>, <code>timezone</code>: useful Python libraries</li>
|
||||
<li><code>log(message, level='info')</code>:logging function to record debug information in <code>ir.logging</code> table</li>
|
||||
<li><code>UserError</code>: Warning Exception to use with <code>raise</code></li>
|
||||
<li><code>float_compare()</code>: utility function to compare floats based on a specific precision</li>
|
||||
<li><code>log(message, level='info')</code>: logging function to record debug information in <code>ir.logging</code> table</li>
|
||||
<li><code>_logger.info(message)</code>: logger to emit messages in server logs</li>
|
||||
<li><code>UserError</code>: exception class for raising user-facing warning messages</li>
|
||||
<li><code>Command</code>: x2many commands namespace</li>
|
||||
<li>To return an action, assign: <code>action = {...}</code></li>
|
||||
</ul>
|
||||
<div attrs="{'invisible': [('state', '!=', 'code')]}">
|
||||
|
||||
Reference in New Issue
Block a user