Skip to content

Commit a31f495

Browse files
author
Peter Giacomo Lombardo
authored
Span: Add Stack Frame limits (instana#292)
* Apply Stack reporting limits in spans * Don't collect backtrace on Entry spans * Better stack processing and a hard limit * Only slice if limit is exceeded * Updates to follow changes * Fix synthetic flag propagation * Update to follow changes
1 parent 7cea4bf commit a31f495

File tree

10 files changed

+110
-179
lines changed

10 files changed

+110
-179
lines changed

instana/span.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,10 @@ def __init__(self, span, source, service_name, **kwargs):
104104
self.f = source
105105
self.ec = span.tags.pop('ec', None)
106106
self.data = DictionaryOfStan()
107+
self.stack = span.stack
107108

108-
if span.synthetic:
109-
self.sy = True
110-
111-
if span.stack:
112-
self.stack = span.stack
109+
if span.synthetic is True:
110+
self.sy = span.synthetic
113111

114112
self.__dict__.update(kwargs)
115113

instana/tracer.py

+36-31
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,6 @@ def start_span(self,
104104
if operation_name in RegisteredSpan.EXIT_SPANS:
105105
self.__add_stack(span)
106106

107-
elif operation_name in RegisteredSpan.ENTRY_SPANS:
108-
# For entry spans, add only a backtrace fingerprint
109-
self.__add_stack(span, limit=2)
110-
111107
return span
112108

113109
def inject(self, span_context, format, carrier):
@@ -122,33 +118,42 @@ def extract(self, format, carrier):
122118

123119
raise ot.UnsupportedFormatException()
124120

125-
def __add_stack(self, span, limit=None):
126-
""" Adds a backtrace to this span """
127-
span.stack = []
128-
frame_count = 0
129-
130-
tb = traceback.extract_stack()
131-
tb.reverse()
132-
for frame in tb:
133-
if limit is not None and frame_count >= limit:
134-
break
135-
136-
# Exclude Instana frames unless we're in dev mode
137-
if "INSTANA_DEBUG" not in os.environ:
138-
if re_tracer_frame.search(frame[0]) is not None:
139-
continue
140-
141-
if re_with_stan_frame.search(frame[2]) is not None:
142-
continue
143-
144-
span.stack.append({
145-
"c": frame[0],
146-
"n": frame[1],
147-
"m": frame[2]
148-
})
149-
150-
if limit is not None:
151-
frame_count += 1
121+
def __add_stack(self, span, limit=30):
122+
"""
123+
Adds a backtrace to <span>. The default length limit for
124+
stack traces is 30 frames. A hard limit of 40 frames is enforced.
125+
"""
126+
try:
127+
sanitized_stack = []
128+
if limit > 40:
129+
limit = 40
130+
131+
trace_back = traceback.extract_stack()
132+
trace_back.reverse()
133+
for frame in trace_back:
134+
# Exclude Instana frames unless we're in dev mode
135+
if "INSTANA_DEBUG" not in os.environ:
136+
if re_tracer_frame.search(frame[0]) is not None:
137+
continue
138+
139+
if re_with_stan_frame.search(frame[2]) is not None:
140+
continue
141+
142+
sanitized_stack.append({
143+
"c": frame[0],
144+
"n": frame[1],
145+
"m": frame[2]
146+
})
147+
148+
if len(sanitized_stack) > limit:
149+
# (limit * -1) gives us negative form of <limit> used for
150+
# slicing from the end of the list. e.g. stack[-30:]
151+
span.stack = sanitized_stack[(limit*-1):]
152+
else:
153+
span.stack = sanitized_stack
154+
except Exception:
155+
# No fail
156+
pass
152157

153158

154159
# Used by __add_stack

tests/clients/test_urllib3.py

+14-28
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ def test_get_request(self):
6262
self.assertEqual('GET', wsgi_span.data["http"]["method"])
6363
self.assertEqual(200, wsgi_span.data["http"]["status"])
6464
self.assertIsNone(wsgi_span.data["http"]["error"])
65-
self.assertIsNotNone(wsgi_span.stack)
66-
self.assertEqual(2, len(wsgi_span.stack))
65+
self.assertIsNone(wsgi_span.stack)
6766

6867
# urllib3
6968
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -110,8 +109,7 @@ def test_get_request_with_query(self):
110109
self.assertEqual('GET', wsgi_span.data["http"]["method"])
111110
self.assertEqual(200, wsgi_span.data["http"]["status"])
112111
self.assertIsNone(wsgi_span.data["http"]["error"])
113-
self.assertIsNotNone(wsgi_span.stack)
114-
self.assertEqual(2, len(wsgi_span.stack))
112+
self.assertIsNone(wsgi_span.stack)
115113

116114
# urllib3
117115
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -159,8 +157,7 @@ def test_get_request_with_alt_query(self):
159157
self.assertEqual('GET', wsgi_span.data["http"]["method"])
160158
self.assertEqual(200, wsgi_span.data["http"]["status"])
161159
self.assertIsNone(wsgi_span.data["http"]["error"])
162-
self.assertIsNotNone(wsgi_span.stack)
163-
self.assertEqual(2, len(wsgi_span.stack))
160+
self.assertIsNone(wsgi_span.stack)
164161

165162
# urllib3
166163
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -208,8 +205,7 @@ def test_put_request(self):
208205
self.assertEqual('PUT', wsgi_span.data["http"]["method"])
209206
self.assertEqual(404, wsgi_span.data["http"]["status"])
210207
self.assertIsNone(wsgi_span.data["http"]["error"])
211-
self.assertIsNotNone(wsgi_span.stack)
212-
self.assertEqual(2, len(wsgi_span.stack))
208+
self.assertIsNone(wsgi_span.stack)
213209

214210
# urllib3
215211
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -265,17 +261,15 @@ def test_301_redirect(self):
265261
self.assertEqual('GET', wsgi_span1.data["http"]["method"])
266262
self.assertEqual(200, wsgi_span1.data["http"]["status"])
267263
self.assertIsNone(wsgi_span1.data["http"]["error"])
268-
self.assertIsNotNone(wsgi_span1.stack)
269-
self.assertEqual(2, len(wsgi_span1.stack))
264+
self.assertIsNone(wsgi_span1.stack)
270265

271266
self.assertEqual("wsgi", wsgi_span2.n)
272267
self.assertEqual('127.0.0.1:' + str(testenv["wsgi_port"]), wsgi_span2.data["http"]["host"])
273268
self.assertEqual('/301', wsgi_span2.data["http"]["url"])
274269
self.assertEqual('GET', wsgi_span2.data["http"]["method"])
275270
self.assertEqual(301, wsgi_span2.data["http"]["status"])
276271
self.assertIsNone(wsgi_span2.data["http"]["error"])
277-
self.assertIsNotNone(wsgi_span2.stack)
278-
self.assertEqual(2, len(wsgi_span2.stack))
272+
self.assertIsNone(wsgi_span2.stack)
279273

280274
# urllib3
281275
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -339,17 +333,15 @@ def test_302_redirect(self):
339333
self.assertEqual('GET', wsgi_span1.data["http"]["method"])
340334
self.assertEqual(200, wsgi_span1.data["http"]["status"])
341335
self.assertIsNone(wsgi_span1.data["http"]["error"])
342-
self.assertIsNotNone(wsgi_span1.stack)
343-
self.assertEqual(2, len(wsgi_span1.stack))
336+
self.assertIsNone(wsgi_span1.stack)
344337

345338
self.assertEqual("wsgi", wsgi_span2.n)
346339
self.assertEqual('127.0.0.1:' + str(testenv["wsgi_port"]), wsgi_span2.data["http"]["host"])
347340
self.assertEqual('/302', wsgi_span2.data["http"]["url"])
348341
self.assertEqual('GET', wsgi_span2.data["http"]["method"])
349342
self.assertEqual(302, wsgi_span2.data["http"]["status"])
350343
self.assertIsNone(wsgi_span2.data["http"]["error"])
351-
self.assertIsNotNone(wsgi_span2.stack)
352-
self.assertEqual(2, len(wsgi_span2.stack))
344+
self.assertIsNone(wsgi_span2.stack)
353345

354346
# urllib3
355347
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -405,8 +397,7 @@ def test_5xx_request(self):
405397
self.assertEqual('GET', wsgi_span.data["http"]["method"])
406398
self.assertEqual(504, wsgi_span.data["http"]["status"])
407399
self.assertIsNone(wsgi_span.data["http"]["error"])
408-
self.assertIsNotNone(wsgi_span.stack)
409-
self.assertEqual(2, len(wsgi_span.stack))
400+
self.assertIsNone(wsgi_span.stack)
410401

411402
# urllib3
412403
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -457,8 +448,7 @@ def test_exception_logging(self):
457448
self.assertEqual('GET', wsgi_span.data["http"]["method"])
458449
self.assertEqual(500, wsgi_span.data["http"]["status"])
459450
self.assertIsNone(wsgi_span.data["http"]["error"])
460-
self.assertIsNotNone(wsgi_span.stack)
461-
self.assertEqual(2, len(wsgi_span.stack))
451+
self.assertIsNone(wsgi_span.stack)
462452

463453
# urllib3
464454
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -545,8 +535,7 @@ def test_requestspkg_get(self):
545535
self.assertEqual('GET', wsgi_span.data["http"]["method"])
546536
self.assertEqual(200, wsgi_span.data["http"]["status"])
547537
self.assertIsNone(wsgi_span.data["http"]["error"])
548-
self.assertIsNotNone(wsgi_span.stack)
549-
self.assertEqual(2, len(wsgi_span.stack))
538+
self.assertIsNone(wsgi_span.stack)
550539

551540
# urllib3
552541
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -596,8 +585,7 @@ def test_requestspkg_get_with_custom_headers(self):
596585
self.assertEqual('GET', wsgi_span.data["http"]["method"])
597586
self.assertEqual(200, wsgi_span.data["http"]["status"])
598587
self.assertIsNone(wsgi_span.data["http"]["error"])
599-
self.assertIsNotNone(wsgi_span.stack)
600-
self.assertEqual(2, len(wsgi_span.stack))
588+
self.assertIsNone(wsgi_span.stack)
601589

602590
# urllib3
603591
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -643,8 +631,7 @@ def test_requestspkg_put(self):
643631
self.assertEqual('PUT', wsgi_span.data["http"]["method"])
644632
self.assertEqual(404, wsgi_span.data["http"]["status"])
645633
self.assertIsNone(wsgi_span.data["http"]["error"])
646-
self.assertIsNotNone(wsgi_span.stack)
647-
self.assertEqual(2, len(wsgi_span.stack))
634+
self.assertIsNone(wsgi_span.stack)
648635

649636
# urllib3
650637
self.assertEqual("test", test_span.data["sdk"]["name"])
@@ -694,8 +681,7 @@ def test_response_header_capture(self):
694681
self.assertEqual('GET', wsgi_span.data["http"]["method"])
695682
self.assertEqual(200, wsgi_span.data["http"]["status"])
696683
self.assertIsNone(wsgi_span.data["http"]["error"])
697-
self.assertIsNotNone(wsgi_span.stack)
698-
self.assertEqual(2, len(wsgi_span.stack))
684+
self.assertIsNone(wsgi_span.stack)
699685

700686
# urllib3
701687
self.assertEqual("test", test_span.data["sdk"]["name"])

tests/frameworks/test_aiohttp_server.py

+7-21
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ async def test():
7272
self.assertEqual(testenv["aiohttp_server"] +
7373
"/", aioserver_span.data["http"]["url"])
7474
self.assertEqual("GET", aioserver_span.data["http"]["method"])
75-
self.assertIsNotNone(aioserver_span.stack)
76-
self.assertTrue(isinstance(aioserver_span.stack, list))
77-
self.assertTrue(len(aioserver_span.stack) > 1)
75+
self.assertIsNone(aioserver_span.stack)
7876

7977
self.assertEqual("aiohttp-client", aioclient_span.n)
8078
self.assertEqual(200, aioclient_span.data["http"]["status"])
@@ -136,9 +134,7 @@ async def test():
136134
self.assertEqual(testenv["aiohttp_server"] +
137135
"/204", aioserver_span.data["http"]["url"])
138136
self.assertEqual("GET", aioserver_span.data["http"]["method"])
139-
self.assertIsNotNone(aioserver_span.stack)
140-
self.assertTrue(isinstance(aioserver_span.stack, list))
141-
self.assertTrue(len(aioserver_span.stack) > 1)
137+
self.assertIsNone(aioserver_span.stack)
142138

143139
self.assertEqual("aiohttp-client", aioclient_span.n)
144140
self.assertEqual(204, aioclient_span.data["http"]["status"])
@@ -220,9 +216,7 @@ async def test():
220216
self.assertEqual("GET", aioserver_span.data["http"]["method"])
221217
self.assertEqual("secret=<redacted>",
222218
aioserver_span.data["http"]["params"])
223-
self.assertIsNotNone(aioserver_span.stack)
224-
self.assertTrue(isinstance(aioserver_span.stack, list))
225-
self.assertTrue(len(aioserver_span.stack) > 1)
219+
self.assertIsNone(aioserver_span.stack)
226220

227221
self.assertEqual("aiohttp-client", aioclient_span.n)
228222
self.assertEqual(200, aioclient_span.data["http"]["status"])
@@ -291,9 +285,7 @@ async def test():
291285
self.assertEqual("GET", aioserver_span.data["http"]["method"])
292286
self.assertEqual("secret=<redacted>",
293287
aioserver_span.data["http"]["params"])
294-
self.assertIsNotNone(aioserver_span.stack)
295-
self.assertTrue(isinstance(aioserver_span.stack, list))
296-
self.assertTrue(len(aioserver_span.stack) > 1)
288+
self.assertIsNone(aioserver_span.stack)
297289

298290
self.assertEqual("aiohttp-client", aioclient_span.n)
299291
self.assertEqual(200, aioclient_span.data["http"]["status"])
@@ -357,9 +349,7 @@ async def test():
357349
self.assertEqual(testenv["aiohttp_server"] +
358350
"/401", aioserver_span.data["http"]["url"])
359351
self.assertEqual("GET", aioserver_span.data["http"]["method"])
360-
self.assertIsNotNone(aioserver_span.stack)
361-
self.assertTrue(isinstance(aioserver_span.stack, list))
362-
self.assertTrue(len(aioserver_span.stack) > 1)
352+
self.assertIsNone(aioserver_span.stack)
363353

364354
self.assertEqual("aiohttp-client", aioclient_span.n)
365355
self.assertEqual(401, aioclient_span.data["http"]["status"])
@@ -416,9 +406,7 @@ async def test():
416406
self.assertEqual(testenv["aiohttp_server"] +
417407
"/500", aioserver_span.data["http"]["url"])
418408
self.assertEqual("GET", aioserver_span.data["http"]["method"])
419-
self.assertIsNotNone(aioserver_span.stack)
420-
self.assertTrue(isinstance(aioserver_span.stack, list))
421-
self.assertTrue(len(aioserver_span.stack) > 1)
409+
self.assertIsNone(aioserver_span.stack)
422410

423411
self.assertEqual("aiohttp-client", aioclient_span.n)
424412
self.assertEqual(500, aioclient_span.data["http"]["status"])
@@ -477,9 +465,7 @@ async def test():
477465
self.assertEqual(testenv["aiohttp_server"] +
478466
"/exception", aioserver_span.data["http"]["url"])
479467
self.assertEqual("GET", aioserver_span.data["http"]["method"])
480-
self.assertIsNotNone(aioserver_span.stack)
481-
self.assertTrue(isinstance(aioserver_span.stack, list))
482-
self.assertTrue(len(aioserver_span.stack) > 1)
468+
self.assertIsNone(aioserver_span.stack)
483469

484470
self.assertEqual("aiohttp-client", aioclient_span.n)
485471
self.assertEqual(500, aioclient_span.data["http"]["status"])

tests/frameworks/test_django.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ def test_basic_request(self):
7171
self.assertEqual('/', django_span.data["http"]["url"])
7272
self.assertEqual('GET', django_span.data["http"]["method"])
7373
self.assertEqual(200, django_span.data["http"]["status"])
74-
assert django_span.stack
75-
self.assertEqual(2, len(django_span.stack))
74+
self.assertIsNone(django_span.stack)
7675

7776
def test_synthetic_request(self):
7877
headers = {
@@ -154,8 +153,7 @@ def test_request_with_error(self):
154153
self.assertEqual('GET', django_span.data["http"]["method"])
155154
self.assertEqual(500, django_span.data["http"]["status"])
156155
self.assertEqual('This is a fake error: /cause-error', django_span.data["http"]["error"])
157-
assert(django_span.stack)
158-
self.assertEqual(2, len(django_span.stack))
156+
self.assertIsNone(django_span.stack)
159157

160158
def test_complex_request(self):
161159
with tracer.start_active_span('test'):
@@ -204,8 +202,7 @@ def test_complex_request(self):
204202
self.assertEqual(ot_span2.p, ot_span1.s)
205203

206204
self.assertEqual(None, django_span.ec)
207-
assert(django_span.stack)
208-
self.assertEqual(2, len(django_span.stack))
205+
self.assertIsNone(django_span.stack)
209206

210207
self.assertEqual('/complex', django_span.data["http"]["url"])
211208
self.assertEqual('GET', django_span.data["http"]["method"])
@@ -244,8 +241,7 @@ def test_custom_header_capture(self):
244241
self.assertEqual(django_span.p, urllib3_span.s)
245242

246243
self.assertEqual(None, django_span.ec)
247-
assert(django_span.stack)
248-
self.assertEqual(2, len(django_span.stack))
244+
self.assertIsNone(django_span.stack)
249245

250246
self.assertEqual('/', django_span.data["http"]["url"])
251247
self.assertEqual('GET', django_span.data["http"]["method"])

0 commit comments

Comments
 (0)