1
- import typing
2
- from typing import Optional , Sequence
1
+ from typing import Optional , Sequence , Dict
3
2
3
+ from opentelemetry .sdk .metrics ._internal .aggregation import AggregationTemporality
4
+ from opentelemetry .sdk .metrics .view import Aggregation
4
5
from opentelemetry .sdk .trace import ReadableSpan
5
6
from opentelemetry .sdk .trace .export import SpanExporter , SpanExportResult
6
7
from opentelemetry .sdk .metrics .export import MetricExporter
16
17
17
18
import socket
18
19
19
- PROTOCOL_HEADER = "{\" format\" :\" json\" ,\" version\" :1}"
20
+ DEFAULT_ENDPOINT = "127.0.0.1:2000"
21
+ PROTOCOL_HEADER = '{"format":"json","version":1}\n '
20
22
PROTOCOL_DELIMITER = '\n '
21
23
22
24
23
- class OtlpUdpExporterCommon :
25
+ class UdpExporter :
24
26
def __init__ (
25
27
self ,
26
28
endpoint : Optional [str ] = None
27
29
):
28
- self ._endpoint = endpoint or "http://127.0.0.1:2000"
29
- self ._socket = socket .socket (socket .AF_INET , socket .SOCK_DGRAM ) #TODO: What does this mean?
30
- self ._socket .setblocking (False ) #TODO: Is this correct that we don't want to block?
30
+ self ._endpoint = endpoint or DEFAULT_ENDPOINT
31
+ self ._host , self ._port = self ._parse_endpoint (self ._endpoint )
32
+ self ._socket = socket .socket (socket .AF_INET , socket .SOCK_DGRAM )
33
+ self ._socket .setblocking (False )
31
34
32
- def send_data (self , data , format ):
33
- udp_data = {
34
- "format" : format ,
35
- "data" : data
36
- }
35
+ def send_data (self , data : str , format : str ):
36
+ udp_data = f'{{"format":"{ format } ","data":{ data } }}'
37
+ message = PROTOCOL_HEADER + udp_data
37
38
38
- message = "%s%s%s" % (PROTOCOL_HEADER ,
39
- PROTOCOL_DELIMITER ,
40
- udp_data )
39
+ try :
40
+ print ("Sending UDP data: " , message ) # TODO: remove
41
+ self ._socket .sendto (message .encode ('utf-8' ), (self ._host , int (self ._port )))
42
+ except Exception as e :
43
+ print ("Error sending UDP data: " , e )
41
44
42
- print ("Sending UDP data: " , message )
43
- self ._socket .sendto (message .encode ('utf-8' ), self ._endpoint )
44
- pass
45
+ def shutdown (self ):
46
+ self ._socket .close ()
45
47
48
+ def _parse_endpoint (self , endpoint : str ) -> tuple [str , int ]:
49
+ try :
50
+ vals = endpoint .split (":" )
51
+ host = vals [0 ]
52
+ port = int (vals [1 ])
53
+ except Exception as e :
54
+ raise ValueError (f"Invalid endpoint: { endpoint } " ) from e
46
55
47
- class OtlpUdpMetricExporter (OtlpUdpExporterCommon , MetricExporter ):
48
- def shutdown (self , timeout_millis : float = 30_000 , ** kwargs ) -> None :
49
- pass
56
+ return host , port
50
57
51
- def force_flush (self , timeout_millis : float = 10_000 ) -> bool :
52
- pass
53
58
54
- def __init__ (self , endpoint = None , temporality_dict = None ):
55
- OtlpUdpExporterCommon .__init__ (self , endpoint )
56
- MetricExporter .__init__ (self , preferred_temporality = temporality_dict )
59
+ class OtlpUdpMetricExporter (MetricExporter ):
60
+ def __init__ (
61
+ self ,
62
+ endpoint : Optional [str ] = None ,
63
+ preferred_temporality : Dict [type , AggregationTemporality ] = None ,
64
+ preferred_aggregation : Dict [type , Aggregation ] = None
65
+ ):
66
+ super ().__init__ (
67
+ preferred_temporality = preferred_temporality ,
68
+ preferred_aggregation = preferred_aggregation ,
69
+ )
70
+ self ._udp_exporter = UdpExporter (endpoint = endpoint )
57
71
58
72
@override
59
73
def export (
@@ -62,26 +76,31 @@ def export(
62
76
timeout_millis : float = 10_000 ,
63
77
** kwargs ,
64
78
) -> MetricExportResult :
65
- # serialized_data = encode_metrics(metrics_data).SerializeToString()
66
- # self.send_data(data=serialized_data, format="OTEL_V1_METRICS")
67
- return MetricExportResult .SUCCESS
79
+ serialized_data = encode_metrics (metrics_data ).SerializeToString ()
80
+ self ._udp_exporter .send_data (data = serialized_data , format = "OTEL_V1_METRICS" ) # TODO: Convert to constant
81
+ return MetricExportResult .SUCCESS # TODO: send appropriate status back. Need to??
82
+
83
+ def force_flush (self , timeout_millis : float = 10_000 ) -> bool :
84
+ return True
85
+
86
+ def shutdown (self , timeout_millis : float = 30_000 , ** kwargs ) -> None :
87
+ self ._udp_exporter .shutdown ()
68
88
69
89
70
- class OtlpUdpTraceExporter (SpanExporter ):
71
- def __init__ (self , endpoint = None ):
72
- self ._exp = OtlpUdpExporterCommon (endpoint = endpoint )
90
+ class OtlpUdpSpanExporter (SpanExporter ):
91
+ def __init__ (self , endpoint : Optional [ str ] = None ):
92
+ self ._udp_exporter = UdpExporter (endpoint = endpoint )
73
93
74
94
@override
75
95
def export (self , spans : Sequence [ReadableSpan ]) -> SpanExportResult :
76
- print ("SPAN EXPORTER CALLED" )
77
96
serialized_data = encode_spans (spans ).SerializeToString ()
78
- self ._exp .send_data (data = serialized_data , format = "OTEL_V1_TRACES" )
79
- return SpanExportResult .SUCCESS
97
+ self ._udp_exporter .send_data (data = serialized_data , format = "OTEL_V1_TRACES" ) # TODO: Convert to constant
98
+ return SpanExportResult .SUCCESS # TODO: send appropriate status back. Need to??
80
99
81
100
@override
82
101
def force_flush (self , timeout_millis : int = 30000 ) -> bool :
83
102
return True
84
103
85
104
@override
86
105
def shutdown (self ) -> None :
87
- pass
106
+ self . _udp_exporter . shutdown ()
0 commit comments