about summary refs log tree commit diff
path: root/gn_libs/http_logging.py
blob: 79660a87acd857fa04ec5cb702c659285d85f0fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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