11#!/usr/bin/env python
2+ import cProfile
23import sys
34import time
45import os
1617"""
1718
1819
20+ def family_iterator (text ):
21+ """
22+ A parsing iterator to scrape the web page of a federate endpoint of a Prometheus server.
23+ It iterates over sample_iterator.
24+ """
25+ left_index = 0
26+ text_length = len (text )
27+ while left_index < text_length :
28+ if text [left_index ] == "#" : # Metric declaration
29+ left_index += 7
30+ right_index = left_index + 1
31+ while text [right_index ] != " " : # Still in the metric name
32+ right_index += 1
33+ metric_name = text [left_index :right_index ]
34+ left_index = right_index + 1
35+ while text [left_index ] != "\n " :
36+ left_index += 1
37+
38+ yield metric_name , sample_iterator (text , [left_index ])
39+ else :
40+ left_index += 1
41+
42+
43+ def sample_iterator (text , ptr_index ):
44+ """
45+ A sample iterator used to parse the web page of a federate endpoint of a Prometheus server.
46+ It iterates over tuples composed by :
47+ - a dictionnary of labels and their values
48+ - the corresponding sample value (always float)
49+ """
50+ label_dict = {}
51+ while text [ptr_index [0 ]] != "#" :
52+ while text [ptr_index [0 ]] != "{" : # Searching for the start of the labels
53+ ptr_index [0 ] += 1
54+ ptr_index [0 ] += 1 # We point to the first letter of the first label
55+
56+ for label , value in label_iterator (text , ptr_index ):
57+ label_dict [label ] = value
58+
59+ ptr_index [0 ] += 2 # We point at the value of the sample
60+ right_index = ptr_index [0 ] + 1
61+ while text [right_index ] != " " :
62+ right_index += 1
63+ str_value = text [ptr_index [0 ]:right_index ]
64+ if str_value == "NaN" :
65+ value = 0
66+ else :
67+ value = float (str_value )
68+
69+ yield label_dict , value
70+
71+ ptr_index [0 ] = right_index
72+ while text [ptr_index [0 ]] != "\n " :
73+ ptr_index [0 ] += 1
74+ ptr_index [0 ] += 1
75+
76+
77+ def label_iterator (text , ptr_index ):
78+ while text [ptr_index [0 ]] != "}" :
79+ if text [ptr_index [0 ]] == "," :
80+ # We ensure pointing to the start of the label name
81+ ptr_index [0 ] += 1
82+
83+ # We find the name
84+ right_index = ptr_index [0 ] + 1
85+ while text [right_index ] != "=" :
86+ right_index += 1
87+ name = text [ptr_index [0 ]:right_index ]
88+ ptr_index [0 ] = right_index + 2
89+
90+ # We find the value
91+ right_index += 3
92+ while text [right_index ] != '"' :
93+ right_index += 1
94+ value = text [ptr_index [0 ]:right_index ]
95+
96+ yield name , value
97+
98+ ptr_index [0 ] = right_index + 1
99+
100+
19101class ScrapedGauge :
20102 def __init__ (self , name , labels ):
21103 self .name = name
22104 self .labels = tuple (labels [:])
23105 self ._gauge = Gauge (name , "A identified metric found during scraping the /federate web page" , list (self .labels ))
24106
25107 def update (self , label_values , value , prefix ):
108+ start_time = time .time ()
26109 self ._gauge .labels (* self ._label_value_iterator (label_values , prefix )).set (value )
110+ return time .time () - start_time
27111
28112 def _label_value_iterator (self , label_values , prefix ):
29113 for label in self .labels :
@@ -37,39 +121,36 @@ class MetricManager:
37121 def __init__ (self , prefix ):
38122 self .metrics = dict ()
39123 self .prefix = prefix + ":"
124+ self .total_set_time = 0
40125
41- def process (self , parsed_metric ):
126+ def process (self , samples , name ):
42127 """
43128 Set the values for the given family metric
44129 and create it if it has never been encountered before
45130 """
46- metric = self .metrics .get (parsed_metric . name )
131+ metric = self .metrics .get (name )
47132 if metric :
48- self ._update (metric , parsed_metric )
133+ self ._update (metric , samples )
49134 else :
50- self ._update (self ._create (parsed_metric ), parsed_metric )
135+ self ._update (self ._create (samples , name ), samples )
51136
52- def _update (self , metric , parsed_metric ):
137+ def _update (self , metric , samples ):
53138 """
54139 Analyze the parsed labels and values and update the
55140 corresponding metric
56-
57- :param metric: The Gauge object we identified
58- :param parsed_metric: The structure which contains sample datas
59141 """
60- for sample in parsed_metric . samples :
61- metric .update (sample [ 1 ], sample [ 2 ] , self .prefix )
142+ for labels , value in samples :
143+ self . total_set_time += metric .update (labels , value , self .prefix )
62144
63- def _create (self , parsed_metric ):
145+ def _create (self , samples , name ):
64146 """
65147 Analyze the parsed metric to create a proper metric
66-
67- :param parsed_metric: The structure which contains sample datas
68- :return: The newly created metric
69148 """
70- labels = tuple (parsed_metric .samples [0 ][1 ].keys ())
71- new_metric = ScrapedGauge (parsed_metric .name , labels )
72- self .metrics [parsed_metric .name ] = new_metric
149+
150+ labels , value = next (samples )
151+ new_metric = ScrapedGauge (name , tuple (labels .keys ()))
152+ self .metrics [name ] = new_metric
153+ new_metric .update (labels , value , self .prefix )
73154 return new_metric
74155
75156
@@ -100,7 +181,7 @@ def start_server(init_wait_time, threshold, port):
100181def main ():
101182 # getting the env variables
102183 server_port = os .environ .get ('INFRABOX_PORT' , 8044 )
103- distant_prom_addr = os .environ .get ('DISTANT_PROM_ADDR' , "http://localhost :9090" )
184+ distant_prom_addr = os .environ .get ('DISTANT_PROM_ADDR' , "http://local_host :9090" )
104185 instance_prefix = os .environ .get ('INSTANCE_LABEL_PREFIX' , "distant" )
105186
106187 url_to_scrape = distant_prom_addr + '''/federate?match[]={__name__=~"..*"}'''
@@ -124,12 +205,11 @@ def main():
124205 metric_manager = MetricManager (instance_prefix )
125206 while running :
126207 response = requests .get (url_to_scrape )
127- families = parser .text_string_to_metric_families (response .content .decode ("utf-8" ))
128- for family in families :
129- if family .name not in skipped_metrics :
130- metric_manager .process (family )
131-
132- time .sleep (1.9 )
208+ web_text = response .content .decode ("utf-8" )
209+ families = family_iterator (web_text )
210+ for name , samples in families :
211+ if name not in skipped_metrics :
212+ metric_manager .process (samples , name )
133213
134214
135215if __name__ == '__main__' :
0 commit comments