Skip to content

Commit c95313e

Browse files
stjsethmlarsonbasepi
authored
Fix TypeError when exc_info=False (#42)
* Fix TypeError when exc_info=False Formatting of a message fails in the `StdlibFormatter` when the `exc_info` parameter is `False`. The traceback shows that handling of the boolean is not done correct in the formatting for error type and message. ```python Traceback (most recent call last): File "lib/python3.8/logging/__init__.py", line 1081, in emit msg = self.format(record) File "lib/python3.8/logging/__init__.py", line 925, in format return fmt.format(record) File "ecs_logging/_stdlib.py", line 117, in format result = self.format_to_ecs(record) File "ecs_logging/_stdlib.py", line 163, in format_to_ecs value = extractors[field](record) File "ecs_logging/_stdlib.py", line 150, in <lambda> if (r.exc_info is not None and r.exc_info[0] is not None) TypeError: 'bool' object is not subscriptable ``` This replaces the lambda with methods handling `exc_info` being an boolean or iterable. * PR review comments * grammar * add missing import * Update test_stdlib_formatter.py * CHANGELOG Co-authored-by: Seth Michael Larson <sethmichaellarson@gmail.com> Co-authored-by: Colton Myers <colton.myers@gmail.com>
1 parent 8d6d3d3 commit c95313e

File tree

3 files changed

+47
-8
lines changed

3 files changed

+47
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
(used for the log `message`) and `event.dataset` (a field provided by the
77
`elasticapm` integration) ([#46](https://github.com/elastic/ecs-logging-python/pull/46))
88
* Add default/fallback handling for json.dumps ([#47](https://github.com/elastic/ecs-logging-python/pull/47))
9+
* Fixed an issue in `StdLibFormatter` when `exc_info=False` ([#42](https://github.com/elastic/ecs-logging-python/pull/42))
910

1011
## 1.0.0 (2021-02-08)
1112

ecs_logging/_stdlib.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717

1818
import logging
19+
import sys
1920
import time
2021
from traceback import format_tb
2122
from ._meta import ECS_VERSION
@@ -112,6 +113,34 @@ def __init__(self, stack_trace_limit=None, exclude_fields=()):
112113
self._exclude_fields = frozenset(exclude_fields)
113114
self._stack_trace_limit = stack_trace_limit
114115

116+
def _record_error_type(self, record):
117+
# type: (logging.LogRecord) -> Optional[str]
118+
exc_info = record.exc_info
119+
if not exc_info:
120+
# exc_info is either an iterable or bool. If it doesn't
121+
# evaluate to True, then no error type is used.
122+
return None
123+
if isinstance(exc_info, bool):
124+
# if it is a bool, then look at sys.exc_info
125+
exc_info = sys.exc_info()
126+
if isinstance(exc_info, (list, tuple)) and exc_info[0] is not None:
127+
return exc_info[0].__name__
128+
return None
129+
130+
def _record_error_message(self, record):
131+
# type: (logging.LogRecord) -> Optional[str]
132+
exc_info = record.exc_info
133+
if not exc_info:
134+
# exc_info is either an iterable or bool. If it doesn't
135+
# evaluate to True, then no error message is used.
136+
return None
137+
if isinstance(exc_info, bool):
138+
# if it is a bool, then look at sys.exc_info
139+
exc_info = sys.exc_info()
140+
if isinstance(exc_info, (list, tuple)) and exc_info[1]:
141+
return str(exc_info[1])
142+
return None
143+
115144
def format(self, record):
116145
# type: (logging.LogRecord) -> str
117146
result = self.format_to_ecs(record)
@@ -145,14 +174,8 @@ def format_to_ecs(self, record):
145174
"process.name": self._record_attribute("processName"),
146175
"process.thread.id": self._record_attribute("thread"),
147176
"process.thread.name": self._record_attribute("threadName"),
148-
"error.type": lambda r: (
149-
r.exc_info[0].__name__
150-
if (r.exc_info is not None and r.exc_info[0] is not None)
151-
else None
152-
),
153-
"error.message": lambda r: (
154-
str(r.exc_info[1]) if r.exc_info and r.exc_info[1] else None
155-
),
177+
"error.type": self._record_error_type,
178+
"error.message": self._record_error_message,
156179
"error.stack_trace": self._record_error_stack_trace,
157180
} # type: Dict[str, Callable[[logging.LogRecord],Any]]
158181

tests/test_stdlib_formatter.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,21 @@ def test_stack_trace_limit_disabled(stack_trace_limit, logger):
183183
assert ecs["log"]["original"] == "there was an error"
184184

185185

186+
def test_exc_info_false_does_not_raise(logger):
187+
stream = StringIO()
188+
handler = logging.StreamHandler(stream)
189+
handler.setFormatter(ecs_logging.StdlibFormatter())
190+
logger.addHandler(handler)
191+
logger.setLevel(logging.DEBUG)
192+
193+
logger.info("there was %serror", "no ", exc_info=False)
194+
195+
ecs = json.loads(stream.getvalue().rstrip())
196+
assert ecs["log.level"] == "info"
197+
assert ecs["message"] == "there was no error"
198+
assert "error" not in ecs
199+
200+
186201
def test_stack_trace_limit_traceback(logger):
187202
def f():
188203
g()

0 commit comments

Comments
 (0)