diff --git a/pyproject.toml b/pyproject.toml index a31b9731d..94b309065 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ dependencies = [ "ocrmypdf~=16.10.0", "pathvalidate~=3.3.1", "pdf2image~=1.17.0", + "psutil>=7", "psycopg-pool", "python-dateutil~=2.9.0", "python-dotenv~=1.1.0", diff --git a/src/paperless/middleware.py b/src/paperless/middleware.py index cc54e892d..70f26ce72 100644 --- a/src/paperless/middleware.py +++ b/src/paperless/middleware.py @@ -1,7 +1,13 @@ +import logging +import resource +import time + from django.conf import settings from paperless import version +logger = logging.getLogger("middleware") + class ApiVersionMiddleware: def __init__(self, get_response): @@ -15,3 +21,55 @@ class ApiVersionMiddleware: response["X-Version"] = version.__full_version_str__ return response + + +try: + import psutil + + _PSUTIL = True +except Exception: + _PSUTIL = False + + +class MemLogMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # capture baseline + ru_before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + if _PSUTIL: + p = psutil.Process() + rss_before = p.memory_info().rss + else: + rss_before = 0 + + t0 = time.perf_counter() + try: + return self.get_response(request) + finally: + dur_ms = (time.perf_counter() - t0) * 1000.0 + + ru_after = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + # ru_maxrss is KB on Linux; convert to MB + peak_mb = (ru_after) / 1024.0 + peak_delta_mb = (ru_after - ru_before) / 1024.0 + + if _PSUTIL: + rss_after = p.memory_info().rss + delta_mb = (rss_after - rss_before) / (1024 * 1024) + rss_mb = rss_after / (1024 * 1024) + else: + delta_mb = 0.0 + rss_mb = 0.0 + + logger.debug( + "mem rss=%.1fMB Δend=%.1fMB peak=%.1fMB Δpeak=%.1fMB dur=%.1fms %s %s", + rss_mb, + delta_mb, + peak_mb, + peak_delta_mb, + dur_ms, + request.method, + request.path, + ) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 24d297d06..5b015e19d 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -363,6 +363,7 @@ if DEBUG: ) MIDDLEWARE = [ + "paperless.middleware.MemLogMiddleware", "django.middleware.security.SecurityMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -878,6 +879,7 @@ LOGGING = { "kombu": {"handlers": ["file_celery"], "level": "DEBUG"}, "_granian": {"handlers": ["file_paperless"], "level": "DEBUG"}, "granian.access": {"handlers": ["file_paperless"], "level": "DEBUG"}, + "middleware": {"handlers": ["console"], "level": "DEBUG"}, }, } diff --git a/uv.lock b/uv.lock index 3357a9c21..f2caf4e2b 100644 --- a/uv.lock +++ b/uv.lock @@ -2046,6 +2046,7 @@ dependencies = [ { name = "ocrmypdf", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "pathvalidate", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "pdf2image", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "psycopg-pool", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, @@ -2182,6 +2183,7 @@ requires-dist = [ { name = "ocrmypdf", specifier = "~=16.10.0" }, { name = "pathvalidate", specifier = "~=3.3.1" }, { name = "pdf2image", specifier = "~=1.17.0" }, + { name = "psutil", specifier = ">=7.0.0" }, { name = "psycopg", extras = ["c", "pool"], marker = "extra == 'postgres'", specifier = "==3.2.9" }, { name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.9/psycopg_c-3.2.9-cp312-cp312-linux_aarch64.whl" }, { name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.9/psycopg_c-3.2.9-cp312-cp312-linux_x86_64.whl" }, @@ -2548,6 +2550,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816, upload-time = "2025-01-20T15:55:29.98Z" }, ] +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, +] + [[package]] name = "psycopg" version = "3.2.9"