@@ -49,6 +49,8 @@ def _unescape_help(text):
4949
5050def _parse_value (value ):
5151 value = '' .join (value )
52+ if value != value .strip ():
53+ raise ValueError ("Invalid value: {0!r}" .format (value ))
5254 try :
5355 return int (value )
5456 except ValueError :
@@ -59,6 +61,8 @@ def _parse_timestamp(timestamp):
5961 timestamp = '' .join (timestamp )
6062 if not timestamp :
6163 return None
64+ if timestamp != timestamp .strip ():
65+ raise ValueError ("Invalid timestamp: {0!r}" .format (timestamp ))
6266 try :
6367 # Simple int.
6468 return core .Timestamp (int (timestamp ), 0 )
@@ -138,11 +142,13 @@ def _parse_sample(text):
138142 value = []
139143 timestamp = []
140144 labels = {}
145+ exemplar_value = []
146+ exemplar_timestamp = []
147+ exemplar_labels = None
141148
142149 state = 'name'
143150
144151 it = iter (text )
145-
146152 for char in it :
147153 if state == 'name' :
148154 if char == '{' :
@@ -159,23 +165,61 @@ def _parse_sample(text):
159165 else :
160166 value .append (char )
161167 elif state == 'timestamp' :
162- if char == ' ' :
163- # examplars are not supported, halt
164- break
168+ if char == '#' and not timestamp :
169+ state = 'exemplarspace'
170+ elif char == ' ' :
171+ state = 'exemplarhash'
165172 else :
166173 timestamp .append (char )
174+ elif state == 'exemplarhash' :
175+ if char == '#' :
176+ state = 'exemplarspace'
177+ else :
178+ raise ValueError ("Invalid line: " + text )
179+ elif state == 'exemplarspace' :
180+ if char == ' ' :
181+ state = 'exemplarstartoflabels'
182+ else :
183+ raise ValueError ("Invalid line: " + text )
184+ elif state == 'exemplarstartoflabels' :
185+ if char == '{' :
186+ exemplar_labels = _parse_labels (it , text )
187+ # Space has already been parsed.
188+ state = 'exemplarvalue'
189+ else :
190+ raise ValueError ("Invalid line: " + text )
191+ elif state == 'exemplarvalue' :
192+ if char == ' ' :
193+ state = 'exemplartimestamp'
194+ else :
195+ exemplar_value .append (char )
196+ elif state == 'exemplartimestamp' :
197+ exemplar_timestamp .append (char )
167198
168199 # Trailing space after value.
169200 if state == 'timestamp' and not timestamp :
170201 raise ValueError ("Invalid line: " + text )
171202
203+ # Trailing space after value.
204+ if state == 'exemplartimestamp' and not exemplar_timestamp :
205+ raise ValueError ("Invalid line: " + text )
206+
207+ # Incomplete exemplar.
208+ if state in ['exemplarhash' , 'exemplarspace' , 'exemplarstartoflabels' ]:
209+ raise ValueError ("Invalid line: " + text )
210+
172211 if not value :
173212 raise ValueError ("Invalid line: " + text )
174213 value = '' .join (value )
175214 val = _parse_value (value )
176215 ts = _parse_timestamp (timestamp )
216+ exemplar = None
217+ if exemplar_labels is not None :
218+ exemplar = core .Exemplar (exemplar_labels ,
219+ _parse_value (exemplar_value ),
220+ _parse_timestamp (exemplar_timestamp ))
177221
178- return core .Sample ('' .join (name ), labels , val , ts )
222+ return core .Sample ('' .join (name ), labels , val , ts , exemplar )
179223
180224
181225def text_fd_to_metric_families (fd ):
@@ -206,6 +250,7 @@ def build_metric(name, documentation, typ, unit, samples):
206250 # TODO: Info and stateset can't have units
207251 # TODO: check samples are appropriately grouped and ordered
208252 # TODO: check for metadata in middle of samples
253+ # TODO: Check histogram bucket rules being followed
209254 metric .samples = samples
210255 return metric
211256
0 commit comments