1+ import time
2+ import contextlib
3+ import inspect
4+ from typing import Any , Callable
5+
16from dlt .common .configuration .specs import RunConfiguration
2- from dlt .common .runtime .segment import init_segment , disable_segment
7+ from dlt .common .typing import TFun
8+ from dlt .common .configuration import resolve_configuration
9+ from dlt .common .runtime .segment import TEventCategory , init_segment , disable_segment , track
310
411from dlt .common .runtime .sentry import init_sentry , disable_sentry
512
613
7- _TELEMETRY_ENABLED = False
14+ _TELEMETRY_STARTED = False
815
916
1017def start_telemetry (config : RunConfiguration ) -> None :
1118 # enable telemetry only once
1219
13- global _TELEMETRY_ENABLED
14- if _TELEMETRY_ENABLED :
20+ global _TELEMETRY_STARTED
21+ if _TELEMETRY_STARTED :
1522 return
1623
1724 if config .sentry_dsn :
@@ -20,15 +27,61 @@ def start_telemetry(config: RunConfiguration) -> None:
2027 if config .dlthub_telemetry :
2128 init_segment (config )
2229
23- _TELEMETRY_ENABLED = True
30+ _TELEMETRY_STARTED = True
2431
2532
2633def stop_telemetry () -> None :
27- global _TELEMETRY_ENABLED
28- if not _TELEMETRY_ENABLED :
34+ global _TELEMETRY_STARTED
35+ if not _TELEMETRY_STARTED :
2936 return
3037
3138 disable_sentry ()
3239 disable_segment ()
3340
34- _TELEMETRY_ENABLED = False
41+ _TELEMETRY_STARTED = False
42+
43+
44+ def is_telemetry_started () -> bool :
45+ return _TELEMETRY_STARTED
46+
47+
48+ def with_telemetry (category : TEventCategory , command : str , track_before : bool , * args : str ) -> Callable [[TFun ], TFun ]:
49+ """Adds telemetry to f: TFun and add optional f *args values to `properties` of telemetry event"""
50+ def decorator (f : TFun ) -> TFun :
51+ sig : inspect .Signature = inspect .signature (f )
52+ def _wrap (* f_args : Any , ** f_kwargs : Any ) -> Any :
53+ # look for additional arguments
54+ bound_args = sig .bind (* f_args , ** f_kwargs )
55+ props = {p :bound_args .arguments [p ] for p in args if p in bound_args .arguments }
56+ start_ts = time .time ()
57+
58+ def _track (success : bool ) -> None :
59+ with contextlib .suppress (Exception ):
60+ props ["elapsed" ] = time .time () - start_ts
61+ props ["success" ] = success
62+ # resolve runtime config and init telemetry
63+ if not _TELEMETRY_STARTED :
64+ c = resolve_configuration (RunConfiguration ())
65+ start_telemetry (c )
66+ track (category , command , props )
67+
68+ # some commands should be tracked before execution
69+ if track_before :
70+ _track (True )
71+ return f (* f_args , ** f_kwargs )
72+ # some commands we track after, where we can pass the success
73+ try :
74+ rv = f (* f_args , ** f_kwargs )
75+ # if decorated function returns int, 0 is a success - used to track dlt commands
76+ if isinstance (rv , int ):
77+ success = rv == 0
78+ else :
79+ success = True
80+ _track (success )
81+ return rv
82+ except Exception :
83+ _track (False )
84+ raise
85+
86+ return _wrap # type: ignore
87+ return decorator
0 commit comments