1
1
import logging
2
2
import json
3
3
import textwrap
4
+ from json .encoder import JSONEncoder
4
5
from logging import StreamHandler , Formatter , FileHandler
5
6
from ethereum .utils import bcolors , isnumeric
6
7
@@ -155,6 +156,11 @@ def _proxy(self, method_name, *args, **kwargs):
155
156
fatal = critical = lambda self , * args , ** kwargs : self ._proxy ('critical' , * args , ** kwargs )
156
157
157
158
159
+ class _LogJSONEncoder (JSONEncoder ):
160
+ def default (self , o ):
161
+ return repr (o )
162
+
163
+
158
164
class SLogger (logging .Logger ):
159
165
160
166
def __init__ (self , name , level = DEFAULT_LOGLEVEL ):
@@ -174,17 +180,20 @@ def format_message(self, msg, kwargs, highlight, level):
174
180
message ['event' ] = '{}.{}' .format (self .name , msg .lower ().replace (' ' , '_' ))
175
181
message ['level' ] = logging .getLevelName (level )
176
182
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 )
182
191
except UnicodeDecodeError :
183
192
message .update ({
184
193
k : v if isnumeric (v ) or isinstance (v , (float , complex )) else repr (v )
185
194
for k , v in kwargs .items ()
186
195
})
187
- msg = json .dumps (message )
196
+ msg = json .dumps (message , cls = _LogJSONEncoder )
188
197
else :
189
198
msg = "{}{} {}{}" .format (
190
199
bcolors .WARNING if highlight else "" ,
@@ -246,6 +255,21 @@ def getLogger(self, name):
246
255
SLogger .manager = SManager (SLogger .root )
247
256
248
257
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
+
249
273
def getLogger (name = None ):
250
274
"""
251
275
Return a logger with the specified name, creating it if necessary.
0 commit comments