Skip to main content

odoo-logs

Prefiltered, structured data out of Odoo server logs — so you don't grep them by hand, and so an MCP client can consume them. Reads plain and gzipped logs from 9.0 through 19.0.

Installation

uv tool install git+https://github.com/trobz/odoo-logs

Or for development:

git clone https://github.com/trobz/odoo-logs
cd odoo-logs
make install                       # install deps + pre-commit hooks
uv tool install --editable .       # make `odoo-logs` available globally
make check     # lint, format, type-check
make test      # pytest

Other tasks: regenerate the CLI reference with uv run typer odoo_logs.main utils docs --name odoo-logs --output CLI.md.

Commands

See CLI.md — generated from the code, the source of truth — or odoo-logs <command> --help for every command and flag.

CHECKLIST.md tracks the port from emoi logs: every emoi command and option, what it maps to here, and what is deliberately skipped.

Log files are positional, so the shell does the globbing, and global options come before the command:

odoo-logs errors server.log server.log.*.gz
odoo-logs --output-format json crons /var/log/odoo/*.log
odoo-logs --from 2026-08-01 --database prod logins server.log
odoo-logs --period 'last week' calls server.log --check-activity

list shows which files hold which time before anything is parsed. It reads only the first and last entry of each file (a .gz can't be read from the end, so it is inflated once and the result kept in ~/.cache/odoo-logs/periods.json) and takes a directory, or a base server.log for its whole rotation. With a window, it keeps only the files that overlap it — the set a scan of that window has to read:

odoo-logs list /var/log/odoo
odoo-logs --period yesterday list /var/log/odoo
odoo-logs --output-format json --from '2026-09-25 19:50' --to '2026-09-25 20:10' list server.log

--period takes the range in words — today, yesterday, 3 days ago, this week, last month — and is nothing more than --from/--to worked out for you.

Naming no window at all reads the whole of every file given, which is the one default that can't hide a line from a file you named. When that covers a lot, the span you just read is reported on stderr so it is at least visible:

$ odoo-logs calls /var/log/odoo/server.log*
<table>
Read 4636 entries spanning 2026-06-01 to 2026-08-17; -p today or -p 'this week' narrows it.

It is advice, not a filter — nothing is dropped, and a small log stays quiet.

Five behaviours the flag list can't express:

calls reads werkzeug's access line, not a DEBUG handler. Odoo appends query_count query_time remaining_time to it from 12.0 on, at INFO, so request timings are available on a default-configured instance:

$ odoo-logs calls server.log -n 3
endpoint                          count  t_total  t_avg  t_max  q_avg
/tuico_api/static/…/icon.png          2    1.231  0.615  1.097   73.5
res.config.settings.get_views         2    1.063  0.531  0.583  443
/websocket                           49    0.844  0.017  0.026    5.5

Only call_kw / call_button routes carry a real model and method, and those key as model.method. So does an RPC from 19.0 on, which appends the method it ran to the path (/jsonrpc#res.partner.search_count) where older versions only logged /jsonrpc. Every other route keys on its path with record ids collapsed (/web/image/res.partner/3/avatar_128?unique=… → /web/image/res.partner/N/avatar_128) so one endpoint doesn't fragment into one row per record.

-a/--aggregate hour|day|week|month turns a total into a trend. Every aggregating command takes it, and it adds a period column rather than changing the ones already there:

$ odoo-logs calls server.log* -a day -e product.product -n 4
endpoint                        period      count  t_total  t_avg  t_max  q_avg
product.product.get_views       2026-07-01     12    2.350  0.196  0.511   85.8
product.product.web_search_read 2026-07-13     10    2.343  0.234  0.369   54.7
product.product.web_search_read 2026-06-08      5    2.200  0.440  0.559   28.2
product.product.web_search_read 2026-07-01      8    2.150  0.269  0.567   30.8

calls --check-activity drops the endpoint and counts traffic per period instead; usage keeps the period and counts by what the request was for (rpc, login, job, poll, static, report); users does the same for logins, per user.

jobs --stats times queue_job off the same access line. Every job runs through /queue_job/runjob — unchanged from 10.0 to 19.0 — so werkzeug times a job exactly the way it times a request, no DEBUG handler needed:

$ odoo-logs jobs server.log* --stats -a day
endpoint           period      count  t_total  t_avg  t_min  t_max  q_avg
/queue_job/runjob  2026-06-08      5    2.157  0.431  0.048  1.542   67.6

The uuid in that route is unique per run, so there is nothing to aggregate on it — --aggregate buckets by time instead. Without --stats, jobs lists jobrunner lifecycle and per-job events; everything below the jobrunner is logged at DEBUG on every version.

workers --stats is the same shape one level up: a row per pid, with its first and last sighting and — on 15.0 and older, where WorkerCron (N) <db> time:2.386s still exists — how long its runs took.

errors groups by the exception raised, so a log with 20k tracebacks comes back as a handful of rows:

$ odoo-logs errors server.log.1 -n 3
type                 error                                    count  first  last
OSError              [Errno 98] Address already in use: (…)   19785  …      …
RuntimeError         Couldn't bind the websocket. (…)           286  …      …
odoo.service.server  WorkerHTTP (N) timeout after 3600s          18  …      …

A log entry runs from a timestamped line to the next one, so the traceback below an error belongs to it. The pid inside parentheses is normalised to (N) so one recurring failure doesn't split into one group per process. Nothing is filtered by default — the usual noise filter is -x 'raise_exception=False' -x Loading.

Version coverage. Loggers and messages get renamed between versions (base.ir.ir_cron on 10.0/11.0, base.models.ir_cron after), so each command matches every known wording. Patterns are backed by real lines in tests/samples/<version>.log, one file per version from 9.0 through 19.0, and a test asserts every pattern matches one — a wording that goes dead fails rather than quietly returning nothing. The 10.0 and 17.0 cron wordings are the exception: no corpus has one, so they are rendered from that version's own _logger format string.

Two commands depend on a version floor rather than a wording:

  • calls needs 12.0+ for the perf suffix. Older logs still parse — the row comes back with a status and no timing, and is left out of the aggregate.
  • passwords reads res_users' INFO line, added in 14.0; 16.0 added the by half naming who made the change. Before 14.0 it falls back to odoo.api, which does need log_handler = odoo.api:DEBUG.

Not ported from emoi logs: per-call memory deltas (mem: Xk -> Yk (diff: Zk)). That line was dropped from odoo/http.py after 15.0, and it always required log_handler = odoo.http.rpc.response:DEBUG. The timing half of what emoi logs calls reported is covered by calls.

Metadata

Release files for odoo-logs 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for odoo-logs 0.5.0
File Size Uploaded Provenance Actions
odoo_logs-0.5.0.tar.gz 44.1 kB With provenance Details for odoo_logs-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for odoo-logs 0.5.0
File Interpreter ABI Platform Actions
odoo_logs-0.5.0-py3-none-any.whl Python 3 none any Details for odoo_logs-0.5.0-py3-none-any.whl

Total release size: 88.3 kB

Release files / odoo_logs-0.5.0.tar.gz

Download URL odoo_logs-0.5.0.tar.gz
Size 44.1 kB
Tags Source
SHA-256 checksum
How to use checksums
9dc7e535960b5c52a57fdfbb69de7e671e531ecc38f11de77ff3763512dfe851
BLAKE2b-256 checksum
How to use checksums
4b1969074e8a5820e8543d84a0135d7415d73f0aa0ec943008b2589fe71cb974
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / odoo_logs-0.5.0-py3-none-any.whl

Download URL odoo_logs-0.5.0-py3-none-any.whl
Size 44.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e255b69b864c3ffa7b20d9403e1db22d7f7e9100d4fef25c705c9f693d77e63
BLAKE2b-256 checksum
How to use checksums
cd42f49b79ce3f3dc6883b10732d4db3ec96ee029ec4cadd1120616f114a98c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Security for odoo-logs 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for odoo_logs-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page