Skip to content

Commit 1e2ce22

Browse files
committed
Merge pull request #1089 from getsentry/js-culprit
Extract a better culprit after parsing the sourcemap
2 parents a4966d0 + ae37a67 commit 1e2ce22

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

src/sentry/tasks/fetch_source.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
import urllib2
1414
import zlib
1515
import base64
16+
from os.path import splitext
1617
from collections import namedtuple
1718
from simplejson import JSONDecodeError
18-
from urlparse import urljoin
19+
from urlparse import urljoin, urlsplit
1920

20-
from sentry.constants import SOURCE_FETCH_TIMEOUT
21+
from sentry.constants import SOURCE_FETCH_TIMEOUT, MAX_CULPRIT_LENGTH
2122
from sentry.utils.cache import cache
2223
from sentry.utils.sourcemaps import sourcemap_to_index, find_source
24+
from sentry.utils.strings import truncatechars
2325

2426

2527
BAD_SOURCE = -1
@@ -29,6 +31,13 @@
2931
DEFAULT_ENCODING = 'utf-8'
3032
BASE64_SOURCEMAP_PREAMBLE = 'data:application/json;base64,'
3133
BASE64_PREAMBLE_LENGTH = len(BASE64_SOURCEMAP_PREAMBLE)
34+
CLEAN_MODULE_RE = re.compile(r"""^(?:(?:
35+
(?:java)?scripts?|js|build|static|_\w*| # common folder prefixes
36+
v?(?:\d+\.)*\d+| # version numbers, v1, 1.0.0
37+
[a-f0-9]{7,8}| # short sha
38+
[a-f0-9]{32}| # md5
39+
[a-f0-9]{40} # sha1
40+
)/)+""", re.X | re.I)
3241

3342
UrlResult = namedtuple('UrlResult', ['url', 'headers', 'body'])
3443

@@ -333,6 +342,7 @@ def expand_javascript_source(data, **kwargs):
333342
frame.function = state.name
334343
frame.abs_path = abs_path
335344
frame.filename = state.src
345+
frame.module = generate_module(state.src) or '<unknown module>'
336346
elif sourcemap in sourmap_idxs:
337347
frame.data = {
338348
'sourcemap': sourcemap,
@@ -348,3 +358,25 @@ def expand_javascript_source(data, **kwargs):
348358
logger.debug('Updating stacktraces with expanded source context')
349359
for exception, stacktrace in itertools.izip(data['sentry.interfaces.Exception']['values'], stacktraces):
350360
exception['stacktrace'] = stacktrace.serialize()
361+
362+
# Attempt to fix the culrpit now that we have useful information
363+
culprit_frame = stacktraces[0].frames[0]
364+
if culprit_frame.module and culprit_frame.function:
365+
data['culprit'] = truncatechars(generate_culprit(culprit_frame), MAX_CULPRIT_LENGTH)
366+
367+
368+
def generate_module(src):
369+
"""
370+
Converts a url into a made-up module name by doing the following:
371+
* Extract just the path name
372+
* Trimming off the initial /
373+
* Trimming off the file extension
374+
* Removes off useless folder prefixes
375+
376+
e.g. http://google.com/js/v1.0/foo/bar/baz.js -> foo/bar/baz
377+
"""
378+
return CLEAN_MODULE_RE.sub('', splitext(urlsplit(src).path[1:])[0])
379+
380+
381+
def generate_culprit(frame):
382+
return '%s in %s' % (frame.module, frame.function)

tests/sentry/tasks/fetch_source/tests.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import mock
66

77
from sentry.tasks.fetch_source import (
8-
UrlResult, expand_javascript_source, discover_sourcemap, fetch_sourcemap)
8+
UrlResult, expand_javascript_source, discover_sourcemap,
9+
fetch_sourcemap, generate_module)
910
from sentry.utils.sourcemaps import (SourceMap, SourceMapIndex)
1011
from sentry.testutils import TestCase
1112

@@ -121,6 +122,23 @@ def test_inlined_sources(self, discover_sourcemap, fetch_url, update):
121122
assert frame['post_context'] == []
122123

123124

125+
class GenerateModuleTest(TestCase):
126+
def test_simple(self):
127+
assert generate_module('http://example.com/foo.js') == 'foo'
128+
assert generate_module('http://example.com/foo/bar.js') == 'foo/bar'
129+
assert generate_module('http://example.com/js/foo/bar.js') == 'foo/bar'
130+
assert generate_module('http://example.com/javascript/foo/bar.js') == 'foo/bar'
131+
assert generate_module('http://example.com/1.0/foo/bar.js') == 'foo/bar'
132+
assert generate_module('http://example.com/v1/foo/bar.js') == 'foo/bar'
133+
assert generate_module('http://example.com/v1.0.0/foo/bar.js') == 'foo/bar'
134+
assert generate_module('http://example.com/_baz/foo/bar.js') == 'foo/bar'
135+
assert generate_module('http://example.com/1/2/3/foo/bar.js') == 'foo/bar'
136+
assert generate_module('http://example.com/abcdef0/foo/bar.js') == 'foo/bar'
137+
assert generate_module('http://example.com/92cd589eca8235e7b373bf5ae94ebf898e3b949c/foo/bar.js') == 'foo/bar'
138+
assert generate_module('http://example.com/7d6d00eae0ceccdc7ee689659585d95f/foo/bar.js') == 'foo/bar'
139+
assert generate_module('/foo/bar.js') == 'foo/bar'
140+
141+
124142
class FetchBase64SourcemapTest(TestCase):
125143
def test_simple(self):
126144
index = fetch_sourcemap(base64_sourcemap)

0 commit comments

Comments
 (0)