Commit 80791d859f38c9e5402bb652e2cc3a1178f62765
1 parent
6bc04064
log_helper: Capture warnings into logging
We set logging.captureWarnings to True and use our logging framework for python's builtin warnings logger. Warnings from that logger will have "type=warning" per default (as opposed to "type=msg").
Showing
3 changed files
with
31 additions
and
5 deletions
oletools/common/log_helper/_json_formatter.py
| ... | ... | @@ -18,12 +18,22 @@ class JsonFormatter(logging.Formatter): |
| 18 | 18 | the output JSON-compatible. The only exception is when printing the first line, |
| 19 | 19 | so we need to keep track of it. |
| 20 | 20 | |
| 21 | - We assume that all input comes from the OletoolsLoggerAdapter which | |
| 22 | - ensures that there is a `type` field in the record. Otherwise will have | |
| 23 | - to add a try-except around the access to `record.type`. | |
| 21 | + The resulting text is just a json dump of the :py:class:`logging.LogRecord` | |
| 22 | + object that is received as input, so no %-formatting or similar is done. Raw | |
| 23 | + unformatted message and formatting arguments are contained in fields `msg` and | |
| 24 | + `args` of the output. | |
| 25 | + | |
| 26 | + Arg `record` has a `type` field when created by `OletoolLoggerAdapter`. If not | |
| 27 | + (e.g. captured warnings or output from third-party libraries), we add one. | |
| 24 | 28 | """ |
| 25 | 29 | json_dict = dict(msg=record.msg.replace('\n', ' '), level=record.levelname) |
| 26 | - json_dict['type'] = record.type | |
| 30 | + try: | |
| 31 | + json_dict['type'] = record.type | |
| 32 | + except AttributeError: | |
| 33 | + if record.name == 'py.warnings': # this is the name of the logger | |
| 34 | + json_dict['type'] = 'warning' | |
| 35 | + else: | |
| 36 | + json_dict['type'] = 'msg' | |
| 27 | 37 | formatted_message = ' ' + json.dumps(json_dict) |
| 28 | 38 | |
| 29 | 39 | if self._is_first_line: | ... | ... |
oletools/common/log_helper/_logger_adapter.py
| ... | ... | @@ -7,6 +7,7 @@ class OletoolsLoggerAdapter(logging.LoggerAdapter): |
| 7 | 7 | Adapter class for all loggers returned by the logging module. |
| 8 | 8 | """ |
| 9 | 9 | _json_enabled = None |
| 10 | + _is_warn_logger = False # this is always False | |
| 10 | 11 | |
| 11 | 12 | def print_str(self, message, **kwargs): |
| 12 | 13 | """ |
| ... | ... | @@ -44,7 +45,10 @@ class OletoolsLoggerAdapter(logging.LoggerAdapter): |
| 44 | 45 | kwargs['extra']['type'] = kwargs['type'] |
| 45 | 46 | del kwargs['type'] # downstream loggers cannot deal with this |
| 46 | 47 | if 'type' not in kwargs['extra']: |
| 47 | - kwargs['extra']['type'] = 'msg' # type will be added to LogRecord | |
| 48 | + if self._is_warn_logger: | |
| 49 | + kwargs['extra']['type'] = 'warning' # this will add field | |
| 50 | + else: | |
| 51 | + kwargs['extra']['type'] = 'msg' # 'type' to LogRecord | |
| 48 | 52 | return msg, kwargs |
| 49 | 53 | |
| 50 | 54 | def set_json_enabled_function(self, json_enabled): |
| ... | ... | @@ -53,6 +57,12 @@ class OletoolsLoggerAdapter(logging.LoggerAdapter): |
| 53 | 57 | """ |
| 54 | 58 | self._json_enabled = json_enabled |
| 55 | 59 | |
| 60 | + def set_warnings_logger(self): | |
| 61 | + """Make this the logger for warnings""" | |
| 62 | + # create a object attribute that shadows the class attribute which is | |
| 63 | + # always False | |
| 64 | + self._is_warn_logger = True | |
| 65 | + | |
| 56 | 66 | def level(self): |
| 57 | 67 | """Return current level of logger.""" |
| 58 | 68 | return self.logger.level | ... | ... |
oletools/common/log_helper/log_helper.py
| ... | ... | @@ -152,6 +152,11 @@ class LogHelper: |
| 152 | 152 | self._use_json = use_json |
| 153 | 153 | sys.excepthook = self._get_except_hook(sys.excepthook) |
| 154 | 154 | |
| 155 | + # make sure warnings do not mess up our output | |
| 156 | + logging.captureWarnings(True) | |
| 157 | + warn_logger = self.get_or_create_silent_logger('py.warnings') | |
| 158 | + warn_logger.set_warnings_logger() | |
| 159 | + | |
| 155 | 160 | # since there could be loggers already created we go through all of them |
| 156 | 161 | # and set their levels to 0 so they will use the root logger's level |
| 157 | 162 | for name in self._all_names: |
| ... | ... | @@ -174,6 +179,7 @@ class LogHelper: |
| 174 | 179 | |
| 175 | 180 | # end logging |
| 176 | 181 | self._all_names = set() |
| 182 | + logging.captureWarnings(False) | |
| 177 | 183 | logging.shutdown() |
| 178 | 184 | |
| 179 | 185 | # end json list | ... | ... |