|
1 | | -from pymongo import monitoring |
2 | | -import sentry_sdk |
3 | | -from sentry_sdk.integrations import Integration |
4 | | - |
5 | | - |
6 | | -class MongoIntegration(Integration): |
7 | | - """ |
8 | | - The sentry_sdk integration of MongoDB. |
9 | | - """ |
10 | | - |
11 | | - identifier = "mongodb" |
12 | | - |
13 | | - @staticmethod |
14 | | - def setup_once(): |
15 | | - monitoring.register(MongoCommandListener()) |
16 | | - |
17 | | - |
18 | | -class MongoCommandListener(monitoring.CommandListener): |
19 | | - """ |
20 | | - Create spans using Mongo command listener. |
21 | | -
|
22 | | - This class handles and store the context of spans, at each executed mongo command we create |
23 | | - a new span and store it for later use. At the end of the command life, we retrieve this span and close it. |
24 | | - """ |
25 | | - |
26 | | - _commands_to_listen = ["updates", "deletes", "documents"] |
27 | | - _operations_to_listen = ["filter", "limit", "sort", "skip", "pipeline", "query"] |
28 | | - """ |
29 | | - These are the type of mongo commands/operators that are gonna to be written to a span. |
30 | | - The _commands_to_listen refers to commands that involves tampering with multiple objets. |
31 | | - The _operations_to_listen refers to operations in general that we want to have information saved. |
32 | | - We made this bit so we can keep track of the number of documents affected by the _commands_to_listen, |
33 | | - while _operations_to_listen we wanted to store individual information of any operator involved in the command, |
34 | | - e.g. how many objects to limit. |
35 | | - """ |
36 | | - |
37 | | - def __init__(self): |
38 | | - self._spans = dict() |
39 | | - """ |
40 | | - Make sure that every class instance has it's own dict of spans; |
41 | | - Keys on this dict should represent the event.request_id object; |
42 | | - """ |
43 | | - |
44 | | - def started(self, event): |
45 | | - """ |
46 | | - Here we gather info about the event, create a new span and store it on memory. |
47 | | - """ |
48 | | - |
49 | | - def get_span(event): |
50 | | - cmd_type = str(event.command_name) |
51 | | - command = str(event.command.get(cmd_type)) |
52 | | - description = f"mongodb.{command}.{cmd_type}" |
53 | | - data = {} |
54 | | - |
55 | | - """Builds span description""" |
56 | | - for atr in self._operations_to_listen: |
57 | | - value = event.command.get(atr) |
58 | | - if value is not None: |
59 | | - data[atr] = str(value) |
60 | | - description += f".{atr}" |
61 | | - |
62 | | - if event.command.get("collection"): |
63 | | - """Concatenates collection name to description""" |
64 | | - data["collection"] = str(event.command["collection"]) |
65 | | - description += f".{data['collection']}" |
66 | | - |
67 | | - for atr in self._commands_to_listen: |
68 | | - """Gather information about the number of documents affected""" |
69 | | - value = event.command.get(atr) |
70 | | - if value is not None: |
71 | | - data[f"number of documents affected by {atr}"] = len(value) |
72 | | - data[atr] = str(value) |
73 | | - |
74 | | - span = sentry_sdk.start_span(op="mongodb", description=description) |
75 | | - |
76 | | - """Add tags to the span""" |
77 | | - span.set_tag("db.type", "mongodb") |
78 | | - span.set_tag("db.instance", str(event.database_name)) |
79 | | - span.set_tag("db.statement", command) |
80 | | - span.set_tag("request_id", str(event.request_id)) |
81 | | - span.set_tag("connection_id", str(event.connection_id)) |
82 | | - |
83 | | - for k, v in data.items(): |
84 | | - """Append our gathered data to the span""" |
85 | | - span.set_data(k, v) |
86 | | - return span |
87 | | - |
88 | | - span = get_span(event) |
89 | | - span = span.__enter__() |
90 | | - |
91 | | - if span is not None: |
92 | | - """Save the span in memory so we can latter use it""" |
93 | | - self._spans[event.request_id] = span |
94 | | - |
95 | | - def succeeded(self, event): |
96 | | - self._stop("ok", event) |
97 | | - |
98 | | - def failed(self, event): |
99 | | - self._stop("internal_error", event) |
100 | | - |
101 | | - def _stop(self, status, event): |
102 | | - """Finishes span context and removes it from the memory""" |
103 | | - span = self._spans.get(event.request_id, None) |
104 | | - if span is not None: |
105 | | - span.set_status(status) |
106 | | - span.__exit__(None, None, None) |
107 | | - del self._spans[event.request_id] |
108 | | - |
109 | | - |
110 | 1 | if __name__ == "__main__": |
111 | 2 | pass |
0 commit comments