Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.

Commit 91fc046

Browse files
dcramerashwoods
authored andcommitted
fix(breadcrumbs): Ensure maximum lengths on several attributes
- Trim message, category, and level attributes - Update pytest support to work in other environments
1 parent efb9e4a commit 91fc046

File tree

7 files changed

+52
-13
lines changed

7 files changed

+52
-13
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ jobs:
143143

144144
script: tox
145145
install:
146-
- pip install tox wheel codecov "coverage<4"
146+
- make
147+
- pip install codecov
147148
before_script:
148149
- pip freeze
149150
after_success:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ bootstrap:
44

55
test: bootstrap lint
66
@echo "Running Python tests"
7-
py.test -x tests
7+
py.test -f tests
88
@echo ""
99

1010
lint:

raven/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,9 @@ def build_msg(self, event_type, data=None, date=None,
498498
# raven client internally in sentry and the alternative
499499
# submission option of a list here is not supported by the
500500
# internal sender.
501-
data.setdefault('breadcrumbs', {'values': crumbs})
501+
data.setdefault('breadcrumbs', {
502+
'values': crumbs
503+
})
502504

503505
return data
504506

raven/breadcrumbs.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
from __future__ import absolute_import
22

33
import os
4-
import time
54
import logging
5+
6+
from time import time
67
from types import FunctionType
78

8-
from raven.utils.compat import iteritems, get_code, text_type, string_types
99
from raven.utils import once
10+
from raven.utils.encoding import to_unicode
11+
from raven.utils.compat import iteritems, get_code, text_type, string_types
12+
13+
CATEGORY_MAX_LENGTH = 128
1014

15+
LEVEL_MAX_LENGTH = 16
1116

1217
special_logging_handlers = []
1318
special_logger_handlers = {}
1419

15-
1620
logger = logging.getLogger('raven')
1721

1822

@@ -28,40 +32,59 @@ def event_payload_considered_equal(a, b):
2832

2933
class BreadcrumbBuffer(object):
3034

31-
def __init__(self, limit=100):
35+
def __init__(self, limit=100, message_max_length=1024):
3236
self.buffer = []
3337
self.limit = limit
38+
self.message_max_length = message_max_length
3439

3540
def record(self, timestamp=None, level=None, message=None,
3641
category=None, data=None, type=None, processor=None):
3742
if not (message or data or processor):
3843
raise ValueError('You must pass either `message`, `data`, '
3944
'or `processor`')
4045
if timestamp is None:
41-
timestamp = time.time()
42-
self.buffer.append(({
46+
timestamp = time()
47+
48+
# we format here to ensure we dont bloat memory due to message size
49+
result = (self.format({
4350
'type': type or 'default',
44-
'timestamp': timestamp,
51+
'timestamp': float(timestamp),
4552
'level': level,
53+
# hardcode message length to prevent huge crumbs
4654
'message': message,
4755
'category': category,
56+
# TODO(dcramer): we should trim data
4857
'data': data,
49-
}, processor))
58+
}), processor)
59+
self.buffer.append(result)
5060
del self.buffer[:-self.limit]
5161

5262
def clear(self):
5363
del self.buffer[:]
5464

65+
def format(self, result):
66+
result['message'] = to_unicode(result['message'])[:self.message_max_length] if result['message'] else None
67+
result['category'] = to_unicode(result['category'])[:CATEGORY_MAX_LENGTH] if result['category'] else None
68+
result['level'] = to_unicode(result['level'])[:LEVEL_MAX_LENGTH].lower() if result['level'] else None
69+
return result
70+
5571
def get_buffer(self):
5672
rv = []
5773
for idx, (payload, processor) in enumerate(self.buffer):
5874
if processor is not None:
5975
try:
6076
processor(payload)
6177
except Exception:
78+
raise
6279
logger.exception('Failed to process breadcrumbs. Ignored')
6380
payload = None
81+
else:
82+
# we format here to ensure we dont bloat memory due to message size
83+
payload = self.format(payload) if payload else None
6484
self.buffer[idx] = (payload, None)
85+
elif payload is not None:
86+
payload = self.format(payload)
87+
6588
if payload is not None and \
6689
(not rv or not event_payload_considered_equal(rv[-1], payload)):
6790
rv.append(payload)
@@ -92,7 +115,7 @@ def record(message=None, timestamp=None, level=None, category=None,
92115
on a specific client.
93116
"""
94117
if timestamp is None:
95-
timestamp = time.time()
118+
timestamp = time()
96119
for ctx in raven.context.get_active_contexts():
97120
ctx.breadcrumbs.record(timestamp, level, message, category,
98121
data, type, processor)

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool:pytest]
22
python_files=test*.py
3-
addopts=--tb=native -p no:doctest -p no:logging --cov=raven -nauto
3+
addopts=--tb=native -p no:doctest -p no:logging --cov=raven
44
norecursedirs=raven build bin dist docs htmlcov hooks node_modules .* {args}
55
DJANGO_SETTINGS_MODULE = tests.contrib.django.settings
66
python_paths = tests

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
tests_require = [
6060
'bottle',
6161
'celery>=2.5',
62+
'coverage<4',
6263
'exam>=0.5.2',
6364
'flake8==3.5.0',
6465
'logbook',
@@ -75,8 +76,10 @@
7576
'pytest-flake8==0.9.1',
7677
'requests',
7778
'tornado>=4.1',
79+
'tox',
7880
'webob',
7981
'webtest',
82+
'wheel',
8083
'anyjson',
8184
'ZConfig',
8285
] + (

tests/breadcrumbs/tests.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ def test_log_crumb_reporting(self):
3434
assert crumbs[0]['data'] == {'blah': 'baz'}
3535
assert crumbs[0]['message'] == 'This is a message with foo!'
3636

37+
def test_log_crumb_reporting_with_large_message(self):
38+
client = Client('http://foo:bar@example.com/0')
39+
with client.context:
40+
log = logging.getLogger('whatever.foo')
41+
log.info('a' * 4096)
42+
crumbs = client.context.breadcrumbs.get_buffer()
43+
44+
assert len(crumbs) == 1
45+
assert crumbs[0]['message'] == 'a' * 1024
46+
3747
def test_log_location(self):
3848
out = StringIO()
3949
logger = logging.getLogger(__name__)

0 commit comments

Comments
 (0)