Skip to content

[16.0][ADD] fastapi_log#554

Merged
OCA-git-bot merged 15 commits intoOCA:16.0from
PyTech-SRL:16.0-add-fastapi_log
Mar 26, 2026
Merged

[16.0][ADD] fastapi_log#554
OCA-git-bot merged 15 commits intoOCA:16.0from
PyTech-SRL:16.0-add-fastapi_log

Conversation

@SirPyTech
Copy link
Copy Markdown

In the past months we have been proposing many improvements to #501 with akretion#8.

From more than a month ago, the branch is outdated (akretion#8 (comment)) and it is causing conflicts in akretion#8.

Here we are proposing all those improvements to OCA directly.

@SirPyTech SirPyTech marked this pull request as ready for review August 4, 2025 10:13
@SirPyTech
Copy link
Copy Markdown
Author

@simahawk here I addressed your latest comments akretion#8 (comment) and akretion#8 (comment), please have a look

Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py Outdated
try:
headers_dict = {key: value for key, value in headers.items()}
return self._sanitize_headers_dict(headers_dict)
except AttributeError:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when do you expect to have this kind of error?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm suspicious about the headers argument, if it does not have the items method then an AttributeError would be raised.
But you're right, let's be optimistic and remove the exception handling 😄

Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py Outdated
def _prepare_log_response(self, response):
return {
"response_status_code": response.status_code,
"response_headers": self._headers_to_dict(response.headers),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the things done in rest_log was to inject a reference to the log entry in the response.
There we assumed the response was always JSON data and added a key.
Here we could inject a response header (eg: API_LOG_ENTRY_URL)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the identifier (api.log.id) of the log record, that is enough to find the log record to whoever has access to them.

Please keep in mind that I agree with the author of #501 that wrote:

This is not a migration of rest_log, this module aims to be simpler, at least in the first iterations.

A more elaborated feature (like building the full URL of the log record) can be added later, after the first simple module is merged.

Comment thread api_log/models/api_log_collection.py Outdated
def _compute_log_ids(self):
for collection in self:
collection.log_ids = self.env["api.log"].search(
[("collection_ref", "=", "%s,%s" % (collection._name, collection.id))]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see my comment above on how to search for logs

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment thread api_log_mail/models/api_log.py Outdated
from odoo import api, models


class FastapiLog(models.Model):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class FastapiLog(models.Model):
class ApiLog(models.Model):

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Thanks

@SirPyTech SirPyTech force-pushed the 16.0-add-fastapi_log branch from 7674bd7 to d1f3eec Compare August 21, 2025 10:21
@SirPyTech
Copy link
Copy Markdown
Author

The error in the tests (https://github.com/OCA/rest-framework/actions/runs/17124124402/job/48571725570?pr=554#step:8:120) does not seem to be related to these changes

Stack
2025-08-21 10:24:04,335 340 INFO odoo odoo.modules.loading: Loading module graphql_base (12/53) 
2025-08-21 10:24:04,475 340 CRITICAL odoo odoo.modules.module: Couldn't load module graphql_base 
2025-08-21 10:24:04,475 340 CRITICAL odoo odoo.modules.module: cannot import name 'HttpQueryError' from 'graphql_server' (/opt/odoo-venv/lib/python3.10/site-packages/graphql_server/__init__.py) 
2025-08-21 10:24:04,477 340 WARNING odoo odoo.modules.loading: Transient module states were reset 
2025-08-21 10:24:04,477 340 ERROR odoo odoo.modules.registry: Failed to load registry 
Traceback (most recent call last):
  File "/opt/odoo/odoo/modules/registry.py", line 87, in new
    odoo.modules.load_modules(registry, force_demo, status, update_module)
  File "/opt/odoo/odoo/modules/loading.py", line 493, in load_modules
    processed_modules += load_marked_modules(cr, graph,
  File "/opt/odoo/odoo/modules/loading.py", line 374, in load_marked_modules
    loaded, processed = load_module_graph(
  File "/opt/odoo/odoo/modules/loading.py", line 190, in load_module_graph
    load_openerp_module(package.name)
  File "/opt/odoo/odoo/modules/module.py", line 471, in load_openerp_module
    __import__('odoo.addons.' + module_name)
  File "/__w/rest-framework/rest-framework/graphql_base/__init__.py", line 4, in 
    from .controllers import GraphQLControllerMixin
  File "/__w/rest-framework/rest-framework/graphql_base/controllers/__init__.py", line 1, in 
    from .main import GraphQLControllerMixin
  File "/__w/rest-framework/rest-framework/graphql_base/controllers/main.py", line 6, in 
    from graphql_server import (
ImportError: cannot import name 'HttpQueryError' from 'graphql_server' (/opt/odoo-venv/lib/python3.10/site-packages/graphql_server/__init__.py)
2025-08-21 10:24:04,478 340 CRITICAL odoo odoo.service.server: Failed to initialize database `odoo`. 
Traceback (most recent call last):
  File "/opt/odoo/odoo/service/server.py", line 1333, in preload_registries
    registry = Registry.new(dbname, update_module=update_module)
  File "", line 2, in new
  File "/opt/odoo/odoo/tools/func.py", line 87, in locked
    return func(inst, *args, **kwargs)
  File "/opt/odoo/odoo/modules/registry.py", line 87, in new
    odoo.modules.load_modules(registry, force_demo, status, update_module)
  File "/opt/odoo/odoo/modules/loading.py", line 493, in load_modules
    processed_modules += load_marked_modules(cr, graph,
  File "/opt/odoo/odoo/modules/loading.py", line 374, in load_marked_modules
    loaded, processed = load_module_graph(
  File "/opt/odoo/odoo/modules/loading.py", line 190, in load_module_graph
    load_openerp_module(package.name)
  File "/opt/odoo/odoo/modules/module.py", line 471, in load_openerp_module
    __import__('odoo.addons.' + module_name)
  File "/__w/rest-framework/rest-framework/graphql_base/__init__.py", line 4, in 
    from .controllers import GraphQLControllerMixin
  File "/__w/rest-framework/rest-framework/graphql_base/controllers/__init__.py", line 1, in 
    from .main import GraphQLControllerMixin
  File "/__w/rest-framework/rest-framework/graphql_base/controllers/main.py", line 6, in 
    from graphql_server import (
ImportError: cannot import name 'HttpQueryError' from 'graphql_server' (/opt/odoo-venv/lib/python3.10/site-packages/graphql_server/__init__.py)

Copy link
Copy Markdown
Author

@SirPyTech SirPyTech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review!
Please check the latest changes

Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py Outdated
try:
headers_dict = {key: value for key, value in headers.items()}
return self._sanitize_headers_dict(headers_dict)
except AttributeError:
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm suspicious about the headers argument, if it does not have the items method then an AttributeError would be raised.
But you're right, let's be optimistic and remove the exception handling 😄

Comment thread api_log/models/api_log.py
Comment thread api_log/models/api_log.py Outdated
def _prepare_log_response(self, response):
return {
"response_status_code": response.status_code,
"response_headers": self._headers_to_dict(response.headers),
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the identifier (api.log.id) of the log record, that is enough to find the log record to whoever has access to them.

Please keep in mind that I agree with the author of #501 that wrote:

This is not a migration of rest_log, this module aims to be simpler, at least in the first iterations.

A more elaborated feature (like building the full URL of the log record) can be added later, after the first simple module is merged.

Comment thread api_log/models/api_log_collection.py Outdated
def _compute_log_ids(self):
for collection in self:
collection.log_ids = self.env["api.log"].search(
[("collection_ref", "=", "%s,%s" % (collection._name, collection.id))]
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment thread api_log_mail/models/api_log.py Outdated
from odoo import api, models


class FastapiLog(models.Model):
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Thanks

@SirPyTech SirPyTech requested a review from simahawk August 21, 2025 10:28
@sebastienbeau sebastienbeau added this to the 16.0 milestone Sep 1, 2025
Copy link
Copy Markdown
Contributor

@paradoxxxzero paradoxxxzero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work thanks. Nothing blocking except the exception commit problem. Once it’s fixed I’ll close my old PR.

Comment thread api_log/models/api_log.py Outdated

def _prepare_log_response(self, response):
headers_dict = self._headers_to_dict(response.headers)
headers_dict["API_LOG_ENTRY_ID"] = self.id
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not add the id to the headers of the response, only in the logs. Set it in the response instead.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, thanks for catching that!
I have added a quick assert in the tests too

<field name="response_status_code" />
<field name="response_headers_preview" />
<field
name="response_preview"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no way to access the full response in the view anymore since the removal of the b64 field.
I think it should be kept one way or another.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Answered to the same query in #554 (comment).

Comment thread fastapi_log/fastapi_dispatcher.py Outdated
_logger.warning("Failed to log exception", exc_info=e)
else:
# Be sure to commit/save the exception's log
env.cr.commit()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very wrong. You are committing a transaction of a failed request.

Consider this simple case :

@router.post("/create_partner")
def create_partner(env: Annotated[api.Environment, Depends(optionally_authenticated_partner_env)]):
    partner = env["res.partner"].create({...})
    if is_something_wrong_with_new_partner(partner):
        raise UserError("Something is wrong with new partner")
    return "Ok"

If logging is enabled the wrong partner will be stored in database.

Please restore the separate cursor (https://github.com/OCA/rest-framework/pull/501/files#diff-67e6f9ad0c4d04c8de3dcd507c53fa5bbebf76b169c660d976f36c84c236ea68R38) or find another way to commit the exception.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified this and you are correct.
I added the separate cursor in a commit with you as a co-author to fix this, could you please check?

@SirPyTech SirPyTech force-pushed the 16.0-add-fastapi_log branch from d1f3eec to 4e1d2b8 Compare September 9, 2025 11:01
Copy link
Copy Markdown
Author

@SirPyTech SirPyTech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @paradoxxxzero, please check the latest changes

Comment thread api_log/models/api_log.py Outdated

def _prepare_log_response(self, response):
headers_dict = self._headers_to_dict(response.headers)
headers_dict["API_LOG_ENTRY_ID"] = self.id
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, thanks for catching that!
I have added a quick assert in the tests too

Comment thread fastapi_log/fastapi_dispatcher.py Outdated
_logger.warning("Failed to log exception", exc_info=e)
else:
# Be sure to commit/save the exception's log
env.cr.commit()
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified this and you are correct.
I added the separate cursor in a commit with you as a co-author to fix this, could you please check?

Comment thread api_log/models/api_log.py
<field name="response_status_code" />
<field name="response_headers_preview" />
<field
name="response_preview"
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Answered to the same query in #554 (comment).

@SirPyTech SirPyTech force-pushed the 16.0-add-fastapi_log branch from 4e1d2b8 to d7bf717 Compare September 9, 2025 11:05
Copy link
Copy Markdown
Contributor

@paradoxxxzero paradoxxxzero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for missing header in exception, LGTM

Comment thread api_log/models/api_log.py Outdated
return self.sudo().create(log_request_values)

def _prepare_log_response(self, response):
response.headers["API_LOG_ENTRY_ID"] = self.id
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it could be better to use kebab-case here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, it is the common convention for headers keys but I didn't know that, thanks!

Comment thread api_log/models/api_log.py
return self.sudo().write(log_response_values)

def _prepare_log_exception(self, exception):
values = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API_LOG_ENTRY_ID header is not set in case of exception

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I added it, please check.

Note that it won't be returned in the response to the client but only stored in the log record, because in case of an exception the dispatcher simply propagates the exception raised by the endpoint.

Comment thread api_log/models/api_log.py Outdated
Comment on lines +175 to +177
values = {
"stack_trace": "".join(format_exception(exception)),
"response_headers": self._inject_log_entry(dict()),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is especially important to return the header in case of exception, luckily we can do this by setting exception headers attribute, see: https://github.com/OCA/rest-framework/blob/18.0/fastapi/fastapi_dispatcher.py#L46

Suggested change
values = {
"stack_trace": "".join(format_exception(exception)),
"response_headers": self._inject_log_entry(dict()),
exception.headers = getattr(exception, "headers", {})
values = {
"stack_trace": "".join(format_exception(exception)),
"response_headers": self._inject_log_entry(exception.headers),
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that's why I mentioned it specifically, but I didn't know a way to inject it; the solution you proposed is awesome and works great!
I included it, please check the new changes.

Copy link
Copy Markdown
Contributor

@paradoxxxzero paradoxxxzero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Also please rename this PR to something like: [16.0][ADD] fastapi_log

@SirPyTech SirPyTech changed the title Log FastAPI requests [16.0][ADD] fastapi_log Oct 20, 2025
@SirPyTech SirPyTech force-pushed the 16.0-add-fastapi_log branch from 67660fd to 0fae517 Compare November 26, 2025 08:54
@SirPyTech
Copy link
Copy Markdown
Author

Rebased to check if this is affected by fastapi/fastapi@51ad909.
It still looks good 😎

@paradoxxxzero
Copy link
Copy Markdown
Contributor

@simahawk @lmignon Can we move forward on this PR? I would like to migrate these modules to 18.0

@lmignon
Copy link
Copy Markdown
Contributor

lmignon commented Jan 13, 2026

I don't use this addon. @simahawk as you made a review, is-it ok for you?

@claudio-mano
Copy link
Copy Markdown

Hi, I'm currently trying this module, and I found a bug I'd want to report.

Describe the bug

  1. Endpoint raises an exception which has unwritable headers property (in my case oauthlib.oauth2.rfc6749.errors.MismatchingRedirectURIError)
  2. fastapi_log tries to log exception
    api_log._prepare_log_exception currently assigns exception.headers = getattr(exception, "headers", {})
  3. Headers is a read-only property, so this triggers:
    AttributeError: property headers of MismatchingRedirectURIError object has no setter

This is my traceback
oauthlib.oauth2.rfc6749.errors.MismatchingRedirectURIError: (invalid_request) Mismatching redirect URI. <oauthlib.Request SANITIZED>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/claudio/venv-enterprise/addons/extra/fastapi_log/fastapi_dispatcher.py", line 66, in dispatch
log and log.log_exception(response_exc)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/claudio/venv-enterprise/addons/extra/api_log/models/api_log.py", line 202, in log_exception
log_exception_values = self._prepare_log_exception(exception)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/claudio/venv-enterprise/addons/extra/fastapi_log/models/api_log.py", line 56, in _prepare_log_exception
values = super()._prepare_log_exception(exception)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/claudio/venv-enterprise/addons/extra/api_log/models/api_log.py", line 175, in _prepare_log_exception
exception.headers = getattr(exception, "headers", {})
^^^^^^^^^^^^^^^^^
AttributeError: property 'headers' of 'MismatchingRedirectURIError' object has no setter

The line of the error is this one
https://github.com/PyTech-SRL/rest-framework/blob/0fae517e7f93f2b2c65505bd9b91f2bc43240a25/api_log/models/api_log.py#L175

@SirPyTech
Copy link
Copy Markdown
Author

SirPyTech commented Mar 19, 2026

@claudio-mano thanks for the detailed report in #554 (comment), it helped a lot! I added a commit to fix your use-case, could you please check if it works as expected now?

Hi @SirPyTech, thank you very much for the quick response, I confirm that now everything works fine

Thanks for the feedback, would you mind dropping an approval in this PR? This would speed up the merge process.
If you don't know how to do that, look at the last step of https://odoo-community.org/resources/review:

Once done, do not forget to leave a review on GitHub (accessible in the "Review changes" green button on the third tab - Files changed -) with:

  • Approve if everything worked as expected

  • Comment or Request changes with the description of the mismatch otherwise.

Anyone having a GitHub account can post a review.

Copy link
Copy Markdown

@claudio-mano claudio-mano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested my scenario described here: #554 (comment)

@SirPyTech

This comment was marked as resolved.

@SirPyTech SirPyTech force-pushed the 16.0-add-fastapi_log branch from 3217ffc to 281e0ee Compare March 25, 2026 08:15
@SirPyTech SirPyTech force-pushed the 16.0-add-fastapi_log branch from 281e0ee to 3fd6a4e Compare March 26, 2026 10:14
@lmignon
Copy link
Copy Markdown
Contributor

lmignon commented Mar 26, 2026

@SirPyTech @paradoxxxzero Is this PR ready to merge?

@SirPyTech
Copy link
Copy Markdown
Author

@SirPyTech @paradoxxxzero Is this PR ready to merge?

Thanks @lmignon for taking care of this PR!
Yes I fixed a small regression caught by the unit tests, caused by the latest changes.

I am probably biased because I'm the author of several commits, but in my opinion this could be merged 🚀

@lmignon
Copy link
Copy Markdown
Contributor

lmignon commented Mar 26, 2026

/ocabot merge nobump

@OCA-git-bot
Copy link
Copy Markdown
Contributor

What a great day to merge this nice PR. Let's do it!
Prepared branch 16.0-ocabot-merge-pr-554-by-lmignon-bump-nobump, awaiting test results.

@OCA-git-bot OCA-git-bot merged commit 3dfc28f into OCA:16.0 Mar 26, 2026
7 checks passed
@OCA-git-bot
Copy link
Copy Markdown
Contributor

Congratulations, your PR was merged at 5de4d3a. Thanks a lot for contributing to OCA. ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants