6
6
import uuid
7
7
from datetime import datetime , timezone
8
8
9
+ from sentry_sdk .consts import VERSION
9
10
from sentry_sdk .envelope import Envelope
10
11
from sentry_sdk ._lru_cache import LRUCache
11
12
from sentry_sdk ._types import TYPE_CHECKING
31
32
from typing import Type
32
33
from typing import Union
33
34
from typing_extensions import TypedDict
34
- from sentry_sdk ._types import ContinuousProfilerMode
35
+ from sentry_sdk ._types import ContinuousProfilerMode , SDKInfo
35
36
from sentry_sdk .profiler .utils import (
36
37
ExtractedSample ,
37
38
FrameId ,
65
66
_scheduler = None # type: Optional[ContinuousScheduler]
66
67
67
68
68
- def setup_continuous_profiler (options , capture_func ):
69
- # type: (Dict[str, Any], Callable[[Envelope], None]) -> bool
69
+ def setup_continuous_profiler (options , sdk_info , capture_func ):
70
+ # type: (Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> bool
70
71
global _scheduler
71
72
72
73
if _scheduler is not None :
@@ -91,9 +92,13 @@ def setup_continuous_profiler(options, capture_func):
91
92
frequency = DEFAULT_SAMPLING_FREQUENCY
92
93
93
94
if profiler_mode == ThreadContinuousScheduler .mode :
94
- _scheduler = ThreadContinuousScheduler (frequency , options , capture_func )
95
+ _scheduler = ThreadContinuousScheduler (
96
+ frequency , options , sdk_info , capture_func
97
+ )
95
98
elif profiler_mode == GeventContinuousScheduler .mode :
96
- _scheduler = GeventContinuousScheduler (frequency , options , capture_func )
99
+ _scheduler = GeventContinuousScheduler (
100
+ frequency , options , sdk_info , capture_func
101
+ )
97
102
else :
98
103
raise ValueError ("Unknown continuous profiler mode: {}" .format (profiler_mode ))
99
104
@@ -162,10 +167,11 @@ def get_profiler_id():
162
167
class ContinuousScheduler (object ):
163
168
mode = "unknown" # type: ContinuousProfilerMode
164
169
165
- def __init__ (self , frequency , options , capture_func ):
166
- # type: (int, Dict[str, Any], Callable[[Envelope], None]) -> None
170
+ def __init__ (self , frequency , options , sdk_info , capture_func ):
171
+ # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None
167
172
self .interval = 1.0 / frequency
168
173
self .options = options
174
+ self .sdk_info = sdk_info
169
175
self .capture_func = capture_func
170
176
self .sampler = self .make_sampler ()
171
177
self .buffer = None # type: Optional[ProfileBuffer]
@@ -194,7 +200,7 @@ def pause(self):
194
200
def reset_buffer (self ):
195
201
# type: () -> None
196
202
self .buffer = ProfileBuffer (
197
- self .options , PROFILE_BUFFER_SECONDS , self .capture_func
203
+ self .options , self . sdk_info , PROFILE_BUFFER_SECONDS , self .capture_func
198
204
)
199
205
200
206
@property
@@ -266,9 +272,9 @@ class ThreadContinuousScheduler(ContinuousScheduler):
266
272
mode = "thread" # type: ContinuousProfilerMode
267
273
name = "sentry.profiler.ThreadContinuousScheduler"
268
274
269
- def __init__ (self , frequency , options , capture_func ):
270
- # type: (int, Dict[str, Any], Callable[[Envelope], None]) -> None
271
- super ().__init__ (frequency , options , capture_func )
275
+ def __init__ (self , frequency , options , sdk_info , capture_func ):
276
+ # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None
277
+ super ().__init__ (frequency , options , sdk_info , capture_func )
272
278
273
279
self .thread = None # type: Optional[threading.Thread]
274
280
self .pid = None # type: Optional[int]
@@ -341,13 +347,13 @@ class GeventContinuousScheduler(ContinuousScheduler):
341
347
342
348
mode = "gevent" # type: ContinuousProfilerMode
343
349
344
- def __init__ (self , frequency , options , capture_func ):
345
- # type: (int, Dict[str, Any], Callable[[Envelope], None]) -> None
350
+ def __init__ (self , frequency , options , sdk_info , capture_func ):
351
+ # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None
346
352
347
353
if ThreadPool is None :
348
354
raise ValueError ("Profiler mode: {} is not available" .format (self .mode ))
349
355
350
- super ().__init__ (frequency , options , capture_func )
356
+ super ().__init__ (frequency , options , sdk_info , capture_func )
351
357
352
358
self .thread = None # type: Optional[_ThreadPool]
353
359
self .pid = None # type: Optional[int]
@@ -405,9 +411,10 @@ def teardown(self):
405
411
406
412
407
413
class ProfileBuffer (object ):
408
- def __init__ (self , options , buffer_size , capture_func ):
409
- # type: (Dict[str, Any], int, Callable[[Envelope], None]) -> None
414
+ def __init__ (self , options , sdk_info , buffer_size , capture_func ):
415
+ # type: (Dict[str, Any], SDKInfo, int, Callable[[Envelope], None]) -> None
410
416
self .options = options
417
+ self .sdk_info = sdk_info
411
418
self .buffer_size = buffer_size
412
419
self .capture_func = capture_func
413
420
@@ -445,7 +452,7 @@ def should_flush(self, monotonic_time):
445
452
446
453
def flush (self ):
447
454
# type: () -> None
448
- chunk = self .chunk .to_json (self .profiler_id , self .options )
455
+ chunk = self .chunk .to_json (self .profiler_id , self .options , self . sdk_info )
449
456
envelope = Envelope ()
450
457
envelope .add_profile_chunk (chunk )
451
458
self .capture_func (envelope )
@@ -491,8 +498,8 @@ def write(self, ts, sample):
491
498
# When this happens, we abandon the current sample as it's bad.
492
499
capture_internal_exception (sys .exc_info ())
493
500
494
- def to_json (self , profiler_id , options ):
495
- # type: (str, Dict[str, Any]) -> Dict[str, Any]
501
+ def to_json (self , profiler_id , options , sdk_info ):
502
+ # type: (str, Dict[str, Any], SDKInfo ) -> Dict[str, Any]
496
503
profile = {
497
504
"frames" : self .frames ,
498
505
"stacks" : self .stacks ,
@@ -514,6 +521,10 @@ def to_json(self, profiler_id, options):
514
521
515
522
payload = {
516
523
"chunk_id" : self .chunk_id ,
524
+ "client_sdk" : {
525
+ "name" : sdk_info ["name" ],
526
+ "version" : VERSION ,
527
+ },
517
528
"platform" : "python" ,
518
529
"profile" : profile ,
519
530
"profiler_id" : profiler_id ,
0 commit comments