Skip to content

Commit

Permalink
Merge pull request OCA#22 from guewen/13.0-shopfloor-logs
Browse files Browse the repository at this point in the history
Log shopfloor requests in a table
  • Loading branch information
guewen authored Jun 2, 2020
2 parents 70c33b8 + 1b174d5 commit f3518a8
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 3 deletions.
3 changes: 3 additions & 0 deletions shopfloor/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@
"delivery",
],
"data": [
"data/ir_config_parameter_data.xml",
"data/ir_cron_data.xml",
"security/ir.model.access.csv",
"views/shopfloor_menu.xml",
"views/stock_picking_type.xml",
"views/stock_location.xml",
"views/stock_move_line.xml",
"views/shopfloor_profile_views.xml",
"views/shopfloor_log_views.xml",
"views/menus.xml",
],
"demo": [
Expand Down
7 changes: 7 additions & 0 deletions shopfloor/data/ir_config_parameter_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8' ?>
<odoo noupdate="1">
<record id="config_shopfloor_log_retention_days" model="ir.config_parameter">
<field name="key">shopfloor.log.retention.days</field>
<field name="value">30</field>
</record>
</odoo>
15 changes: 15 additions & 0 deletions shopfloor/data/ir_cron_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="ir_cron_autovacuum_shopfloor_log" model="ir.cron">
<field name="name">Auto-vacuum Shopfloor Logs</field>
<field ref="model_shopfloor_log" name="model_id" />
<field eval="True" name="active" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field name="state">code</field>
<field name="code">model.autovacuum()</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions shopfloor/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from . import shopfloor_menu
from . import shopfloor_log
from . import stock_picking_type
from . import shopfloor_profile
from . import stock_inventory
Expand Down
58 changes: 58 additions & 0 deletions shopfloor/models/shopfloor_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import logging
from datetime import datetime, timedelta

from odoo import fields, models

_logger = logging.getLogger(__name__)


class ShopfloorLog(models.Model):
_name = "shopfloor.log"
_description = "Shopfloor Logging"
_order = "id desc"

DEFAULT_RETENTION = 30 # days

request_url = fields.Char(readonly=True, string="Request URL")
request_method = fields.Char(readonly=True)
params = fields.Text(readonly=True)
headers = fields.Text(readonly=True)
result = fields.Text(readonly=True)
error = fields.Text(readonly=True)
state = fields.Selection(
selection=[("success", "Success"), ("failed", "Failed")], readonly=True,
)

def _logs_retention_days(self):
retention = self.DEFAULT_RETENTION
param = (
self.env["ir.config_parameter"]
.sudo()
.get_param("shopfloor.log.retention.days")
)
if param:
try:
retention = int(param)
except ValueError:
_logger.exception(
"Could not convert System Parameter"
" 'shopfloor.log.retention.days' to integer,"
" reverting to the"
" default configuration."
)
return retention

def logging_active(self):
retention = self._logs_retention_days()
return retention > 0

def autovacuum(self):
"""Delete logs which have exceeded their retention duration
Called from a cron.
"""
deadline = datetime.now() - timedelta(days=self._logs_retention_days())
logs = self.search([("create_date", "<=", deadline)])
if logs:
logs.unlink()
return True
16 changes: 16 additions & 0 deletions shopfloor/readme/CONFIGURE.rst
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
writeme

Logs retention
--------------

Logs are kept in database for every REST requests made by a client application.
They can be used for debugging and monitoring of the activity.

The Logs menu is shown only with Developer tools (``?debug=1``) activated.

By default, Shopfloor logs are kept 30 days.
You can change the duration of the retention by changing the System Parameter
``shopfloor.log.retention.days``.

If the value is set to 0, the logs are not stored at all.

Logged data is: request URL and method, parameters, headers, result or error.
5 changes: 3 additions & 2 deletions shopfloor/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"id","name","model_id/id","group_id/id","perm_read","perm_write","perm_create","perm_unlink"
"access_shopfloor_menu_users","shopfloor menu","model_shopfloor_menu",,1,0,0,0
"access_shopfloor_menu_users","shopfloor menu","model_shopfloor_menu","stock.group_stock_user",1,0,0,0
"access_shopfloor_menu_stock_manager","shopfloor menu inventory manager","model_shopfloor_menu","stock.group_stock_manager",1,1,1,1
"access_shopfloor_profile_users","shopfloor profile","model_shopfloor_profile",,1,0,0,0
"access_shopfloor_profile_users","shopfloor profile","model_shopfloor_profile","stock.group_stock_user",1,0,0,0
"access_shopfloor_profile_stock_manager","shopfloor profile inventory manager","model_shopfloor_profile","stock.group_stock_manager",1,1,1,1
"access_shopfloor_log","access_shopfloor_log","model_shopfloor_log","stock.group_stock_manager",1,0,0,0
60 changes: 59 additions & 1 deletion shopfloor/services/service.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from odoo import _, exceptions
from odoo import _, exceptions, registry
from odoo.exceptions import MissingError
from odoo.http import request
from odoo.osv import expression

from odoo.addons.base_rest.controllers.main import _PseudoCollection
Expand All @@ -22,6 +23,63 @@ class BaseShopfloorService(AbstractComponent):
_collection = "shopfloor.service"
_actions_collection_name = "shopfloor.action"
_expose_model = None
# can be overridden to disable logging of requests to DB
_log_calls_in_db = True

def dispatch(self, method_name, _id=None, params=None):
if not self._db_logging_active():
return super().dispatch(method_name, _id=_id, params=params)
return self._dispatch_with_db_logging(method_name, _id=_id, params=params)

def _db_logging_active(self):
return (
request
and self._log_calls_in_db
and self.env["shopfloor.log"].logging_active()
)

# TODO logging to DB should be an extra module for base_rest
def _dispatch_with_db_logging(self, method_name, _id=None, params=None):
try:
result = super().dispatch(method_name, _id=_id, params=params)
except Exception as err:
self.env.cr.rollback()
with registry(self.env.cr.dbname).cursor() as cr:
env = self.env(cr=cr)
self._log_call_in_db(env, request, _id, params, error=err)
raise
self._log_call_in_db(self.env, request, _id, params, result=result)
return result

@property
def _log_call_header_strip(self):
return ("Cookie", "Api-Key")

def _log_call_in_db_values(self, _request, _id, params, result=None, error=None):
httprequest = _request.httprequest
headers = dict(httprequest.headers)
for header_key in self._log_call_header_strip:
if header_key in headers:
headers[header_key] = "<redacted>"
if _id:
params = dict(params, _id=_id)
return {
"request_url": httprequest.url,
"request_method": httprequest.method,
"params": params,
"headers": headers,
"result": result,
"error": error,
"state": "success" if result else "failed",
}

def _log_call_in_db(self, env, _request, _id, params, result=None, error=None):
values = self._log_call_in_db_values(
_request, _id, params, result=result, error=error
)
if not values:
return
env["shopfloor.log"].sudo().create(values)

def _get(self, _id):
domain = expression.normalize_domain(self._get_base_search_domain())
Expand Down
7 changes: 7 additions & 0 deletions shopfloor/views/menus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@
parent="menu_shopfloor_settings"
sequence="20"
/>
<menuitem
action="action_shopfloor_log"
id="menu_action_shopfloor_log"
parent="menu_shopfloor_settings"
groups="base.group_no_one"
sequence="30"
/>
</odoo>
125 changes: 125 additions & 0 deletions shopfloor/views/shopfloor_log_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="shopfloor_log_tree_view" model="ir.ui.view">
<field name="name">shopfloor.log tree</field>
<field name="model">shopfloor.log</field>
<field name="arch" type="xml">
<tree decoration-danger="state == 'failed'">
<field name="create_uid" />
<field name="create_date" />
<field name="request_method" />
<field name="request_url" />
<field name="state" />
</tree>
</field>
</record>
<record id="shopfloor_log_form_view" model="ir.ui.view">
<field name="name">shopfloor.log form</field>
<field name="model">shopfloor.log</field>
<field name="arch" type="xml">
<form>
<header>
<field name="state" widget="statusbar" />
</header>
<sheet>
<group>
<group>
<field name="request_url" />
<field name="request_method" />
</group>
<group>
<field name="create_uid" />
<field name="create_date" />
</group>
</group>
<group string="Parameters" name="parameters">
<group>
<field name="params" widget="CopyClipboardText" />
</group>
<group>
<field name="headers" widget="CopyClipboardText" />
</group>
</group>
<group
string="Result"
name="result"
attrs="{'invisible': [('state', '!=', 'success')]}"
>
<group>
<field
name="result"
nolabel="1"
widget="CopyClipboardText"
/>
</group>
</group>
<group
string="Error"
name="error"
attrs="{'invisible': [('state', '!=', 'failed')]}"
>
<group>
<field
name="error"
nolabel="1"
widget="CopyClipboardText"
/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="shopfloor_log_search_view" model="ir.ui.view">
<field name="name">shopfloor.log search</field>
<field name="model">shopfloor.log</field>
<field name="arch" type="xml">
<search>
<field name="state" />
<field name="request_url" />
<field name="request_method" />
<field name="params" />
<field name="headers" />
<filter
string="Today"
name="today"
date="create_date"
help="Logs generated today"
/>
<group expand="0" string="Group By">
<filter
string="User"
name="by_user"
domain="[]"
context="{'group_by': 'create_uid'}"
/>
<filter
string="Request URL"
name="groupby_request_url"
domain="[]"
context="{'group_by': 'request_url'}"
/>
<filter
string="Status"
name="status"
domain="[]"
context="{'group_by': 'state'}"
/>
<filter
string="Date"
name="groupby_create_date"
domain="[]"
context="{'group_by': 'create_date'}"
groups="base.group_no_one"
/>
</group>
</search>
</field>
</record>
<record id="action_shopfloor_log" model="ir.actions.act_window">
<field name="name">Shopfloor Logs</field>
<field name="res_model">shopfloor.log</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

0 comments on commit f3518a8

Please sign in to comment.