Skip to content

Commit 4c0ab4f

Browse files
fix(provider)!: RaygunMessage object in on_grouping_key method (#118)
1 parent b177b27 commit 4c0ab4f

File tree

7 files changed

+82
-29
lines changed

7 files changed

+82
-29
lines changed

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,9 @@ Customer data can be passed in which will be displayed in the Raygun web app. Th
353353
Custom grouping logic
354354
---------------------
355355

356-
You can create custom exception grouping logic that overrides the automatic Raygun grouping by passing in a function that accepts one parameter using this function. The callback's one parameter is an instance of RaygunMessage (python[2/3]/raygunmsgs.py), and the callback should return a string.
356+
You can create custom exception grouping logic that overrides the automatic Raygun grouping by passing in a function that accepts one parameter using this function. The callback's one parameter is an instance of `RaygunMessage` (`python3/raygunmsgs.py`), and the callback should return a string.
357357

358-
The RaygunMessage instance contains all the error and state data that is about to be sent to the Raygun API. In your callback you can inspect this RaygunMessage, hash together the fields you want to group by, then return a string which is the grouping key.
358+
The `RaygunMessage` instance contains all the error and state data that is about to be sent to the Raygun API. In your callback you can inspect this `RaygunMessage`, hash together the fields you want to group by, then return a string which is the grouping key.
359359

360360
This string needs to be between 1 and 100 characters long. If the callback is not set or the string isn't valid, the default automatic grouping will be used.
361361

python3/raygun4py/raygunmsgs.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
USE_MULTIPROCESSING = False
1616

1717
import platform
18-
from datetime import datetime
18+
from datetime import datetime, timezone
1919

2020
from raygun4py import http_utilities
2121

@@ -129,15 +129,30 @@ def set_user(self, user):
129129
class RaygunMessage(object):
130130

131131
def __init__(self):
132-
self.occurredOn = datetime.utcnow()
132+
self.occurredOn = datetime.now(timezone.utc)
133133
self.details = {}
134134

135+
def __copy__(self):
136+
new_instance = RaygunMessage()
137+
new_instance.details = self.details.copy()
138+
new_instance.occurredOn = self.occurredOn
139+
return new_instance
140+
141+
def copy(self):
142+
return self.__copy__()
143+
135144
def get_error(self):
136145
return self.details.get("error")
137146

138147
def get_details(self):
139148
return self.details
140149

150+
def set_details(self, details):
151+
self.details = details
152+
153+
def set_error(self, error):
154+
self.details["error"] = error
155+
141156

142157
class RaygunErrorMessage(object):
143158
log = logging.getLogger(__name__)

python3/raygun4py/raygunprovider.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -295,42 +295,44 @@ def _create_message(
295295
)
296296

297297
def _transform_message(self, message):
298+
message = message.copy()
298299
message = utilities.ignore_exceptions(self.ignored_exceptions, message)
299300

300301
if message is not None:
301-
message = utilities.filter_keys(self.filtered_keys, message)
302-
message["details"]["groupingKey"] = utilities.execute_grouping_key(
302+
details = message.get_details()
303+
details = utilities.filter_keys(self.filtered_keys, details)
304+
details["groupingKey"] = utilities.execute_grouping_key(
303305
self.grouping_key_callback, message
304306
)
307+
message.set_details(details)
305308

306309
if self.before_send_callback is not None:
307-
mutated_payload = self.before_send_callback(message["details"])
310+
mutated_payload = self.before_send_callback(message.get_details())
308311

309312
if mutated_payload is not None:
310-
message["details"] = mutated_payload
313+
message.set_details(mutated_payload)
311314
else:
312315
return None
313316

314317
return message
315318

316319
def _post(self, raygunMessage):
320+
raygunMessage = raygunMessage.copy()
317321
options = {
318322
"enforce_payload_size_limit": self.enforce_payload_size_limit,
319323
"log_payload_size_limit_breaches": self.log_payload_size_limit_breaches,
320324
}
321325

322326
if (
323-
isinstance(raygunMessage["details"]["error"], raygunmsgs.RaygunErrorMessage)
327+
isinstance(raygunMessage.get_error(), raygunmsgs.RaygunErrorMessage)
324328
and "enforce_payload_size_limit" in options
325329
and options["enforce_payload_size_limit"] is True
326330
):
327-
error = jsonpickle.loads(
328-
jsonpickle.dumps(raygunMessage["details"]["error"])
329-
)
331+
error = jsonpickle.loads(jsonpickle.dumps(raygunMessage.get_error()))
330332

331333
error.check_and_modify_payload_size(options)
332334

333-
raygunMessage["details"]["error"] = error
335+
raygunMessage.set_error(error)
334336

335337
json = jsonpickle.encode(raygunMessage, unpicklable=False)
336338

python3/raygun4py/utilities.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import re
22

3-
from raygun4py import raygunmsgs
4-
53

64
def ignore_exceptions(ignored_exceptions, message):
75
classname = message.get_error().get_classname()
@@ -11,11 +9,18 @@ def ignore_exceptions(ignored_exceptions, message):
119
return message
1210

1311

14-
def filter_keys(filtered_keys, object):
15-
iteration_target = object
12+
def filter_keys(filtered_keys, obj):
13+
"""
14+
Filter keys from a dictionary.
15+
16+
Parameters:
17+
filtered_keys (list): A list of keys to filter.
18+
obj (dict): The dictionary to filter.
1619
17-
if isinstance(object, raygunmsgs.RaygunMessage):
18-
iteration_target = object.__dict__
20+
Returns:
21+
dict: The filtered dictionary.
22+
"""
23+
iteration_target = dict(obj)
1924

2025
for key in iter(iteration_target.keys()):
2126
if isinstance(iteration_target[key], dict):

python3/tests/test_raygunmsgs.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,28 @@ def test_environment_variables_are_ignored(self):
159159
self.builder.raygunMessage.details["environment"]["environmentVariables"]
160160
)
161161

162+
def test_get_error(self):
163+
self.builder.set_exception_details(
164+
raygunmsgs.RaygunErrorMessage(Exception, None, None, {})
165+
)
166+
self.assertIsNotNone(self.builder.raygunMessage.get_error())
167+
168+
def test_get_details(self):
169+
self.builder.set_exception_details(
170+
raygunmsgs.RaygunErrorMessage(Exception, None, None, {})
171+
)
172+
self.assertIsNotNone(self.builder.raygunMessage.get_details())
173+
174+
def test_set_error(self):
175+
message = raygunmsgs.RaygunMessage()
176+
message.set_error("Error")
177+
self.assertEqual(message.get_error(), "Error")
178+
179+
def test_set_details(self):
180+
message = raygunmsgs.RaygunMessage()
181+
message.set_details({"foo": "bar"})
182+
self.assertEqual(message.get_details(), {"foo": "bar"})
183+
162184

163185
class TestRaygunErrorMessage(unittest.TestCase):
164186
ONEHUNDRED_AND_FIFTY_KB = 150 * 1024

python3/tests/test_raygunprovider.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class TestGroupingKey(unittest.TestCase):
126126
def the_callback(self, raygun_message):
127127
return self.key
128128

129+
def the_callback_with_error(self, raygun_message):
130+
return raygun_message.get_error().message[:100]
131+
129132
def create_dummy_message(self):
130133
self.sender = raygunprovider.RaygunSender("apikey")
131134

@@ -134,43 +137,49 @@ def create_dummy_message(self):
134137
msg.set_exception_details(errorMessage)
135138
return msg.build()
136139

140+
def test_message_with_error(self):
141+
msg = self.create_dummy_message()
142+
self.sender.on_grouping_key(self.the_callback_with_error)
143+
msg = self.sender._transform_message(msg)
144+
self.assertEqual(msg.get_details()["groupingKey"], "Exception: None")
145+
137146
def test_groupingkey_is_not_none_with_callback(self):
138147
msg = self.create_dummy_message()
139148
self.sender.on_grouping_key(self.the_callback)
140149
self.key = "foo"
141-
self.sender._transform_message(msg)
150+
msg = self.sender._transform_message(msg)
142151

143152
self.assertIsNotNone(msg.get_details()["groupingKey"])
144153

145154
def test_groupingkey_is_set_with_callback(self):
146155
msg = self.create_dummy_message()
147156
self.sender.on_grouping_key(self.the_callback)
148157
self.key = "foo"
149-
self.sender._transform_message(msg)
158+
msg = self.sender._transform_message(msg)
150159

151160
self.assertEqual(msg.get_details()["groupingKey"], "foo")
152161

153162
def test_groupingkey_is_string_with_callback(self):
154163
msg = self.create_dummy_message()
155164
self.sender.on_grouping_key(self.the_callback)
156165
self.key = "foo"
157-
self.sender._transform_message(msg)
166+
msg = self.sender._transform_message(msg)
158167

159168
self.assertIsInstance(msg.get_details()["groupingKey"], str)
160169

161170
def test_groupingkey_is_none_when_not_string_returned_from_callback(self):
162171
msg = self.create_dummy_message()
163172
self.sender.on_grouping_key(self.the_callback)
164173
self.key = object
165-
self.sender._transform_message(msg)
174+
msg = self.sender._transform_message(msg)
166175

167176
self.assertIsNone(msg.get_details()["groupingKey"])
168177

169178
def test_groupingkey_is_none_when_empty_string_returned_from_callback(self):
170179
msg = self.create_dummy_message()
171180
self.sender.on_grouping_key(self.the_callback)
172181
self.key = ""
173-
self.sender._transform_message(msg)
182+
msg = self.sender._transform_message(msg)
174183

175184
self.assertIsNone(msg.get_details()["groupingKey"])
176185

@@ -182,7 +191,7 @@ def test_groupingkey_is_set_when_ok_length_string_returned_from_callback(self):
182191
for i in range(0, 99):
183192
self.key += "a"
184193

185-
self.sender._transform_message(msg)
194+
msg = self.sender._transform_message(msg)
186195
self.assertEqual(msg.get_details()["groupingKey"], self.key)
187196

188197
def test_groupingkey_is_none_when_too_long_string_returned_from_callback(self):
@@ -193,7 +202,7 @@ def test_groupingkey_is_none_when_too_long_string_returned_from_callback(self):
193202
for i in range(0, 100):
194203
self.key += "a"
195204

196-
self.sender._transform_message(msg)
205+
msg = self.sender._transform_message(msg)
197206
self.assertIsNone(msg.get_details()["groupingKey"])
198207

199208

python3/tests/test_utilities.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@ class TestRaygunUtilities(unittest.TestCase):
77
def test_filter_keys(self):
88
test_obj = {"foo": "bar", "baz": "qux"}
99

10-
utilities.filter_keys(["foo"], test_obj)
10+
test_obj = utilities.filter_keys(["foo"], test_obj)
1111

1212
self.assertEqual(test_obj["foo"], "<filtered>")
1313

1414
def test_filter_keys_recursive(self):
1515
test_obj = {"foo": "bar", "baz": "qux", "boo": {"foo": "qux"}}
1616

17-
utilities.filter_keys(["foo"], test_obj)
17+
test_obj = utilities.filter_keys(["foo"], test_obj)
1818

1919
self.assertEqual(test_obj["foo"], "<filtered>")
2020
self.assertEqual(test_obj["boo"]["foo"], "<filtered>")
2121

2222
def test_filter_keys_with_wildcard(self):
2323
test_obj = {"foobr": "bar", "foobz": "baz", "fooqx": "foo", "baz": "qux"}
2424

25-
utilities.filter_keys(["foo*"], test_obj)
25+
test_obj = utilities.filter_keys(["foo*"], test_obj)
2626

2727
self.assertEqual(test_obj["foobr"], "<filtered>")
2828
self.assertEqual(test_obj["foobz"], "<filtered>")

0 commit comments

Comments
 (0)