Skip to content

Commit 0d7f4eb

Browse files
committed
[IMP] fastapi_log: Adapt to log collection
1 parent ddb5c66 commit 0d7f4eb

10 files changed

Lines changed: 96 additions & 154 deletions

File tree

fastapi_log/README.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ Contributors
7272
------------
7373

7474
- Florian Mounier florian.mounier@akretion.com
75+
- `PyTech <https://www.pytech.it>`__:
76+
77+
- Simone Rubino <simone.rubino@pytech.it>
7578

7679
Maintainers
7780
-----------

fastapi_log/fastapi_dispatcher.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# Copyright 2025 Akretion (http://www.akretion.com).
22
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# Copyright 2025 Simone Rubino - PyTech
34
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
5+
46
import logging
57

6-
from odoo import registry, tools
78
from odoo.http import _dispatchers
89

910
from odoo.addons.fastapi.fastapi_dispatcher import (
@@ -24,19 +25,11 @@ def dispatch(self, endpoint, args):
2425
fastapi_endpoint = (
2526
self.request.env["fastapi.endpoint"]
2627
.sudo()
27-
._get_endpoint(environ["PATH_INFO"])
28+
.get_endpoint(environ["PATH_INFO"])
2829
)
2930
if fastapi_endpoint.log_requests:
30-
if tools.config["test_enable"]:
31-
cr = getattr(
32-
self.request.env.registry, "test_log_cr", self.request.env.cr
33-
)
34-
else:
35-
# Create an independent cursor
36-
cr = registry(self.request.env.cr.dbname).cursor()
37-
38-
env = self.request.env(cr=cr, su=True)
3931
request = self.request
32+
env = request.env(su=True)
4033
try:
4134
log = env["api.log"].log_request(request)
4235
except Exception as e:
@@ -50,18 +43,17 @@ def dispatch(self, endpoint, args):
5043
log and log.log_exception(response_exc)
5144
except Exception as e:
5245
_logger.warning("Failed to log exception", exc_info=e)
46+
else:
47+
# Be sure to commit/save the exception's log
48+
env.cr.commit()
49+
5350
raise response_exc
5451
else:
5552
try:
5653
log and log.log_response(response)
5754
except Exception as e:
5855
_logger.warning("Failed to log response", exc_info=e)
59-
finally:
60-
if not tools.config["test_enable"]:
61-
try:
62-
cr.commit() # pylint: disable=E8102
63-
finally:
64-
cr.close()
56+
6557
return response
6658
else:
6759
return super().dispatch(endpoint, args)

fastapi_log/models/api_log.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
# Copyright 2025 Akretion (http://www.akretion.com).
22
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# Copyright 2025 Simone Rubino - PyTech
34
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
45

56
from starlette.exceptions import HTTPException as StarletteHTTPException
67

7-
from odoo import api, fields, models
8+
from odoo import api, models
89

910

1011
class FastapiLog(models.Model):
1112
_inherit = "api.log"
1213

13-
fastapi_endpoint_id = fields.Many2one(
14-
comodel_name="fastapi.endpoint",
15-
string="Endpoint",
16-
ondelete="cascade",
17-
index=True,
18-
)
14+
@api.model
15+
def _selection_collection_ref(self):
16+
collections = super()._selection_collection_ref()
17+
fastapi_endpoint_model = self.env["fastapi.endpoint"]
18+
collections.append(
19+
(fastapi_endpoint_model._name, fastapi_endpoint_model._description)
20+
)
21+
return collections
1922

2023
@api.model
2124
def _get_request_body(self, request):
@@ -40,9 +43,13 @@ def _prepare_log_request(self, request):
4043
endpoint = (
4144
request.env["fastapi.endpoint"]
4245
.sudo()
43-
._get_endpoint(environ["PATH_INFO"])
46+
.get_endpoint(environ["PATH_INFO"])
47+
)
48+
log_request_values["collection_ref"] = "%s,%s" % (
49+
endpoint._name,
50+
endpoint.id,
4451
)
45-
log_request_values["fastapi_endpoint_id"] = endpoint.id
52+
4653
return log_request_values
4754

4855
def _prepare_log_exception(self, exception):
Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,14 @@
11
# Copyright 2025 Akretion (http://www.akretion.com).
22
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# Copyright 2025 Simone Rubino - PyTech
34
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
45

5-
from odoo import api, fields, models
6+
from odoo import models
67

78

89
class FastapiEndpoint(models.Model):
9-
_inherit = "fastapi.endpoint"
10-
11-
log_requests = fields.Boolean(
12-
help="Log requests in database.",
13-
)
14-
15-
fastapi_log_ids = fields.One2many(
16-
comodel_name="api.log",
17-
inverse_name="fastapi_endpoint_id",
18-
string="Logs",
19-
)
20-
21-
fastapi_log_count = fields.Integer(
22-
compute="_compute_fastapi_log_count", string="Logs Count", store=True
23-
)
24-
25-
@api.depends("fastapi_log_ids")
26-
def _compute_fastapi_log_count(self):
27-
groups = self.env["api.log"].read_group(
28-
[("fastapi_endpoint_id", "in", self.ids)],
29-
["fastapi_endpoint_id"],
30-
["fastapi_endpoint_id"],
31-
)
32-
mapped_data = {
33-
g["fastapi_endpoint_id"][0]: g["fastapi_endpoint_id_count"] for g in groups
34-
}
35-
for endpoint in self:
36-
endpoint.fastapi_log_count = mapped_data.get(endpoint.id, 0)
10+
_name = "fastapi.endpoint"
11+
_inherit = [
12+
"api.log_collection",
13+
"fastapi.endpoint",
14+
]

fastapi_log/readme/CONTRIBUTORS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
- Florian Mounier <florian.mounier@akretion.com>
2+
- [PyTech](https://www.pytech.it):
3+
- Simone Rubino \<<simone.rubino@pytech.it>\>

fastapi_log/static/description/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ <h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
416416
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
417417
<ul class="simple">
418418
<li>Florian Mounier <a class="reference external" href="mailto:florian.mounier&#64;akretion.com">florian.mounier&#64;akretion.com</a></li>
419+
<li><a class="reference external" href="https://www.pytech.it">PyTech</a>:<ul>
420+
<li>Simone Rubino &lt;<a class="reference external" href="mailto:simone.rubino&#64;pytech.it">simone.rubino&#64;pytech.it</a>&gt;</li>
421+
</ul>
422+
</li>
419423
</ul>
420424
</div>
421425
<div class="section" id="maintainers">

fastapi_log/tests/common.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright 2025 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# Copyright 2025 Simone Rubino - PyTech
4+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
5+
6+
from contextlib import contextmanager
7+
8+
from odoo.tests.common import RecordCapturer
9+
10+
from odoo.addons.api_log.tests.common import Common as CommonAPILog
11+
12+
13+
class Common(CommonAPILog):
14+
@classmethod
15+
def setUpClass(cls):
16+
super().setUpClass()
17+
cls.fastapi_demo_app = cls.env.ref("fastapi.fastapi_endpoint_demo")
18+
cls.fastapi_demo_app.root_path += "/test"
19+
cls.fastapi_demo_app._handle_registry_sync()
20+
cls.fastapi_demo_app.write({"log_requests": True})
21+
lang = (
22+
cls.env["res.lang"]
23+
.with_context(active_test=False)
24+
.search([("code", "=", "fr_BE")])
25+
)
26+
lang.active = True
27+
28+
@contextmanager
29+
def log_capturer(self):
30+
app = self.fastapi_demo_app
31+
with RecordCapturer(
32+
self.env[self.log_model._name],
33+
[("collection_ref", "=", "%s,%s" % (app._name, app.id))],
34+
) as capturer:
35+
yield capturer

fastapi_log/tests/test_fastapi_log.py

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,19 @@
11
# Copyright 2025 Akretion (http://www.akretion.com).
22
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# Copyright 2025 Simone Rubino - PyTech
34
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
5+
46
import os
5-
import threading
67
import unittest
7-
from contextlib import contextmanager
8-
9-
from odoo.sql_db import TestCursor
10-
from odoo.tests.common import RecordCapturer
118

12-
from odoo.addons.api_log.tests.common import CommonAPILog
139
from odoo.addons.fastapi.schemas import DemoExceptionType
10+
from odoo.addons.fastapi_log.tests.common import Common
1411

1512
from fastapi import status
1613

1714

18-
@unittest.skipIf(os.getenv("SKIP_HTTP_CASE"), "FastAPIEncryptedErrorsCase skipped")
19-
class FastAPIEncryptedErrorsCase(CommonAPILog):
20-
@classmethod
21-
def setUpClass(cls):
22-
super().setUpClass()
23-
cls.fastapi_demo_app = cls.env.ref("fastapi.fastapi_endpoint_demo")
24-
cls.fastapi_demo_app.root_path += "/test"
25-
cls.fastapi_demo_app._handle_registry_sync()
26-
cls.fastapi_demo_app.write({"log_requests": True})
27-
lang = (
28-
cls.env["res.lang"]
29-
.with_context(active_test=False)
30-
.search([("code", "=", "fr_BE")])
31-
)
32-
lang.active = True
33-
34-
def setUp(self):
35-
super().setUp()
36-
# Use a side test cursor to be able to get exception logs
37-
reg = self.env.registry
38-
reg.test_log_lock = threading.RLock()
39-
reg.test_log_cr = TestCursor(reg._db.cursor(), reg.test_log_lock)
40-
41-
def tearDown(self):
42-
reg = self.env.registry
43-
reg.test_log_cr.rollback()
44-
reg.test_log_cr.close()
45-
reg.test_log_cr = None
46-
reg.test_log_lock = None
47-
super().tearDown()
48-
49-
@contextmanager
50-
def log_capturer(self):
51-
with RecordCapturer(
52-
self.env(cr=self.env.registry.test_log_cr)[self.log_model._name],
53-
[("fastapi_endpoint_id", "=", self.fastapi_demo_app.id)],
54-
) as capturer:
55-
yield capturer
56-
15+
@unittest.skipIf(os.getenv("SKIP_HTTP_CASE"), "TestFastapiLog skipped")
16+
class TestFastapiLog(Common):
5717
def test_no_log_if_disabled(self):
5818
self.fastapi_demo_app.write({"log_requests": False})
5919

fastapi_log/views/fastapi_endpoint_views.xml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,32 @@
22
<!--
33
Copyright 2025 Akretion (http://www.akretion.com).
44
@author Florian Mounier <florian.mounier@akretion.com>
5+
Copyright 2025 Simone Rubino - PyTech
56
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
67
-->
78
<odoo>
89

9-
<record id="fastapi_log_action_from_endpoint" model="ir.actions.act_window">
10-
<field name="name">Fastapi Log</field>
11-
<field name="res_model">api.log</field>
12-
<field name="view_mode">tree,form</field>
13-
<field name="domain">[('fastapi_endpoint_id', '=', active_id)]</field>
14-
<field name="context">{'default_fastapi_endpoint_id': active_id}</field>
15-
</record>
16-
17-
1810
<record id="fastapi_endpoint_form_view" model="ir.ui.view">
1911
<field name="model">fastapi.endpoint</field>
2012
<field name="inherit_id" ref="fastapi.fastapi_endpoint_form_view" />
2113
<field name="arch" type="xml">
2214

2315
<div class="oe_button_box" name="button_box" position="inside">
16+
<field name="log_ids" invisible="True" />
2417
<button
25-
type="action"
26-
name="%(fastapi_log.fastapi_log_action_from_endpoint)s"
18+
type="object"
19+
name="action_logs"
2720
icon="fa-book"
21+
attrs="{
22+
'invisible': [
23+
('log_ids', '=', []),
24+
],
25+
}"
2826
groups="api_log.group_api_log"
29-
attrs="{'invisible': [('fastapi_log_count', '=', 0)]}"
3027
>
31-
<field string="Logs" name="fastapi_log_count" widget="statinfo" />
28+
<div class="o_stat_info">
29+
<span class="o_stat_text">Logs</span>
30+
</div>
3231
</button>
3332
</div>
3433

fastapi_log/views/fastapi_log_views.xml

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,16 @@
22
<!--
33
Copyright 2025 Akretion (http://www.akretion.com).
44
@author Florian Mounier <florian.mounier@akretion.com>
5+
Copyright 2025 Simone Rubino - PyTech
56
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
67
-->
78
<odoo>
8-
<record id="api_log_view_form" model="ir.ui.view">
9-
<field name="name">Add Fastapi fields to API log form view</field>
10-
<field name="model">api.log</field>
11-
<field name="inherit_id" ref="api_log.api_log_view_form" />
12-
<field name="arch" type="xml">
13-
<group name="misc" position="inside">
14-
<field name="fastapi_endpoint_id" />
15-
</group>
16-
</field>
17-
</record>
18-
19-
<record id="api_log_view_tree" model="ir.ui.view">
20-
<field name="name">Add Fastapi fields to API log tree view</field>
21-
<field name="model">api.log</field>
22-
<field name="inherit_id" ref="api_log.api_log_view_tree" />
23-
<field name="arch" type="xml">
24-
<tree position="inside">
25-
<field name="fastapi_endpoint_id" optional="hide" />
26-
</tree>
27-
</field>
28-
</record>
29-
30-
<record id="api_log_view_search" model="ir.ui.view">
31-
<field name="name">Add Fastapi fields to API log search view</field>
32-
<field name="model">api.log</field>
33-
<field name="inherit_id" ref="api_log.api_log_view_search" />
34-
<field name="arch" type="xml">
35-
<search position="inside">
36-
<field name="fastapi_endpoint_id" />
37-
</search>
38-
<group name="groupby" position="inside">
39-
<filter
40-
name="fastapi_endpoint_groupby"
41-
string="Fastapi Endpoint"
42-
context="{'group_by': 'fastapi_endpoint_id'}"
43-
/>
44-
</group>
45-
</field>
46-
</record>
47-
489
<record id="fastapi_log_action" model="ir.actions.act_window">
4910
<field name="name">Fastapi Logs</field>
5011
<field name="res_model">api.log</field>
5112
<field name="domain">[
52-
("fastapi_endpoint_id", "!=", False)
13+
("collection_model", "=", "fastapi.endpoint"),
14+
("collection_id", "=", active_id),
5315
]</field>
5416
<field name="view_mode">tree,form</field>
5517
</record>

0 commit comments

Comments
 (0)