Skip to content

Commit 702015b

Browse files
authored
Merge pull request #103 from lightstep/codeboten/traceback
Re-enable traceback formatting with cause
2 parents ad11cc2 + a605944 commit 702015b

File tree

3 files changed

+86
-9
lines changed

3 files changed

+86
-9
lines changed

lightstep/recorder.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import warnings
1515

1616
from basictracer.recorder import SpanRecorder
17-
from opentracing.logs import ERROR_KIND, STACK
17+
from opentracing.logs import ERROR_KIND, STACK, ERROR_OBJECT
1818

1919
from lightstep.http_converter import HttpConverter
2020
from lightstep.thrift_converter import ThriftConverter
@@ -171,7 +171,11 @@ def _normalize_log(self, log):
171171
log.key_values[ERROR_KIND] = util._format_exc_type(log.key_values[ERROR_KIND])
172172

173173
if STACK in log.key_values:
174-
log.key_values[STACK] = util._format_exc_tb(log.key_values[STACK])
174+
log.key_values[STACK] = util._format_exc_tb(
175+
log.key_values.get(ERROR_KIND),
176+
log.key_values.get(ERROR_OBJECT),
177+
log.key_values[STACK]
178+
)
175179

176180
return log
177181

lightstep/util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ def _coerce_to_unicode(val):
105105
return '(encoding error)'
106106

107107

108-
def _format_exc_tb(exc_tb):
108+
def _format_exc_tb(exc_type, exc_value, exc_tb):
109109
if type(exc_tb) is types.TracebackType:
110-
return ''.join(traceback.format_tb(exc_tb))
110+
return ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
111111

112112
return exc_tb
113113

tests/recorder_test.py

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import socket
2+
import sys
23
import time
3-
import unittest
44

55
import lightstep.constants
66
import lightstep.recorder
77
import lightstep.tracer
8-
import lightstep.recorder
98
from basictracer.span import BasicSpan
109
from basictracer.context import SpanContext
10+
from opentracing.logs import ERROR_KIND, STACK, ERROR_OBJECT
1111
import pytest
1212

1313
from lightstep.crouton import ttypes
@@ -80,9 +80,7 @@ def test_default_tags_set_correctly(recorder):
8080
"access_token": "{your_access_token}",
8181
"component_name": "python/runtime_test",
8282
"periodic_flush_seconds": 0,
83-
"tags": {
84-
"lightstep.hostname": "hostname",
85-
},
83+
"tags": {"lightstep.hostname": "hostname",},
8684
}
8785
new_recorder = lightstep.recorder.Recorder(**runtime_args)
8886
for tag in new_recorder._runtime.tags:
@@ -179,3 +177,78 @@ def dummy_basic_span(recorder, i):
179177
)
180178
span.finish()
181179
return span
180+
181+
182+
def test_exception_formatting(recorder):
183+
mock_connection = MockConnection()
184+
mock_connection.open()
185+
186+
assert len(recorder._span_records) == 0
187+
188+
span = BasicSpan(
189+
lightstep.tracer._LightstepTracer(False, recorder, None),
190+
operation_name="exception span",
191+
context=SpanContext(trace_id=1000, span_id=2000),
192+
start_time=time.time() - 100,
193+
)
194+
span.log_kv({ERROR_KIND: AttributeError})
195+
span.finish()
196+
assert len(recorder._span_records) == 1
197+
assert recorder.flush(mock_connection)
198+
spans = recorder.converter.get_span_records(mock_connection.reports[0])
199+
if hasattr(spans[0], "log_records"):
200+
assert len(spans[0].log_records) == 1
201+
assert len(spans[0].log_records[0].fields) == 1
202+
assert spans[0].log_records[0].fields[0] == ttypes.KeyValue(
203+
Key="error.kind", Value="AttributeError"
204+
)
205+
else:
206+
assert len(spans[0].logs) == 1
207+
assert len(spans[0].logs[0].fields) == 1
208+
assert spans[0].logs[0].fields[0].key == "error.kind"
209+
assert spans[0].logs[0].fields[0].string_value == "AttributeError"
210+
211+
span = BasicSpan(
212+
lightstep.tracer._LightstepTracer(False, recorder, None),
213+
operation_name="exception span",
214+
context=SpanContext(trace_id=1000, span_id=2000),
215+
start_time=time.time() - 100,
216+
)
217+
218+
try:
219+
raise Exception
220+
except Exception: # pylint: disable=broad-except
221+
exc_type, exc_value, exc_tb = sys.exc_info()
222+
span.log_kv({STACK: exc_tb, ERROR_KIND: exc_type, ERROR_OBJECT: exc_value})
223+
224+
span.finish()
225+
assert len(recorder._span_records) == 1
226+
assert recorder.flush(mock_connection)
227+
spans = recorder.converter.get_span_records(mock_connection.reports[1])
228+
229+
if hasattr(spans[0], "log_records"):
230+
assert len(spans[0].log_records) == 1
231+
assert len(spans[0].log_records[0].fields) == 3
232+
for field in spans[0].log_records[0].fields:
233+
if field.Key == "stack":
234+
assert "Traceback (most recent call last):" in field.Value
235+
elif field.Key == "error.kind":
236+
assert field.Value == "Exception"
237+
elif field.Key == "error.object":
238+
assert field.Value == ""
239+
else:
240+
raise AttributeError("unexpected field: %s".format(field.Key))
241+
else:
242+
assert len(spans[0].logs) == 1
243+
assert len(spans[0].logs[0].fields) == 3
244+
245+
for field in spans[0].logs[0].fields:
246+
if field.key == "stack":
247+
assert "Traceback (most recent call last):" in field.string_value
248+
elif field.key == "error.kind":
249+
assert field.string_value == "Exception"
250+
elif field.key == "error.object":
251+
assert field.string_value == ""
252+
else:
253+
raise AttributeError("unexpected field: %s".format(field.key))
254+

0 commit comments

Comments
 (0)