This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git


The following commit(s) were added to refs/heads/main by this push:
     new 70e8ba7  Allow admin access to recent server logs during testing
70e8ba7 is described below

commit 70e8ba7539299e476804916677b09ff01646f3f3
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Jan 5 14:43:26 2026 +0000

    Allow admin access to recent server logs during testing
---
 atr/admin/__init__.py | 12 ++++++++++++
 atr/log.py            | 33 +++++++++++++++++++++++++++++++++
 atr/server.py         |  5 ++++-
 3 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/atr/admin/__init__.py b/atr/admin/__init__.py
index 3b54161..0ce76e0 100644
--- a/atr/admin/__init__.py
+++ b/atr/admin/__init__.py
@@ -560,6 +560,18 @@ async def ldap_post(session: web.Committer, lookup_form: 
LdapLookupForm) -> str:
     )
 
 
[email protected]("/logs")
+async def logs(session: web.Committer) -> web.QuartResponse:
+    conf = config.get()
+    debug_and_allow_tests = (config.get_mode() == config.Mode.Debug) and 
conf.ALLOW_TESTS
+    if not debug_and_allow_tests:
+        raise base.ASFQuartException("Not available without ALLOW_TESTS", 
errorcode=403)
+    recent_logs = log.get_recent_logs()
+    if recent_logs is None:
+        raise base.ASFQuartException("Debug logging not initialised", 
errorcode=404)
+    return web.TextResponse("\n".join(recent_logs))
+
+
 @admin.get("/ongoing-tasks/<project_name>/<version_name>/<revision>")
 async def ongoing_tasks_get(
     session: web.Committer, project_name: str, version_name: str, revision: str
diff --git a/atr/log.py b/atr/log.py
index 6d91f1d..b138c8c 100644
--- a/atr/log.py
+++ b/atr/log.py
@@ -15,14 +15,31 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import collections
 import inspect
 import logging
 import logging.handlers
 import queue
+import threading
 from typing import Final
 
 PERFORMANCE: logging.Logger | None = None
 
+_global_recent_logs: collections.deque[str] | None = None
+_global_recent_logs_lock = threading.Lock()
+
+
+class _BufferingHandler(logging.Handler):
+    def emit(self, record: logging.LogRecord) -> None:
+        if _global_recent_logs is None:
+            return
+        try:
+            msg = self.format(record)
+            with _global_recent_logs_lock:
+                _global_recent_logs.append(msg)
+        except Exception:
+            self.handleError(record)
+
 
 def caller_name(depth: int = 1) -> str:
     frame = inspect.currentframe()
@@ -73,10 +90,26 @@ def exception(msg: str) -> None:
     _event(logging.ERROR, msg, exc_info=True)
 
 
+def get_recent_logs() -> list[str] | None:
+    if _global_recent_logs is None:
+        return None
+    with _global_recent_logs_lock:
+        return list(_global_recent_logs)
+
+
 def info(msg: str) -> None:
     _event(logging.INFO, msg)
 
 
+def create_debug_handler() -> logging.Handler:
+    global _global_recent_logs
+    _global_recent_logs = collections.deque(maxlen=100)
+    handler = _BufferingHandler()
+    handler.setFormatter(logging.Formatter("%(asctime)s %(name)s 
%(levelname)s: %(message)s"))
+    handler.setLevel(logging.DEBUG)
+    return handler
+
+
 def interface_name(depth: int = 1) -> str:
     return caller_name(depth=depth)
 
diff --git a/atr/server.py b/atr/server.py
index 120e302..e39ffff 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -278,7 +278,10 @@ def _app_setup_logging(app: base.QuartApp, config_mode: 
config.Mode, app_config:
 
     console_handler = rich_logging.RichHandler(rich_tracebacks=True, 
show_time=False)
     log_queue = queue.Queue(-1)
-    listener = logging.handlers.QueueListener(log_queue, console_handler)
+    handlers: list[logging.Handler] = [console_handler]
+    if (config_mode == config.Mode.Debug) and app_config.ALLOW_TESTS:
+        handlers.append(log.create_debug_handler())
+    listener = logging.handlers.QueueListener(log_queue, *handlers)
     app.extensions["logging_listener"] = listener
 
     logging.basicConfig(


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to