From cc22db1000502a7178a7aa7a0fe01471bacf1b41 Mon Sep 17 00:00:00 2001 From: Cory Virok Date: Mon, 28 Dec 2015 15:35:59 -0800 Subject: [PATCH 1/2] Add safe_repr option to serializer and expose serializer's whitelist_types param This allows users to configure Rollbar to use `repr(obj)` during the serialization stage. @jfarrimo cc @brianr --- README.md | 3 +++ rollbar/__init__.py | 6 +++-- rollbar/lib/transforms/serializable.py | 11 ++++++++- rollbar/test/test_serializable_transform.py | 26 ++++++++++++++++++--- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f193c019..474ad9e9 100644 --- a/README.md +++ b/README.md @@ -396,6 +396,9 @@ Default: ```thread```
Default 100
+
whitelisted_types
+
A list of `type` objects, (e.g. `type(my_class_instance)` or `MyClass`) that will be serialized using + `repr()`. Default `[]`
root
diff --git a/rollbar/__init__.py b/rollbar/__init__.py index fcce1aa4..27f7ad2c 100644 --- a/rollbar/__init__.py +++ b/rollbar/__init__.py @@ -287,7 +287,8 @@ def _get_pylons_request(): 'locals': { 'enabled': True, 'safe_repr': True, - 'sizes': DEFAULT_LOCALS_SIZES + 'sizes': DEFAULT_LOCALS_SIZES, + 'whitelisted_types': [] }, 'verify_https': True } @@ -331,7 +332,8 @@ def init(access_token, environment='production', **kw): # 3. Scrub URLs in the payload for keys that end with 'url' # 4. Optional - If local variable gathering is enabled, transform the # trace frame values using the ShortReprTransform. - _serialize_transform = SerializableTransform() + _serialize_transform = SerializableTransform(safe_repr=SETTINGS['locals']['safe_repr'], + whitelist_types=SETTINGS['locals']['whitelisted_types']) _transforms = [ _serialize_transform, ScrubTransform(suffixes=[(field,) for field in SETTINGS['scrub_fields']], redact_char='*'), diff --git a/rollbar/lib/transforms/serializable.py b/rollbar/lib/transforms/serializable.py index 231efd42..6c27f983 100644 --- a/rollbar/lib/transforms/serializable.py +++ b/rollbar/lib/transforms/serializable.py @@ -8,8 +8,9 @@ class SerializableTransform(Transform): - def __init__(self, whitelist_types=None): + def __init__(self, safe_repr=True, whitelist_types=None): super(SerializableTransform, self).__init__() + self.safe_repr = safe_repr self.whitelist = set(whitelist_types or []) def transform_circular_reference(self, o, key=None, ref_key=None): @@ -80,6 +81,14 @@ def transform_custom(self, o, key=None): except TypeError: pass + # If self.safe_repr is False, use repr() to serialize the object + if not self.safe_repr: + try: + return repr(o) + except TypeError: + pass + + # Otherwise, just use the type name return str(type(o)) diff --git a/rollbar/test/test_serializable_transform.py b/rollbar/test/test_serializable_transform.py index eaee0c70..b64bd314 100644 --- a/rollbar/test/test_serializable_transform.py +++ b/rollbar/test/test_serializable_transform.py @@ -24,8 +24,8 @@ class SerializableTransformTest(BaseTest): - def _assertSerialized(self, start, expected, whitelist=None, skip_id_check=False): - serializable = SerializableTransform(whitelist_types=whitelist) + def _assertSerialized(self, start, expected, safe_repr=True, whitelist=None, skip_id_check=False): + serializable = SerializableTransform(safe_repr=safe_repr, whitelist_types=whitelist) result = transforms.transform(start, [serializable]) """ @@ -152,7 +152,27 @@ def test_encode_dict_with_bytes_key(self): del expected[invalid] self._assertSerialized(start, expected) - def test_encode_with_custom_repr(self): + def test_encode_with_custom_repr_no_whitelist(self): + class CustomRepr(object): + def __repr__(self): + return 'hello' + + start = {'hello': 'world', 'custom': CustomRepr()} + expected = copy.deepcopy(start) + expected['custom'] = str(CustomRepr) + self._assertSerialized(start, expected) + + def test_encode_with_custom_repr_no_whitelist_no_safe_repr(self): + class CustomRepr(object): + def __repr__(self): + return 'hello' + + start = {'hello': 'world', 'custom': CustomRepr()} + expected = copy.deepcopy(start) + expected['custom'] = 'hello' + self._assertSerialized(start, expected, safe_repr=False) + + def test_encode_with_custom_repr_whitelist(self): class CustomRepr(object): def __repr__(self): return 'hello' From 6423b3fcd732370b1e90e983789030efac06b7d5 Mon Sep 17 00:00:00 2001 From: Cory Virok Date: Mon, 28 Dec 2015 15:41:07 -0800 Subject: [PATCH 2/2] Fix unittest for new locals setting --- rollbar/test/test_rollbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollbar/test/test_rollbar.py b/rollbar/test/test_rollbar.py index d3d1744c..b1fa0538 100644 --- a/rollbar/test/test_rollbar.py +++ b/rollbar/test/test_rollbar.py @@ -36,7 +36,7 @@ def setUp(self): rollbar.init(_test_access_token, locals={'enabled': True}, dummy_key='asdf', handler='blocking', timeout=12345) def test_merged_settings(self): - expected = {'enabled': True, 'sizes': rollbar.DEFAULT_LOCALS_SIZES, 'safe_repr': True} + expected = {'enabled': True, 'sizes': rollbar.DEFAULT_LOCALS_SIZES, 'safe_repr': True, 'whitelisted_types': []} self.assertDictEqual(rollbar.SETTINGS['locals'], expected) self.assertEqual(rollbar.SETTINGS['timeout'], 12345) self.assertEqual(rollbar.SETTINGS['dummy_key'], 'asdf')