Test Failed
Push — develop ( 2f2c0e...2adb06 )
by Dean
02:41
created

ErrorReporter.build_dsn()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
cc 1
dl 0
loc 6
rs 9.4285
ccs 0
cts 4
cp 0
crap 2
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