about summary refs log tree commit diff
path: root/gn_libs/http_logging.py
diff options
context:
space:
mode:
Diffstat (limited to 'gn_libs/http_logging.py')
-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