Commit 9dc1dff336f6d5812fc9cdc78e37a355fc5d1b4d
1 parent
f55f8966
logger: introduce 'type' field for json output
This is a start to bring back some structure into the json output. Might not be a permanent solution, need to further discuss in PR 308
Showing
2 changed files
with
35 additions
and
3 deletions
oletools/common/log_helper/_json_formatter.py
| ... | ... | @@ -13,8 +13,13 @@ class JsonFormatter(logging.Formatter): |
| 13 | 13 | Since we don't buffer messages, we always prepend messages with a comma to make |
| 14 | 14 | the output JSON-compatible. The only exception is when printing the first line, |
| 15 | 15 | so we need to keep track of it. |
| 16 | + | |
| 17 | + We assume that all input comes from the OletoolsLoggerAdapter which | |
| 18 | + ensures that there is a `type` field in the record. Otherwise will have | |
| 19 | + to add a try-except around the access to `record.type`. | |
| 16 | 20 | """ |
| 17 | 21 | json_dict = dict(msg=record.msg, level=record.levelname) |
| 22 | + json_dict['type'] = record.type | |
| 18 | 23 | formatted_message = ' ' + json.dumps(json_dict) |
| 19 | 24 | |
| 20 | 25 | if self._is_first_line: | ... | ... |
oletools/common/log_helper/_logger_adapter.py
| ... | ... | @@ -8,18 +8,45 @@ class OletoolsLoggerAdapter(logging.LoggerAdapter): |
| 8 | 8 | """ |
| 9 | 9 | _json_enabled = None |
| 10 | 10 | |
| 11 | - def print_str(self, message): | |
| 11 | + def print_str(self, message, **kwargs): | |
| 12 | 12 | """ |
| 13 | 13 | This function replaces normal print() calls so we can format them as JSON |
| 14 | 14 | when needed or just print them right away otherwise. |
| 15 | 15 | """ |
| 16 | 16 | if self._json_enabled and self._json_enabled(): |
| 17 | 17 | # Messages from this function should always be printed, |
| 18 | - # so when using JSON we log using the same level that set | |
| 19 | - self.log(_root_logger_wrapper.level(), message) | |
| 18 | + # so when using JSON we log using the same level that set. | |
| 19 | + # Additional information in kwargs is added to LogRecord | |
| 20 | + self.log(_root_logger_wrapper.level(), message, extra=kwargs) | |
| 20 | 21 | else: |
| 21 | 22 | print(message) |
| 22 | 23 | |
| 24 | + def log(self, lvl, msg, *args, **kwargs): | |
| 25 | + """ | |
| 26 | + Run :py:meth:`process` on kwargs, then forward to actual logger. | |
| 27 | + | |
| 28 | + This is based on the logging cookbox, section "Using LoggerAdapter to | |
| 29 | + impart contextual information". | |
| 30 | + """ | |
| 31 | + msg, kwargs = self.process(msg, kwargs) | |
| 32 | + self.logger.log(lvl, msg, *args, **kwargs) | |
| 33 | + | |
| 34 | + def process(self, msg, kwargs): | |
| 35 | + """ | |
| 36 | + Ensure `kwargs['extra']['type']` exists, init with given arg `type`. | |
| 37 | + | |
| 38 | + The `type` field will be added to the :py:class:`logging.LogRecord` and | |
| 39 | + is used by the :py:class:`JsonFormatter`. | |
| 40 | + """ | |
| 41 | + if 'extra' not in kwargs: | |
| 42 | + kwargs['extra'] = {} | |
| 43 | + if 'type' in kwargs: | |
| 44 | + kwargs['extra']['type'] = kwargs['type'] | |
| 45 | + del kwargs['type'] # downstream loggers cannot deal with this | |
| 46 | + if 'type' not in kwargs['extra']: | |
| 47 | + kwargs['extra']['type'] = 'msg' # type will be added to LogRecord | |
| 48 | + return msg, kwargs | |
| 49 | + | |
| 23 | 50 | def set_json_enabled_function(self, json_enabled): |
| 24 | 51 | """ |
| 25 | 52 | Set a function to be called to check whether JSON output is enabled. | ... | ... |