29
29
from ..utils .version import version as tracer_version
30
30
from . import modules
31
31
from .constants import TELEMETRY_APM_PRODUCT
32
- from .constants import TELEMETRY_LOG_LEVEL # noqa:F401
33
32
from .constants import TELEMETRY_NAMESPACE
34
- from .constants import TELEMETRY_TYPE_DISTRIBUTION
35
- from .constants import TELEMETRY_TYPE_GENERATE_METRICS
36
33
from .constants import TELEMETRY_TYPE_LOGS
37
34
from .data import get_application
38
35
from .data import get_host_info
@@ -202,8 +199,8 @@ def __init__(self, is_periodic=True, agentless=None):
202
199
# This will occur when the agent writer starts.
203
200
self .enable ()
204
201
# Force app started for unit tests
205
- if config .FORCE_START :
206
- self ._app_started ( )
202
+ if config .FORCE_START and ( app_started := self . _app_started ()) :
203
+ self ._events_queue . append ( app_started )
207
204
if config .LOG_COLLECTION_ENABLED :
208
205
get_logger ("ddtrace" ).addHandler (DDTelemetryLogHandler (self ))
209
206
@@ -259,7 +256,17 @@ def add_event(self, payload, payload_type):
259
256
Payload types accepted by telemetry/proxy v2: app-started, app-closing, app-integrations-change
260
257
"""
261
258
if self .enable ():
262
- self ._events_queue .append ({"payload" : payload , "request_type" : payload_type })
259
+ with self ._service_lock :
260
+ self ._events_queue .append ({"payload" : payload , "request_type" : payload_type })
261
+
262
+ def add_events (self , events ):
263
+ # type: (List[Dict[str, Any]]) -> None
264
+ """
265
+ Adds a list of Telemetry events to the TelemetryWriter event buffer
266
+ """
267
+ if self .enable ():
268
+ with self ._service_lock :
269
+ self ._events_queue .extend (events )
263
270
264
271
def add_integration (self , integration_name , patched , auto_patched = None , error_msg = None , version = "" ):
265
272
# type: (str, bool, Optional[bool], Optional[str], Optional[str]) -> None
@@ -269,6 +276,9 @@ def add_integration(self, integration_name, patched, auto_patched=None, error_ms
269
276
:param str integration_name: name of patched module
270
277
:param bool auto_enabled: True if module is enabled in _monkey.PATCH_MODULES
271
278
"""
279
+ if not self .enable ():
280
+ return
281
+
272
282
# Integrations can be patched before the telemetry writer is enabled.
273
283
with self ._service_lock :
274
284
if integration_name not in self ._integrations_queue :
@@ -294,11 +304,11 @@ def add_error(self, code, msg, filename, line_number):
294
304
self ._error = (code , msg )
295
305
296
306
def _app_started (self , register_app_shutdown = True ):
297
- # type: (bool) -> None
307
+ # type: (bool) -> Optional[Dict[str, Any]]
298
308
"""Sent when TelemetryWriter is enabled or forks"""
299
309
if self ._forked or self .started :
300
310
# app-started events should only be sent by the main process
301
- return
311
+ return None
302
312
# List of configurations to be collected
303
313
304
314
self .started = True
@@ -329,10 +339,10 @@ def _app_started(self, register_app_shutdown=True):
329
339
330
340
# Reset the error after it has been reported.
331
341
self ._error = (0 , "" )
332
- self . add_event ( payload , "app-started" )
342
+ return { " payload" : payload , "request_type" : " app-started"}
333
343
334
344
def _app_heartbeat_event (self ):
335
- # type: () -> None
345
+ # type: () -> Dict[str, Any]
336
346
if config .DEPENDENCY_COLLECTION and time .monotonic () - self ._extended_time > self ._extended_heartbeat_interval :
337
347
self ._extended_time += self ._extended_heartbeat_interval
338
348
self ._app_dependencies_loaded_event ()
@@ -341,26 +351,27 @@ def _app_heartbeat_event(self):
341
351
{"name" : name , "version" : version } for name , version in self ._imported_dependencies .items ()
342
352
]
343
353
}
344
- self . add_event ( payload , "app-extended-heartbeat" )
354
+ request_type = "app-extended-heartbeat"
345
355
else :
346
- self .add_event ({}, "app-heartbeat" )
356
+ payload = {}
357
+ request_type = "app-heartbeat"
358
+ return {"payload" : payload , "request_type" : request_type }
347
359
348
360
def _app_closing_event (self ):
349
- # type: () -> None
361
+ # type: () -> Optional[Dict[str, Any]]
350
362
"""Adds a Telemetry event which notifies the agent that an application instance has terminated"""
351
363
if self ._forked :
352
364
# app-closing event should only be sent by the main process
353
- return
354
- payload = {} # type: Dict
355
- self .add_event (payload , "app-closing" )
365
+ return None
366
+ return {"payload" : {}, "request_type" : "app-closing" }
356
367
357
368
def _app_integrations_changed_event (self , integrations ):
358
- # type: (List[Dict]) -> None
369
+ # type: (List[Dict]) -> Dict
359
370
"""Adds a Telemetry event which sends a list of configured integrations to the agent"""
360
371
payload = {
361
372
"integrations" : integrations ,
362
373
}
363
- self . add_event ( payload , "app-integrations-change" )
374
+ return { " payload" : payload , "request_type" : " app-integrations-change"}
364
375
365
376
def _flush_integrations_queue (self ):
366
377
# type: () -> List[Dict]
@@ -379,46 +390,44 @@ def _flush_configuration_queue(self):
379
390
return configurations
380
391
381
392
def _app_client_configuration_changed_event (self , configurations ):
382
- # type: (List[Dict]) -> None
393
+ # type: (List[Dict]) -> Dict[str, Any]
383
394
"""Adds a Telemetry event which sends list of modified configurations to the agent"""
384
395
payload = {
385
396
"configuration" : configurations ,
386
397
}
387
- self . add_event ( payload , "app-client-configuration-change" )
398
+ return { " payload" : payload , "request_type" : " app-client-configuration-change"}
388
399
389
400
def _app_dependencies_loaded_event (self ):
401
+ # type: () -> Optional[Dict[str, Any]]
390
402
"""Adds events to report imports done since the last periodic run"""
391
-
392
403
if not config .DEPENDENCY_COLLECTION or not self ._enabled :
393
- return
404
+ return None
394
405
with self ._service_lock :
395
406
newly_imported_deps = modules .get_newly_imported_modules (self ._modules_already_imported )
396
407
397
408
if not newly_imported_deps :
398
- return
409
+ return None
399
410
400
411
with self ._service_lock :
401
- packages = update_imported_dependencies (self ._imported_dependencies , newly_imported_deps )
402
-
403
- if packages :
404
- payload = {"dependencies" : packages }
405
- self .add_event (payload , "app-dependencies-loaded" )
412
+ if packages := update_imported_dependencies (self ._imported_dependencies , newly_imported_deps ):
413
+ return {"payload" : {"dependencies" : packages }, "request_type" : "app-dependencies-loaded" }
414
+ return None
406
415
407
416
def _app_product_change (self ):
408
- # type: () -> None
417
+ # type: () -> Optional[Dict[str, Any]]
409
418
"""Adds a Telemetry event which reports the enablement of an APM product"""
410
419
411
420
if not self ._send_product_change_updates :
412
- return
421
+ return None
413
422
414
423
payload = {
415
424
"products" : {
416
425
product : {"version" : tracer_version , "enabled" : status }
417
426
for product , status in self ._product_enablement .items ()
418
427
}
419
428
}
420
- self .add_event (payload , "app-product-change" )
421
429
self ._send_product_change_updates = False
430
+ return {"payload" : payload , "request_type" : "app-product-change" }
422
431
423
432
def product_activated (self , product , enabled ):
424
433
# type: (str, bool) -> None
@@ -471,7 +480,7 @@ def add_configurations(self, configuration_list):
471
480
472
481
def add_log (self , level , message , stack_trace = "" , tags = None ):
473
482
"""
474
- Queues log. This event is meant to send library logs to Datadog’ s backend through the Telemetry intake.
483
+ Queues log. This event is meant to send library logs to Datadog' s backend through the Telemetry intake.
475
484
This will make support cycles easier and ensure we know about potentially silent issues in libraries.
476
485
"""
477
486
if tags is None :
@@ -553,7 +562,8 @@ def _flush_log_metrics(self):
553
562
self ._logs = set ()
554
563
return log_metrics
555
564
556
- def _generate_metrics_event (self , namespace_metrics ) -> None :
565
+ def _generate_metrics_event (self , namespace_metrics ):
566
+ # type: (Dict[str, Dict[str, List[Dict[str, Any]]]]) -> Optional[Dict[str, Any]]
557
567
for payload_type , namespaces in namespace_metrics .items ():
558
568
for namespace , metrics in namespaces .items ():
559
569
if metrics :
@@ -562,52 +572,56 @@ def _generate_metrics_event(self, namespace_metrics) -> None:
562
572
"series" : metrics ,
563
573
}
564
574
log .debug ("%s request payload, namespace %s" , payload_type , namespace )
565
- if payload_type == TELEMETRY_TYPE_DISTRIBUTION :
566
- self .add_event (payload , TELEMETRY_TYPE_DISTRIBUTION )
567
- elif payload_type == TELEMETRY_TYPE_GENERATE_METRICS :
568
- self .add_event (payload , TELEMETRY_TYPE_GENERATE_METRICS )
575
+ return {"payload" : payload , "request_type" : payload_type }
576
+ return None
569
577
570
578
def _generate_logs_event (self , logs ):
571
- # type: (Set[Dict[str, str]]) -> None
579
+ # type: (Set[Dict[str, str]]) -> Dict[str, Any]
572
580
log .debug ("%s request payload" , TELEMETRY_TYPE_LOGS )
573
- self . add_event ({ " logs" : list (logs )}, TELEMETRY_TYPE_LOGS )
581
+ return { "payload" : { " logs" : list (logs )}, "request_type" : TELEMETRY_TYPE_LOGS }
574
582
575
583
def periodic (self , force_flush = False , shutting_down = False ):
576
584
# ensure app_started is called at least once in case traces weren't flushed
577
- self ._app_started ()
578
- self ._app_product_change ()
585
+ events = []
586
+ if app_started := self ._app_started ():
587
+ events .append (app_started )
588
+ if app_product_change := self ._app_product_change ():
589
+ events .append (app_product_change )
579
590
580
- namespace_metrics = self ._namespace .flush (float (self .interval ))
581
- if namespace_metrics :
582
- self . _generate_metrics_event ( namespace_metrics )
591
+ if namespace_metrics : = self ._namespace .flush (float (self .interval )):
592
+ if metrics_event := self . _generate_metrics_event ( namespace_metrics ) :
593
+ events . append ( metrics_event )
583
594
584
- logs_metrics = self ._flush_log_metrics ()
585
- if logs_metrics :
586
- self ._generate_logs_event (logs_metrics )
595
+ if logs_metrics := self ._flush_log_metrics ():
596
+ events .append (self ._generate_logs_event (logs_metrics ))
587
597
588
598
# Telemetry metrics and logs should be aggregated into payloads every time periodic is called.
589
599
# This ensures metrics and logs are submitted in 10 second time buckets.
590
600
if self ._is_periodic and force_flush is False :
591
601
if self ._periodic_count < self ._periodic_threshold :
602
+ if events :
603
+ self .add_events (events )
592
604
self ._periodic_count += 1
593
605
return
594
606
self ._periodic_count = 0
595
607
596
- integrations = self ._flush_integrations_queue ()
597
- if integrations :
598
- self ._app_integrations_changed_event (integrations )
608
+ if integrations := self ._flush_integrations_queue ():
609
+ events .append (self ._app_integrations_changed_event (integrations ))
599
610
600
- configurations = self ._flush_configuration_queue ()
601
- if configurations :
602
- self ._app_client_configuration_changed_event (configurations )
611
+ if configurations := self ._flush_configuration_queue ():
612
+ events .append (self ._app_client_configuration_changed_event (configurations ))
603
613
604
- self ._app_dependencies_loaded_event ()
614
+ if app_dependencies_loaded := self ._app_dependencies_loaded_event ():
615
+ events .append (app_dependencies_loaded )
605
616
606
- if shutting_down :
607
- self . _app_closing_event ( )
617
+ if shutting_down and ( app_closing := self . _app_closing_event ()) :
618
+ events . append ( app_closing )
608
619
609
620
# Send a heartbeat event to the agent, this is required to keep RC connections alive
610
- self ._app_heartbeat_event ()
621
+ events .append (self ._app_heartbeat_event ())
622
+
623
+ if queued_events := self ._flush_events_queue ():
624
+ events .extend (queued_events )
611
625
612
626
batch_event = {
613
627
"tracer_time" : int (time .time ()),
@@ -617,7 +631,7 @@ def periodic(self, force_flush=False, shutting_down=False):
617
631
"debug" : self ._debug ,
618
632
"application" : get_application (config .SERVICE , config .VERSION , config .ENV ),
619
633
"host" : get_host_info (),
620
- "payload" : self . _flush_events_queue () ,
634
+ "payload" : events ,
621
635
"request_type" : "message-batch" ,
622
636
}
623
637
self ._client .send_event (batch_event )
@@ -702,8 +716,8 @@ def _telemetry_excepthook(self, tp, value, root_traceback):
702
716
error_msg = "{}:{} {}" .format (filename , lineno , str (value ))
703
717
self .add_integration (integration_name , True , error_msg = error_msg )
704
718
705
- if self . _enabled and not self .started :
706
- self ._app_started ( False )
719
+ if app_started := self ._app_started ( False ) :
720
+ self ._events_queue . append ( app_started )
707
721
708
722
self .app_shutdown ()
709
723
0 commit comments