Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions endpoint_log/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

============
Endpoint Log
============

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:b802c5eba07d74e17c9a0dd597cadeafc97886d35636eff3a7813a8d5903a558
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb--api-lightgray.png?logo=github
:target: https://github.com/OCA/web-api/tree/19.0/endpoint_log
:alt: OCA/web-api
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-api-19-0/web-api-19-0-endpoint_log
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/web-api&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module logs the requests and responses of endpoints
(``endpoint.endpoint``) in the database, using the generic ``api.log``
model of ``api_log``.

Each log stores the request URL, method, headers, body and date, the
response status, headers and body, the elapsed time and, when the call
failed, the stack trace. Logs are written with a separate database
connection, so the logs of failed calls are kept even though the call's
own transaction is rolled back.

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

Usage
=====

To log the calls of an endpoint, go to *Settings > Technical >
Endpoints*, open the endpoint and check *Log Requests* in the *Request*
group. The option is visible to members of the *API Log Access* group.

The *Logs* smart button on the endpoint shows its logs. The logs of all
endpoints are listed in *Settings > Technical > Endpoint Logs*.

Each logged response carries an ``API-Log-Entry-ID`` header with the ID
of its log, which helps to find the log of a given call.

Known issues / Roadmap
======================

- Requests rejected before the endpoint is reached, such as an
authentication failure or a request method the route does not accept,
are not logged.
- The body of a form-encoded request is not logged, because Odoo parses
it into the request parameters before the endpoint is reached.
- Logs are never deleted automatically.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/web-api/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/web-api/issues/new?body=module:%20endpoint_log%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Quartile

Contributors
------------

- `Quartile <https://www.quartile.co>`__:

- Aung Ko Ko Lin

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-AungKoKoLin1997| image:: https://github.com/AungKoKoLin1997.png?size=40px
:target: https://github.com/AungKoKoLin1997
:alt: AungKoKoLin1997

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-AungKoKoLin1997|

This module is part of the `OCA/web-api <https://github.com/OCA/web-api/tree/19.0/endpoint_log>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions endpoint_log/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
19 changes: 19 additions & 0 deletions endpoint_log/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Endpoint Log",
"summary": "Log endpoint requests in database",
"version": "19.0.1.0.0",
"license": "AGPL-3",
"development_status": "Alpha",
"author": "Quartile, Odoo Community Association (OCA)",
"maintainers": ["AungKoKoLin1997"],
"website": "https://github.com/OCA/web-api",
"category": "Tools",
"depends": ["api_log", "endpoint"],
"data": [
"views/endpoint_endpoint_views.xml",
"views/endpoint_log_views.xml",
],
}
3 changes: 3 additions & 0 deletions endpoint_log/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import api_log
from . import endpoint_endpoint
from . import ir_http
36 changes: 36 additions & 0 deletions endpoint_log/models/api_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from traceback import format_exception

from werkzeug.exceptions import HTTPException

from odoo import api, models


class APILog(models.Model):
_inherit = "api.log"

@api.model
def _selection_collection_ref(self):
collections = super()._selection_collection_ref()
endpoint_model = self.env["endpoint.endpoint"]
collections.append((endpoint_model._name, endpoint_model._description))
return collections

def _prepare_log_response(self, response):
# For a plain Odoo route, ``ir.http._handle_error`` (called by
# ``log_exception``) returns the HTTP exception itself, not a response.
if isinstance(response, HTTPException):
response = response.get_response()
return super()._prepare_log_response(response)

def _prepare_log_exception(self, exception):
if not isinstance(exception, HTTPException):
return super()._prepare_log_exception(exception)
# Log the response the exception renders: an exception may override
# ``get_response()`` (e.g. endpoint's ``RequestValidationError``), which
# ``code``, ``get_headers()`` and ``get_body()`` do not reflect.
values = self._prepare_log_response(exception.get_response())
values["stack_trace"] = "".join(format_exception(exception))
return values
12 changes: 12 additions & 0 deletions endpoint_log/models/endpoint_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class EndpointEndpoint(models.Model):
_name = "endpoint.endpoint"
_inherit = [
"api.log_collection.mixin",
"endpoint.endpoint",
]
89 changes: 89 additions & 0 deletions endpoint_log/models/ir_http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import functools
import logging
from contextlib import contextmanager

from odoo import models
from odoo.http import request
from odoo.modules.registry import Registry

from odoo.addons.endpoint.controllers.main import EndpointControllerMixin

_logger = logging.getLogger(__name__)


class IrHttp(models.AbstractModel):
_inherit = "ir.http"

@classmethod
def _get_endpoint_to_log(cls, endpoint):
"""Return the endpoint record served by ``endpoint`` if it logs requests.

Routes of ``endpoint.mixin`` records are registered by
``endpoint_route_handler`` as a ``functools.partial`` of the controller
method, bound to the model name and the route.
"""
if not isinstance(endpoint, functools.partial):
return None
controller = getattr(endpoint.func, "__self__", None)
if not isinstance(controller, EndpointControllerMixin):
return None
if len(endpoint.args) < 2:
return None
model_name, route = endpoint.args[:2]
model = request.env.get(model_name)
if model is None or "log_requests" not in model._fields:
return None
record = model._find_endpoint(route)
return record if record.log_requests else None

@classmethod
@contextmanager
def _create_endpoint_log_env(cls, request_env):
request_registry = request_env.registry
# `test_log_cr` is set on the registry by the tests (see tests/common.py)
# to capture the logs with a dedicated cursor.
test_log_cr = getattr(request_registry, "test_log_cr", None)
if test_log_cr is not None:
cr = test_log_cr
else:
# Use an independent cursor so that the logs are committed even when
# the request's transaction is rolled back because of an exception.
cr = Registry(request_registry.db_name).cursor()
try:
yield request_env(cr=cr, su=True)
finally:
if test_log_cr is None:
try:
cr.commit() # pylint: disable=invalid-commit
finally:
cr.close()

@classmethod
def _dispatch(cls, endpoint):
record = cls._get_endpoint_to_log(endpoint)
if not record:
return super()._dispatch(endpoint)
with cls._create_endpoint_log_env(request.env) as log_env:
try:
log = log_env["api.log"].log_request(
request, {"collection_ref": f"{record._name},{record.id}"}
)
except Exception as e:
_logger.warning("Failed to log request", exc_info=e)
log = None
try:
response = super()._dispatch(endpoint)
except Exception as response_exc:
try:
log and log.log_exception(response_exc)
except Exception as e:
_logger.warning("Failed to log exception", exc_info=e)
raise
try:
log and log.log_response(response)
except Exception as e:
_logger.warning("Failed to log response", exc_info=e)
return response
3 changes: 3 additions & 0 deletions endpoint_log/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions endpoint_log/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [Quartile](https://www.quartile.co):
- Aung Ko Ko Lin
7 changes: 7 additions & 0 deletions endpoint_log/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This module logs the requests and responses of endpoints (`endpoint.endpoint`) in
the database, using the generic `api.log` model of `api_log`.

Each log stores the request URL, method, headers, body and date, the response status,
headers and body, the elapsed time and, when the call failed, the stack trace. Logs are
written with a separate database connection, so the logs of failed calls are kept even
though the call's own transaction is rolled back.
5 changes: 5 additions & 0 deletions endpoint_log/readme/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- Requests rejected before the endpoint is reached, such as an authentication failure
or a request method the route does not accept, are not logged.
- The body of a form-encoded request is not logged, because Odoo parses it into the
request parameters before the endpoint is reached.
- Logs are never deleted automatically.
9 changes: 9 additions & 0 deletions endpoint_log/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
To log the calls of an endpoint, go to *Settings > Technical > Endpoints*, open the
endpoint and check *Log Requests* in the *Request* group. The option is visible to
members of the *API Log Access* group.

The *Logs* smart button on the endpoint shows its logs. The logs of all endpoints are
listed in *Settings > Technical > Endpoint Logs*.

Each logged response carries an `API-Log-Entry-ID` header with the ID of its log, which
helps to find the log of a given call.
Binary file added endpoint_log/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading