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]