File size: 1,620 Bytes
6dc0c9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
57
58
59
60
# A JSON logger that sends data to remote endpoint.
# Architecturally, it hosts a background thread that sends logs to a remote endpoint.
import os
import json
import requests
import threading
import queue
import logging

_global_logger = None


def get_remote_logger():
    global _global_logger
    if _global_logger is None:
        if url := os.environ.get("REMOTE_LOGGER_URL"):
            logging.info(f"Remote logger enabled, sending data to {url}")
            _global_logger = RemoteLogger(url=url)
        else:
            _global_logger = EmptyLogger()
    return _global_logger


class EmptyLogger:
    """Dummy logger that does nothing."""

    def __init__(self):
        pass

    def log(self, _data: dict):
        pass


class RemoteLogger:
    """A JSON logger that sends data to remote endpoint."""

    def __init__(self, url: str):
        self.url = url

        self.logs = queue.Queue()
        self.thread = threading.Thread(target=self._send_logs, daemon=True)
        self.thread.start()

    def log(self, data: dict):
        self.logs.put_nowait(data)

    def _send_logs(self):
        while True:
            data = self.logs.get()

            # process the data by keep only the top level fields, and turn any nested dict into a string
            for key, value in data.items():
                if isinstance(value, (dict, list, tuple)):
                    data[key] = json.dumps(value, ensure_ascii=False)

            try:
                requests.post(self.url, json=data)
            except Exception:
                logging.exception("Failed to send logs to remote endpoint")