Skip to content
This repository was archived by the owner on May 23, 2023. It is now read-only.

Commit ce28113

Browse files
committed
Improve json logging reliability with a custom encoder
1 parent 3841e9a commit ce28113

File tree

1 file changed

+30
-6
lines changed

1 file changed

+30
-6
lines changed

ethereum/slogging.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import json
33
import textwrap
4+
from json.encoder import JSONEncoder
45
from logging import StreamHandler, Formatter, FileHandler
56
from ethereum.utils import bcolors, isnumeric
67

@@ -155,6 +156,11 @@ def _proxy(self, method_name, *args, **kwargs):
155156
fatal = critical = lambda self, *args, **kwargs: self._proxy('critical', *args, **kwargs)
156157

157158

159+
class _LogJSONEncoder(JSONEncoder):
160+
def default(self, o):
161+
return repr(o)
162+
163+
158164
class SLogger(logging.Logger):
159165

160166
def __init__(self, name, level=DEFAULT_LOGLEVEL):
@@ -174,17 +180,20 @@ def format_message(self, msg, kwargs, highlight, level):
174180
message['event'] = '{}.{}'.format(self.name, msg.lower().replace(' ', '_'))
175181
message['level'] = logging.getLevelName(level)
176182
try:
177-
message.update({
178-
k: v if isnumeric(v) or isinstance(v, (float, complex, list, str, dict)) else repr(v)
179-
for k, v in kwargs.items()
180-
})
181-
msg = json.dumps(message)
183+
message.update(kwargs)
184+
try:
185+
msg = json.dumps(message, cls=_LogJSONEncoder)
186+
except TypeError:
187+
# Invalid value. With our custom encoder this can only happen with non-string
188+
# dict keys (see: https://bugs.python.org/issue18820).
189+
message = _stringify_dict_keys(message)
190+
msg = json.dumps(message, cls=_LogJSONEncoder)
182191
except UnicodeDecodeError:
183192
message.update({
184193
k: v if isnumeric(v) or isinstance(v, (float, complex)) else repr(v)
185194
for k, v in kwargs.items()
186195
})
187-
msg = json.dumps(message)
196+
msg = json.dumps(message, cls=_LogJSONEncoder)
188197
else:
189198
msg = "{}{} {}{}".format(
190199
bcolors.WARNING if highlight else "",
@@ -246,6 +255,21 @@ def getLogger(self, name):
246255
SLogger.manager = SManager(SLogger.root)
247256

248257

258+
def _stringify_dict_keys(input_):
259+
if isinstance(input_, dict):
260+
res = {}
261+
for k, v in input_.items():
262+
v = _stringify_dict_keys(v)
263+
if not isinstance(k, (int, long, bool, None.__class__)):
264+
k = str(k)
265+
res[k] = v
266+
elif isinstance(input_, (list, tuple)):
267+
res = input_.__class__([_stringify_dict_keys(i) for i in input_])
268+
else:
269+
res = input_
270+
return res
271+
272+
249273
def getLogger(name=None):
250274
"""
251275
Return a logger with the specified name, creating it if necessary.

0 commit comments

Comments
 (0)