Last active
February 21, 2025 17:22
-
-
Save costa/a72e53854fc93e14874452adbc6ea9c3 to your computer and use it in GitHub Desktop.
Python (3.12+ish tested) logging.handlers.HTTPHandler JSON support monkey-patching
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# NOTE Le monkey patch | |
# (https://github.com/python/cpython/blob/main/Lib/logging/handlers.py as of 2025-02-19) | |
def http_json_emit(self, record): | |
""" | |
Emit a record. | |
Send the record to the web server as a percent-encoded dictionary | |
""" | |
try: | |
# MONKEY import urllib.parse | |
import json | |
host = self.host | |
h = self.getConnection(host, self.secure) | |
url = self.url | |
# MONKEY data = urllib.parse.urlencode(self.mapLogRecord(record)) | |
data = json.dumps(self.mapLogRecord(record)) | |
if self.method == "GET": | |
if (url.find('?') >= 0): | |
sep = '&' | |
else: | |
sep = '?' | |
# MONKEY url = url + "%c%s" % (sep, data) | |
url = url + "%c%s" % (sep, urllib.parse.urlencode(data)) | |
h.putrequest(self.method, url) | |
# support multiple hosts on one IP address... | |
# need to strip optional :port from host, if present | |
i = host.find(":") | |
if i >= 0: | |
host = host[:i] | |
# See issue #30904: putrequest call above already adds this header | |
# on Python 3.x. | |
# h.putheader("Host", host) | |
if self.method == "POST": | |
# MONKEY h.putheader("Content-type", | |
# "application/x-www-form-urlencoded") | |
h.putheader("Content-type", "application/json") | |
h.putheader("Content-length", str(len(data))) | |
if self.credentials: | |
import base64 | |
s = ('%s:%s' % self.credentials).encode('utf-8') | |
s = 'Basic ' + base64.b64encode(s).strip().decode('ascii') | |
h.putheader('Authorization', s) | |
h.endheaders() | |
if self.method == "POST": | |
h.send(data.encode('utf-8')) | |
h.getresponse() # can't do anything with the result | |
except Exception: | |
self.handleError(record) | |
logging.handlers.HTTPHandler.emit = http_json_emit | |
def http_json_mapLogRecord(self, record): | |
# NOTE filtering empty values and duplicate attrs | |
return {k: v for k, v in record.__dict__.items() if v and k not in ['msg', 'exc_info']} | |
logging.handlers.HTTPHandler.mapLogRecord = http_json_mapLogRecord | |
# NOTE When you start monkey-patching, you just cannot stop...;) | |
_srcfile = os.path.normcase(http_json_emit.__code__.co_filename) | |
_logging_is_internal_frame = logging._is_internal_frame | |
def _is_internal_frame(frame): | |
filename = os.path.normcase(frame.f_code.co_filename) | |
return filename == _srcfile or _logging_is_internal_frame(frame) | |
logging._is_internal_frame = _is_internal_frame |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Not the best solution, but it works, and makes kinda more sense than https://github.com/nhairs/python-json-logger -- when you're not ready to go full https://github.com/Delgan/loguru just yet.