@@ -49,6 +49,8 @@ def _unescape_help(text):
49
49
50
50
def _parse_value (value ):
51
51
value = '' .join (value )
52
+ if value != value .strip ():
53
+ raise ValueError ("Invalid value: {0!r}" .format (value ))
52
54
try :
53
55
return int (value )
54
56
except ValueError :
@@ -59,6 +61,8 @@ def _parse_timestamp(timestamp):
59
61
timestamp = '' .join (timestamp )
60
62
if not timestamp :
61
63
return None
64
+ if timestamp != timestamp .strip ():
65
+ raise ValueError ("Invalid timestamp: {0!r}" .format (timestamp ))
62
66
try :
63
67
# Simple int.
64
68
return core .Timestamp (int (timestamp ), 0 )
@@ -138,11 +142,13 @@ def _parse_sample(text):
138
142
value = []
139
143
timestamp = []
140
144
labels = {}
145
+ exemplar_value = []
146
+ exemplar_timestamp = []
147
+ exemplar_labels = None
141
148
142
149
state = 'name'
143
150
144
151
it = iter (text )
145
-
146
152
for char in it :
147
153
if state == 'name' :
148
154
if char == '{' :
@@ -159,23 +165,61 @@ def _parse_sample(text):
159
165
else :
160
166
value .append (char )
161
167
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'
165
172
else :
166
173
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 )
167
198
168
199
# Trailing space after value.
169
200
if state == 'timestamp' and not timestamp :
170
201
raise ValueError ("Invalid line: " + text )
171
202
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
+
172
211
if not value :
173
212
raise ValueError ("Invalid line: " + text )
174
213
value = '' .join (value )
175
214
val = _parse_value (value )
176
215
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 ))
177
221
178
- return core .Sample ('' .join (name ), labels , val , ts )
222
+ return core .Sample ('' .join (name ), labels , val , ts , exemplar )
179
223
180
224
181
225
def text_fd_to_metric_families (fd ):
@@ -206,6 +250,7 @@ def build_metric(name, documentation, typ, unit, samples):
206
250
# TODO: Info and stateset can't have units
207
251
# TODO: check samples are appropriately grouped and ordered
208
252
# TODO: check for metadata in middle of samples
253
+ # TODO: Check histogram bucket rules being followed
209
254
metric .samples = samples
210
255
return metric
211
256
0 commit comments