1
|
|
|
from plugin.core.constants import PLUGIN_VERSION_BASE, PLUGIN_VERSION_BRANCH |
2
|
|
|
from plugin.core.helpers.error import ErrorHasher |
3
|
|
|
from plugin.core.logger.filters import RequestsFilter |
4
|
|
|
|
5
|
|
|
from raven import Client |
6
|
|
|
from raven.handlers.logging import SentryHandler, RESERVED |
7
|
|
|
from raven.utils import six |
8
|
|
|
from raven.utils.stacks import iter_stack_frames, label_from_frame |
9
|
|
|
import datetime |
10
|
|
|
import logging |
11
|
|
|
import platform |
12
|
|
|
|
13
|
|
|
log = logging.getLogger(__name__) |
14
|
|
|
|
15
|
|
|
|
16
|
|
|
VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_BASE]) |
17
|
|
|
|
18
|
|
|
PARAMS = { |
19
|
|
|
# Message processors + filters |
20
|
|
|
'processors': [ |
21
|
|
|
'raven.processors.RemoveStackLocalsProcessor', |
22
|
|
|
'plugin.raven.processors.RelativePathProcessor' |
23
|
|
|
], |
24
|
|
|
|
25
|
|
|
# Plugin + System details |
26
|
|
|
'release': VERSION, |
27
|
|
|
'tags': { |
28
|
|
|
# Plugin |
29
|
|
|
'plugin.version': VERSION, |
30
|
|
|
'plugin.branch': PLUGIN_VERSION_BRANCH, |
31
|
|
|
|
32
|
|
|
# System |
33
|
|
|
'os.system': platform.system(), |
34
|
|
|
'os.release': platform.release(), |
35
|
|
|
'os.version': platform.version() |
36
|
|
|
} |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
|
40
|
|
|
class ErrorReporter(Client): |
41
|
|
|
server = 'sentry.skipthe.net' |
42
|
|
|
key = '6bd64b4a32ac4ce280e809db6845210d:c7051e89810f4f4f9e543a0f142f533b' |
43
|
|
|
project = 1 |
44
|
|
|
|
45
|
|
|
def __init__(self, dsn=None, raise_send_errors=False, **options): |
46
|
|
|
# Build URI |
47
|
|
|
if dsn is None: |
48
|
|
|
dsn = self.build_dsn() |
49
|
|
|
|
50
|
|
|
# Construct raven client |
51
|
|
|
super(ErrorReporter, self).__init__(dsn, raise_send_errors, **options) |
52
|
|
|
|
53
|
|
|
def build_dsn(self, protocol='requests+http'): |
54
|
|
|
return '%s://%s@%s/%s' % ( |
55
|
|
|
protocol, |
56
|
|
|
self.key, |
57
|
|
|
self.server, |
58
|
|
|
self.project |
59
|
|
|
) |
60
|
|
|
|
61
|
|
|
def set_protocol(self, protocol): |
62
|
|
|
# Build new DSN URI |
63
|
|
|
dsn = self.build_dsn(protocol) |
64
|
|
|
|
65
|
|
|
# Update client DSN |
66
|
|
|
self.set_dsn(dsn) |
67
|
|
|
|
68
|
|
|
|
69
|
|
|
class ErrorReporterHandler(SentryHandler): |
70
|
|
|
def _emit(self, record, **kwargs): |
71
|
|
|
data = { |
72
|
|
|
'user': {'id': self.client.name} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
extra = getattr(record, 'data', None) |
76
|
|
|
if not isinstance(extra, dict): |
77
|
|
|
if extra: |
78
|
|
|
extra = {'data': extra} |
79
|
|
|
else: |
80
|
|
|
extra = {} |
81
|
|
|
|
82
|
|
|
for k, v in six.iteritems(vars(record)): |
83
|
|
|
if k in RESERVED: |
84
|
|
|
continue |
85
|
|
|
if k.startswith('_'): |
86
|
|
|
continue |
87
|
|
|
if '.' not in k and k not in ('culprit', 'server_name'): |
88
|
|
|
extra[k] = v |
89
|
|
|
else: |
90
|
|
|
data[k] = v |
91
|
|
|
|
92
|
|
|
stack = getattr(record, 'stack', None) |
93
|
|
|
if stack is True: |
94
|
|
|
stack = iter_stack_frames() |
95
|
|
|
|
96
|
|
|
if stack: |
97
|
|
|
stack = self._get_targetted_stack(stack, record) |
98
|
|
|
|
99
|
|
|
date = datetime.datetime.utcfromtimestamp(record.created) |
100
|
|
|
event_type = 'raven.events.Message' |
101
|
|
|
handler_kwargs = { |
102
|
|
|
'params': record.args, |
103
|
|
|
} |
104
|
|
|
try: |
105
|
|
|
handler_kwargs['message'] = six.text_type(record.msg) |
106
|
|
|
except UnicodeDecodeError: |
107
|
|
|
# Handle binary strings where it should be unicode... |
108
|
|
|
handler_kwargs['message'] = repr(record.msg)[1:-1] |
109
|
|
|
|
110
|
|
|
try: |
111
|
|
|
handler_kwargs['formatted'] = six.text_type(record.message) |
112
|
|
|
except UnicodeDecodeError: |
113
|
|
|
# Handle binary strings where it should be unicode... |
114
|
|
|
handler_kwargs['formatted'] = repr(record.message)[1:-1] |
115
|
|
|
|
116
|
|
|
# If there's no exception being processed, exc_info may be a 3-tuple of None |
117
|
|
|
# http://docs.python.org/library/sys.html#sys.exc_info |
118
|
|
|
exception_hash = None |
119
|
|
|
|
120
|
|
|
if record.exc_info and all(record.exc_info): |
121
|
|
|
# capture the standard message first so that we ensure |
122
|
|
|
# the event is recorded as an exception, in addition to having our |
123
|
|
|
# message interface attached |
124
|
|
|
handler = self.client.get_handler(event_type) |
125
|
|
|
data.update(handler.capture(**handler_kwargs)) |
126
|
|
|
|
127
|
|
|
event_type = 'raven.events.Exception' |
128
|
|
|
handler_kwargs = {'exc_info': record.exc_info} |
129
|
|
|
|
130
|
|
|
# Calculate exception hash |
131
|
|
|
exception_hash = ErrorHasher.hash(exc_info=record.exc_info) |
132
|
|
|
|
133
|
|
|
# HACK: discover a culprit when we normally couldn't |
134
|
|
|
elif not (data.get('stacktrace') or data.get('culprit')) and (record.name or record.funcName): |
135
|
|
|
culprit = label_from_frame({'module': record.name, 'function': record.funcName}) |
136
|
|
|
if culprit: |
137
|
|
|
data['culprit'] = culprit |
138
|
|
|
|
139
|
|
|
data['level'] = record.levelno |
140
|
|
|
data['logger'] = record.name |
141
|
|
|
|
142
|
|
|
# Store record `tags` in message |
143
|
|
|
if hasattr(record, 'tags'): |
144
|
|
|
kwargs['tags'] = record.tags |
145
|
|
|
|
146
|
|
|
if exception_hash: |
147
|
|
|
# Store `exception_hash` in message |
148
|
|
|
if 'tags' not in kwargs: |
149
|
|
|
kwargs['tags'] = {} |
150
|
|
|
|
151
|
|
|
kwargs['tags']['exception.hash'] = exception_hash |
152
|
|
|
|
153
|
|
|
kwargs.update(handler_kwargs) |
154
|
|
|
|
155
|
|
|
return self.client.capture( |
156
|
|
|
event_type, stack=stack, data=data, |
157
|
|
|
extra=extra, date=date, **kwargs |
158
|
|
|
) |
159
|
|
|
|
160
|
|
|
|
161
|
|
|
# Build client |
162
|
|
|
RAVEN = ErrorReporter(**PARAMS) |
163
|
|
|
|
164
|
|
|
# Construct logging handler |
165
|
|
|
ERROR_REPORTER_HANDLER = ErrorReporterHandler(RAVEN, level=logging.ERROR) |
166
|
|
|
ERROR_REPORTER_HANDLER.addFilter(RequestsFilter()) |
167
|
|
|
|