about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMunyoki Kilyungi2025-12-09 14:12:10 +0300
committerMunyoki Kilyungi2025-12-09 14:12:10 +0300
commit9422e39e5b79a4aaca7dc368368d39141a0242b7 (patch)
tree39920f260b1632cf0a6854d1522382ebe2dbdfbd
parent8cdf6bcddcfd6466662b057a5d4e80c283231002 (diff)
downloadgn-libs-9422e39e5b79a4aaca7dc368368d39141a0242b7.tar.gz
Add http logging utilities to be used in the rest of gn.
Signed-off-by: Munyoki Kilyungi <me@bonfacemunyoki.com>
-rw-r--r--gn_libs/http_logging.py56
1 files changed, 56 insertions, 0 deletions
diff --git a/gn_libs/http_logging.py b/gn_libs/http_logging.py
new file mode 100644
index 0000000..79660a8
--- /dev/null
+++ b/gn_libs/http_logging.py
@@ -0,0 +1,56 @@
+"""Provide a way to emit logs to an HTTP endpoint"""
+import logging
+import json
+import traceback
+import urllib.request
+from datetime import datetime
+
+
+class SilentHTTPHandler(logging.Handler):
+    """A logging handler that emits logs to an HTTP endpoint silently.
+
+    This handler converts log records to JSON and sends them via POST
+    to a specified HTTP endpoint. Failures are suppressed to avoid
+    interfering with the main application.
+    """
+    def __init__(self, endpoint, timeout=0.1):
+        super().__init__()
+        self.endpoint = endpoint
+        self.timeout = timeout
+
+    def emit(self, record):
+        try:
+            payload = {
+                "timestamp":  datetime.utcfromtimestamp(record.created).isoformat(),
+                "level": record.levelname.lower(),
+                "logger": record.name,
+                "message": record.getMessage(),
+            }
+            for attr in ("remote_addr", "user_agent", "extra"):
+                if hasattr(record, attr):
+                    payload.update({attr: getattr(record, attr)})
+
+            if record.exc_info:
+                payload["exception"] = "".join(
+                    traceback.format_exception(*record.exc_info)
+                )
+
+            # fire-and-forget
+            self._send(payload)
+
+        except Exception:
+            # absolute silence
+            pass
+
+    def _send(self, payload):
+        try:
+            req = urllib.request.Request(
+                url=self.endpoint,
+                data=json.dumps(payload).encode("utf-8"),
+                headers={"Content-Type": "application/json"},
+                method="POST",
+            )
+            with urllib.request.urlopen(req, timeout=5) as resp:
+                resp.read()  # ignore body
+        except Exception:
+            pass