2
2
import uuid
3
3
import random
4
4
import atexit
5
-
6
- from .utils import Dsn , SkipEvent , ContextVar
5
+ import weakref
6
+ from datetime import datetime
7
+
8
+ from ._compat import string_types
9
+ from .utils import (
10
+ strip_event ,
11
+ flatten_metadata ,
12
+ convert_types ,
13
+ handle_in_app ,
14
+ get_type_name ,
15
+ pop_hidden_keys ,
16
+ Dsn ,
17
+ )
7
18
from .transport import Transport
8
19
from .consts import DEFAULT_OPTIONS , SDK_INFO
9
- from .event import strip_event , flatten_metadata , convert_types , Event
10
20
11
21
12
22
NO_DSN = object ()
13
23
14
- _most_recent_exception = ContextVar ("sentry_most_recent_exception" )
15
24
25
+ def get_options (* args , ** kwargs ):
26
+ if args and (isinstance (args [0 ], string_types ) or args [0 ] is None ):
27
+ dsn = args [0 ]
28
+ args = args [1 :]
29
+ else :
30
+ dsn = None
31
+
32
+ rv = dict (DEFAULT_OPTIONS )
33
+ options = dict (* args , ** kwargs )
34
+ if dsn is not None and options .get ("dsn" ) is None :
35
+ options ["dsn" ] = dsn
36
+
37
+ for key , value in options .items ():
38
+ if key not in rv :
39
+ raise TypeError ("Unknown option %r" % (key ,))
40
+ rv [key ] = value
16
41
17
- def _get_default_integrations ():
18
- from .integrations .logging import LoggingIntegration
19
- from .integrations .excepthook import ExcepthookIntegration
42
+ if rv ["dsn" ] is None :
43
+ rv ["dsn" ] = os .environ .get ("SENTRY_DSN" )
20
44
21
- yield LoggingIntegration
22
- yield ExcepthookIntegration
45
+ return rv
23
46
24
47
25
48
class Client (object ):
26
- def __init__ (self , dsn = None , * args , ** kwargs ):
27
- passed_dsn = dsn
28
- if dsn is NO_DSN :
29
- dsn = None
30
- else :
31
- if dsn is None :
32
- dsn = os .environ .get ("SENTRY_DSN" )
33
- if not dsn :
34
- dsn = None
35
- else :
36
- dsn = Dsn (dsn )
37
- options = dict (DEFAULT_OPTIONS )
38
- options .update (* args , ** kwargs )
49
+ def __init__ (self , * args , ** kwargs ):
50
+ options = get_options (* args , ** kwargs )
51
+
52
+ dsn = options ["dsn" ]
53
+ if dsn is not None :
54
+ dsn = Dsn (dsn )
55
+
39
56
self .options = options
40
57
self ._transport = self .options .pop ("transport" )
41
58
if self ._transport is None and dsn is not None :
@@ -45,18 +62,6 @@ def __init__(self, dsn=None, *args, **kwargs):
45
62
https_proxy = self .options .pop ("https_proxy" ),
46
63
)
47
64
self ._transport .start ()
48
- elif passed_dsn is not None and self ._transport is not None :
49
- raise ValueError ("Cannot pass DSN and a custom transport." )
50
-
51
- integrations = list (options .pop ("integrations" ) or ())
52
-
53
- if options ["default_integrations" ]:
54
- for cls in _get_default_integrations ():
55
- if not any (isinstance (x , cls ) for x in integrations ):
56
- integrations .append (cls ())
57
-
58
- for integration in integrations :
59
- integration (self )
60
65
61
66
request_bodies = ("always" , "never" , "small" , "medium" )
62
67
if options ["request_bodies" ] not in request_bodies :
@@ -66,6 +71,8 @@ def __init__(self, dsn=None, *args, **kwargs):
66
71
)
67
72
)
68
73
74
+ self ._exceptions_seen = weakref .WeakKeyDictionary ()
75
+
69
76
atexit .register (self .close )
70
77
71
78
@property
@@ -80,11 +87,13 @@ def disabled(cls):
80
87
return cls (NO_DSN )
81
88
82
89
def _prepare_event (self , event , scope ):
83
- if event .get ("event_id " ) is None :
84
- event ["event_id " ] = uuid . uuid4 (). hex
90
+ if event .get ("timestamp " ) is None :
91
+ event ["timestamp " ] = datetime . utcnow ()
85
92
86
93
if scope is not None :
87
- scope .apply_to_event (event )
94
+ event = scope .apply_to_event (event )
95
+ if event is None :
96
+ return
88
97
89
98
for key in "release" , "environment" , "server_name" , "repos" , "dist" :
90
99
if event .get (key ) is None :
@@ -95,41 +104,67 @@ def _prepare_event(self, event, scope):
95
104
if event .get ("platform" ) is None :
96
105
event ["platform" ] = "python"
97
106
107
+ event = handle_in_app (
108
+ event , self .options ["in_app_exclude" ], self .options ["in_app_include" ]
109
+ )
98
110
event = strip_event (event )
99
- event = flatten_metadata (event )
100
- event = convert_types (event )
111
+
112
+ before_send = self .options ["before_send" ]
113
+ if before_send is not None :
114
+ event = before_send (event )
115
+
116
+ if event is not None :
117
+ pop_hidden_keys (event )
118
+ event = flatten_metadata (event )
119
+ event = convert_types (event )
120
+
101
121
return event
102
122
103
- def _check_should_capture (self , event ):
123
+ def _is_ignored_error (self , event ):
124
+ exc_info = event .get ("__sentry_exc_info" )
125
+
126
+ if not exc_info or exc_info [0 ] is None :
127
+ return False
128
+
129
+ type_name = get_type_name (exc_info [0 ])
130
+ full_name = "%s.%s" % (exc_info [0 ].__module__ , type_name )
131
+
132
+ for errcls in self .options ["ignore_errors" ]:
133
+ # String types are matched against the type name in the
134
+ # exception only
135
+ if isinstance (errcls , string_types ):
136
+ if errcls == full_name or errcls == type_name :
137
+ return True
138
+ else :
139
+ if issubclass (exc_info [0 ], errcls ):
140
+ return True
141
+
142
+ return False
143
+
144
+ def _should_capture (self , event , scope = None ):
104
145
if (
105
146
self .options ["sample_rate" ] < 1.0
106
147
and random .random () >= self .options ["sample_rate" ]
107
148
):
108
- raise SkipEvent ()
149
+ return False
109
150
110
- if event ._exc_value is not None :
111
- exclusions = self .options ["ignore_errors" ]
112
- exc_type = type (event ._exc_value )
151
+ if self ._is_ignored_error (event ):
152
+ return False
113
153
114
- if any (issubclass (exc_type , e ) for e in exclusions ):
115
- raise SkipEvent ()
116
-
117
- if _most_recent_exception .get (None ) is event ._exc_value :
118
- raise SkipEvent ()
119
- _most_recent_exception .set (event ._exc_value )
154
+ return True
120
155
121
156
def capture_event (self , event , scope = None ):
122
157
"""Captures an event."""
123
158
if self ._transport is None :
124
159
return
125
- if not isinstance ( event , Event ):
126
- event = Event ( event )
127
- try :
128
- self ._check_should_capture (event )
160
+ rv = event . get ( "event_id" )
161
+ if rv is None :
162
+ event [ "event_id" ] = rv = uuid . uuid4 (). hex
163
+ if self ._should_capture (event , scope ):
129
164
event = self ._prepare_event (event , scope )
130
- except SkipEvent :
131
- return
132
- self . _transport . capture_event ( event )
165
+ if event is not None :
166
+ self . _transport . capture_event ( event )
167
+ return True
133
168
134
169
def drain_events (self , timeout = None ):
135
170
if timeout is None :
0 commit comments