prometheus-client-mmap 0.7.0.beta30 → 0.7.0.beta31
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/fast_mmaped_file/extconf.rb +3 -1
- data/ext/fast_mmaped_file/fast_mmaped_file.c +160 -46
- data/ext/fast_mmaped_file/jsmn.c +313 -0
- data/ext/fast_mmaped_file/jsmn.h +76 -0
- data/lib/fast_mmaped_file.bundle +0 -0
- data/lib/prometheus/client/formats/text.rb +28 -123
- data/lib/prometheus/client/helper/entry_parser.rb +118 -0
- data/lib/prometheus/client/helper/json_parser.rb +2 -0
- data/lib/prometheus/client/helper/metrics_processing.rb +62 -0
- data/lib/prometheus/client/helper/metrics_representation.rb +52 -0
- data/lib/prometheus/client/helper/plain_file.rb +2 -0
- data/lib/prometheus/client/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e30cce5a536c75e237464f432aaa103efd53f9af
|
4
|
+
data.tar.gz: c9d27972e59162b19c9b4a71a7d35ea4c1fc849b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce6c424d737eb6b5863008e5a08be2a4172ea1df4fad9eae3246746b90503b849079b77025bc2401895686580c63b7b17e64b760f51189b12b8f0b054c45c79b
|
7
|
+
data.tar.gz: bc6977b2f00f9b0a047cdf0c38c72018cde0e1682e07070d356ee4e7385e710881cd243be6978c8738cb69851635e7bdfb97b137691fdfdf2ea81ec1a500f0a6
|
@@ -10,25 +10,7 @@
|
|
10
10
|
VALUE MMAPED_FILE = Qnil;
|
11
11
|
|
12
12
|
#define START_POSITION 8
|
13
|
-
|
14
|
-
VALUE rb_hash_has_key(VALUE hash, VALUE key);
|
15
|
-
|
16
|
-
VALUE method_get_double(VALUE self, VALUE index) {
|
17
|
-
mm_ipc *i_mm;
|
18
|
-
GetMmap(self, i_mm, MM_MODIFY);
|
19
|
-
|
20
|
-
Check_Type(index, T_FIXNUM);
|
21
|
-
size_t idx = NUM2UINT(index);
|
22
|
-
|
23
|
-
if ((i_mm->t->real + sizeof(double)) <= idx) {
|
24
|
-
rb_raise(rb_eIndexError, "index %ld out of string", idx);
|
25
|
-
}
|
26
|
-
|
27
|
-
double tmp;
|
28
|
-
|
29
|
-
memcpy(&tmp, (char *)i_mm->t->addr + idx, sizeof(double));
|
30
|
-
return DBL2NUM(tmp);
|
31
|
-
}
|
13
|
+
#define INITIAL_SIZE (2 * sizeof(int32_t))
|
32
14
|
|
33
15
|
void expand(mm_ipc *i_mm, size_t len){
|
34
16
|
int fd;
|
@@ -64,9 +46,46 @@ void expand(mm_ipc *i_mm, size_t len){
|
|
64
46
|
i_mm->t->real = len;
|
65
47
|
}
|
66
48
|
|
49
|
+
inline uint32_t padding_length(uint32_t key_length){
|
50
|
+
return 8 - (key_length + 4) % 8; // padding | 8 byte aligned
|
51
|
+
}
|
52
|
+
|
53
|
+
void save_entry(mm_ipc *i_mm, uint32_t offset, VALUE key, VALUE value){
|
54
|
+
uint32_t key_length = (uint32_t)RSTRING_LEN(key);
|
55
|
+
|
56
|
+
char *pos = (char *)i_mm->t->addr + offset;
|
57
|
+
|
58
|
+
memcpy(pos, &key_length, sizeof(uint32_t));
|
59
|
+
pos += sizeof(uint32_t);
|
60
|
+
|
61
|
+
memmove(pos, StringValuePtr(key), key_length);
|
62
|
+
pos += key_length;
|
63
|
+
|
64
|
+
memset(pos, ' ', padding_length(key_length));
|
65
|
+
pos += padding_length(key_length);
|
66
|
+
|
67
|
+
double val = NUM2DBL(value);
|
68
|
+
memcpy(pos, &val, sizeof(double));
|
69
|
+
}
|
70
|
+
|
71
|
+
inline uint32_t load_used(mm_ipc *i_mm){
|
72
|
+
uint32_t used=0;
|
73
|
+
memcpy(&used, (char *)i_mm->t->addr, sizeof(uint32_t));
|
74
|
+
|
75
|
+
if (used == 0){
|
76
|
+
used = START_POSITION;
|
77
|
+
}
|
78
|
+
return used;
|
79
|
+
}
|
80
|
+
|
81
|
+
inline void save_used(mm_ipc *i_mm, uint32_t used){
|
82
|
+
memcpy((char *)i_mm->t->addr, &used, sizeof(uint32_t));
|
83
|
+
}
|
84
|
+
|
67
85
|
VALUE method_add_entry(VALUE self, VALUE positions, VALUE key, VALUE value) {
|
68
86
|
Check_Type(positions, T_HASH);
|
69
87
|
Check_Type(key, T_STRING);
|
88
|
+
|
70
89
|
VALUE position = rb_hash_lookup(positions, key);
|
71
90
|
if (position != Qnil){
|
72
91
|
return position;
|
@@ -75,56 +94,151 @@ VALUE method_add_entry(VALUE self, VALUE positions, VALUE key, VALUE value) {
|
|
75
94
|
mm_ipc *i_mm;
|
76
95
|
GetMmap(self, i_mm, MM_MODIFY);
|
77
96
|
|
78
|
-
if ((i_mm->t->len) <
|
79
|
-
expand(i_mm,
|
97
|
+
if ((i_mm->t->len) < INITIAL_SIZE) {
|
98
|
+
expand(i_mm, INITIAL_SIZE);
|
80
99
|
}
|
81
100
|
|
82
|
-
|
83
|
-
|
84
|
-
|
101
|
+
if (i_mm->t->flag & MM_FROZEN){
|
102
|
+
rb_error_frozen("mmap");
|
103
|
+
}
|
85
104
|
|
86
|
-
if (
|
87
|
-
|
105
|
+
if (RSTRING_LEN(key) > UINT32_MAX){
|
106
|
+
rb_raise(rb_eArgError, "string length gt %u", UINT32_MAX);
|
88
107
|
}
|
89
108
|
|
90
|
-
|
91
|
-
uint32_t
|
92
|
-
|
93
|
-
uint32_t entry_length = (uint32_t)slen;
|
94
|
-
uint32_t new_used = used + sizeof(uint32_t) + entry_length + padding_length + sizeof(double);
|
109
|
+
uint32_t key_length = (uint32_t)RSTRING_LEN(key);
|
110
|
+
uint32_t value_offset = sizeof(uint32_t) + key_length + padding_length(key_length);
|
111
|
+
uint32_t entry_length = value_offset + sizeof(double);
|
95
112
|
|
96
|
-
|
113
|
+
uint32_t used = load_used(i_mm);
|
114
|
+
while (i_mm->t->len < (used + entry_length)) {
|
97
115
|
expand(i_mm, i_mm->t->len * 2);
|
98
116
|
}
|
99
117
|
|
100
|
-
|
118
|
+
save_entry(i_mm, used, key, value);
|
119
|
+
save_used(i_mm, used + entry_length);
|
120
|
+
return rb_hash_aset(positions, key, INT2NUM(used + value_offset));
|
121
|
+
}
|
101
122
|
|
102
|
-
|
123
|
+
VALUE method_get_double(VALUE self, VALUE index) {
|
124
|
+
mm_ipc *i_mm;
|
125
|
+
GetMmap(self, i_mm, MM_MODIFY);
|
103
126
|
|
104
|
-
|
105
|
-
|
127
|
+
Check_Type(index, T_FIXNUM);
|
128
|
+
size_t idx = NUM2UINT(index);
|
129
|
+
|
130
|
+
if ((i_mm->t->real + sizeof(double)) <= idx) {
|
131
|
+
rb_raise(rb_eIndexError, "index %ld out of string", idx);
|
132
|
+
}
|
106
133
|
|
107
|
-
|
108
|
-
pos += slen;
|
134
|
+
double tmp;
|
109
135
|
|
110
|
-
|
111
|
-
|
136
|
+
memcpy(&tmp, (char *)i_mm->t->addr + idx, sizeof(double));
|
137
|
+
return DBL2NUM(tmp);
|
138
|
+
}
|
112
139
|
|
113
|
-
|
114
|
-
|
140
|
+
#include <jsmn.h>
|
141
|
+
|
142
|
+
#define min(a,b) \
|
143
|
+
({ __typeof__ (a) _a = (a); \
|
144
|
+
__typeof__ (b) _b = (b); \
|
145
|
+
_a < _b ? _a : _b; })
|
146
|
+
|
147
|
+
VALUE token_to_s(char* json, jsmntok_t *t){
|
148
|
+
int len = t->end - t->start;
|
149
|
+
if (len == 0) {
|
150
|
+
return rb_str_new_literal("");
|
151
|
+
} else {
|
152
|
+
return rb_str_new(json + t->start, t->end - t->start);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
VALUE fast_json_parse(char* json, size_t json_size){
|
157
|
+
jsmn_parser parser;
|
158
|
+
jsmn_init(&parser);
|
115
159
|
|
160
|
+
jsmntok_t t[128];
|
161
|
+
int r = jsmn_parse(&parser, json, json_size, t, sizeof(t)/sizeof(t[0]));
|
116
162
|
|
117
|
-
|
118
|
-
|
163
|
+
if (r < 3) {
|
164
|
+
return Qnil;
|
165
|
+
}
|
166
|
+
|
167
|
+
VALUE labels = rb_hash_new();
|
168
|
+
int label_cnt = (r-5)/2;
|
169
|
+
//TODO: (r - 4) % 2 == 0
|
170
|
+
|
171
|
+
for(int i = 0; i < label_cnt; i++){
|
172
|
+
int key = 4 + i;
|
173
|
+
int val = 5 + label_cnt + i;
|
174
|
+
//TODO: add type checks
|
119
175
|
|
120
|
-
|
176
|
+
if (strncmp("null", json + t[val].start, min(4, t[val].end - t[val].start)) == 0) {
|
177
|
+
rb_hash_aset(labels, token_to_s(json, &t[key]), Qnil);
|
178
|
+
} else {
|
179
|
+
rb_hash_aset(labels, token_to_s(json, &t[key]), token_to_s(json, &t[val]));
|
180
|
+
}
|
181
|
+
}
|
121
182
|
|
122
|
-
return
|
183
|
+
return rb_ary_new_from_args(3, token_to_s(json, &t[1]), token_to_s(json, &t[2]), labels);
|
123
184
|
}
|
124
185
|
|
125
186
|
|
187
|
+
VALUE method_fast_json_parse(VALUE self, VALUE source){
|
188
|
+
Check_Type(source, T_STRING);
|
189
|
+
NIL_P(self);
|
190
|
+
|
191
|
+
return fast_json_parse(StringValuePtr(source), RSTRING_LEN(source));
|
192
|
+
}
|
193
|
+
|
194
|
+
VALUE method_fast_entries(VALUE self, VALUE _data){
|
195
|
+
Check_Type(_data, T_STRING);
|
196
|
+
NIL_P(self);
|
197
|
+
|
198
|
+
char *data = StringValuePtr(_data);
|
199
|
+
uint64_t data_len = RSTRING_LEN(_data);
|
200
|
+
|
201
|
+
uint32_t used;
|
202
|
+
memcpy(&used, data, sizeof(uint32_t));
|
203
|
+
|
204
|
+
uint32_t pos = START_POSITION;
|
205
|
+
|
206
|
+
while (pos < used && (pos + 2*sizeof(uint32_t)) < data_len) {
|
207
|
+
uint32_t encoded_len, first_bytes;
|
208
|
+
memcpy(&encoded_len, data + pos, sizeof(uint32_t));
|
209
|
+
pos += sizeof(uint32_t);
|
210
|
+
|
211
|
+
memcpy(&first_bytes, data + pos, sizeof(uint32_t));
|
212
|
+
if (encoded_len == 0 || first_bytes == 0){
|
213
|
+
pos += 8;
|
214
|
+
continue; // do not parse empty data
|
215
|
+
}
|
216
|
+
|
217
|
+
uint32_t value_offset = encoded_len + padding_length(encoded_len);
|
218
|
+
VALUE metric_name_labels = fast_json_parse(data + pos, encoded_len);
|
219
|
+
|
220
|
+
double value;
|
221
|
+
pos += value_offset;
|
222
|
+
memcpy(&value, data + pos, sizeof(double));
|
223
|
+
|
224
|
+
rb_yield_values(4, rb_ary_entry(metric_name_labels, 0),
|
225
|
+
rb_ary_entry(metric_name_labels, 1),
|
226
|
+
rb_ary_entry(metric_name_labels, 2),
|
227
|
+
DBL2NUM(value)
|
228
|
+
);
|
229
|
+
|
230
|
+
pos += sizeof(double);
|
231
|
+
}
|
232
|
+
|
233
|
+
return Qnil;
|
234
|
+
}
|
235
|
+
|
126
236
|
void Init_fast_mmaped_file() {
|
237
|
+
|
127
238
|
MMAPED_FILE = rb_define_module("FastMmapedFile");
|
239
|
+
rb_define_singleton_method(MMAPED_FILE, "fast_json_parse", method_fast_json_parse, 1);
|
240
|
+
rb_define_singleton_method(MMAPED_FILE, "fast_entries", method_fast_entries, 1);
|
241
|
+
|
128
242
|
rb_define_method(MMAPED_FILE, "get_double", method_get_double, 1);
|
129
243
|
rb_define_method(MMAPED_FILE, "add_entry", method_add_entry, 3);
|
130
244
|
}
|
@@ -0,0 +1,313 @@
|
|
1
|
+
#include "jsmn.h"
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Allocates a fresh unused token from the token pull.
|
5
|
+
*/
|
6
|
+
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
|
7
|
+
jsmntok_t *tokens, size_t num_tokens) {
|
8
|
+
jsmntok_t *tok;
|
9
|
+
if (parser->toknext >= num_tokens) {
|
10
|
+
return NULL;
|
11
|
+
}
|
12
|
+
tok = &tokens[parser->toknext++];
|
13
|
+
tok->start = tok->end = -1;
|
14
|
+
tok->size = 0;
|
15
|
+
#ifdef JSMN_PARENT_LINKS
|
16
|
+
tok->parent = -1;
|
17
|
+
#endif
|
18
|
+
return tok;
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Fills token type and boundaries.
|
23
|
+
*/
|
24
|
+
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
|
25
|
+
int start, int end) {
|
26
|
+
token->type = type;
|
27
|
+
token->start = start;
|
28
|
+
token->end = end;
|
29
|
+
token->size = 0;
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Fills next available token with JSON primitive.
|
34
|
+
*/
|
35
|
+
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
36
|
+
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
37
|
+
jsmntok_t *token;
|
38
|
+
int start;
|
39
|
+
|
40
|
+
start = parser->pos;
|
41
|
+
|
42
|
+
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
43
|
+
switch (js[parser->pos]) {
|
44
|
+
#ifndef JSMN_STRICT
|
45
|
+
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
46
|
+
case ':':
|
47
|
+
#endif
|
48
|
+
case '\t' : case '\r' : case '\n' : case ' ' :
|
49
|
+
case ',' : case ']' : case '}' :
|
50
|
+
goto found;
|
51
|
+
}
|
52
|
+
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
|
53
|
+
parser->pos = start;
|
54
|
+
return JSMN_ERROR_INVAL;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
#ifdef JSMN_STRICT
|
58
|
+
/* In strict mode primitive must be followed by a comma/object/array */
|
59
|
+
parser->pos = start;
|
60
|
+
return JSMN_ERROR_PART;
|
61
|
+
#endif
|
62
|
+
|
63
|
+
found:
|
64
|
+
if (tokens == NULL) {
|
65
|
+
parser->pos--;
|
66
|
+
return 0;
|
67
|
+
}
|
68
|
+
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
69
|
+
if (token == NULL) {
|
70
|
+
parser->pos = start;
|
71
|
+
return JSMN_ERROR_NOMEM;
|
72
|
+
}
|
73
|
+
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
74
|
+
#ifdef JSMN_PARENT_LINKS
|
75
|
+
token->parent = parser->toksuper;
|
76
|
+
#endif
|
77
|
+
parser->pos--;
|
78
|
+
return 0;
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Fills next token with JSON string.
|
83
|
+
*/
|
84
|
+
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
|
85
|
+
size_t len, jsmntok_t *tokens, size_t num_tokens) {
|
86
|
+
jsmntok_t *token;
|
87
|
+
|
88
|
+
int start = parser->pos;
|
89
|
+
|
90
|
+
parser->pos++;
|
91
|
+
|
92
|
+
/* Skip starting quote */
|
93
|
+
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
94
|
+
char c = js[parser->pos];
|
95
|
+
|
96
|
+
/* Quote: end of string */
|
97
|
+
if (c == '\"') {
|
98
|
+
if (tokens == NULL) {
|
99
|
+
return 0;
|
100
|
+
}
|
101
|
+
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
102
|
+
if (token == NULL) {
|
103
|
+
parser->pos = start;
|
104
|
+
return JSMN_ERROR_NOMEM;
|
105
|
+
}
|
106
|
+
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
|
107
|
+
#ifdef JSMN_PARENT_LINKS
|
108
|
+
token->parent = parser->toksuper;
|
109
|
+
#endif
|
110
|
+
return 0;
|
111
|
+
}
|
112
|
+
|
113
|
+
/* Backslash: Quoted symbol expected */
|
114
|
+
if (c == '\\' && parser->pos + 1 < len) {
|
115
|
+
int i;
|
116
|
+
parser->pos++;
|
117
|
+
switch (js[parser->pos]) {
|
118
|
+
/* Allowed escaped symbols */
|
119
|
+
case '\"': case '/' : case '\\' : case 'b' :
|
120
|
+
case 'f' : case 'r' : case 'n' : case 't' :
|
121
|
+
break;
|
122
|
+
/* Allows escaped symbol \uXXXX */
|
123
|
+
case 'u':
|
124
|
+
parser->pos++;
|
125
|
+
for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
|
126
|
+
/* If it isn't a hex character we have an error */
|
127
|
+
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
|
128
|
+
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
|
129
|
+
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
|
130
|
+
parser->pos = start;
|
131
|
+
return JSMN_ERROR_INVAL;
|
132
|
+
}
|
133
|
+
parser->pos++;
|
134
|
+
}
|
135
|
+
parser->pos--;
|
136
|
+
break;
|
137
|
+
/* Unexpected symbol */
|
138
|
+
default:
|
139
|
+
parser->pos = start;
|
140
|
+
return JSMN_ERROR_INVAL;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
parser->pos = start;
|
145
|
+
return JSMN_ERROR_PART;
|
146
|
+
}
|
147
|
+
|
148
|
+
/**
|
149
|
+
* Parse JSON string and fill tokens.
|
150
|
+
*/
|
151
|
+
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
152
|
+
jsmntok_t *tokens, unsigned int num_tokens) {
|
153
|
+
int r;
|
154
|
+
int i;
|
155
|
+
jsmntok_t *token;
|
156
|
+
int count = parser->toknext;
|
157
|
+
|
158
|
+
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
|
159
|
+
char c;
|
160
|
+
jsmntype_t type;
|
161
|
+
|
162
|
+
c = js[parser->pos];
|
163
|
+
switch (c) {
|
164
|
+
case '{': case '[':
|
165
|
+
count++;
|
166
|
+
if (tokens == NULL) {
|
167
|
+
break;
|
168
|
+
}
|
169
|
+
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
170
|
+
if (token == NULL)
|
171
|
+
return JSMN_ERROR_NOMEM;
|
172
|
+
if (parser->toksuper != -1) {
|
173
|
+
tokens[parser->toksuper].size++;
|
174
|
+
#ifdef JSMN_PARENT_LINKS
|
175
|
+
token->parent = parser->toksuper;
|
176
|
+
#endif
|
177
|
+
}
|
178
|
+
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
179
|
+
token->start = parser->pos;
|
180
|
+
parser->toksuper = parser->toknext - 1;
|
181
|
+
break;
|
182
|
+
case '}': case ']':
|
183
|
+
if (tokens == NULL)
|
184
|
+
break;
|
185
|
+
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
186
|
+
#ifdef JSMN_PARENT_LINKS
|
187
|
+
if (parser->toknext < 1) {
|
188
|
+
return JSMN_ERROR_INVAL;
|
189
|
+
}
|
190
|
+
token = &tokens[parser->toknext - 1];
|
191
|
+
for (;;) {
|
192
|
+
if (token->start != -1 && token->end == -1) {
|
193
|
+
if (token->type != type) {
|
194
|
+
return JSMN_ERROR_INVAL;
|
195
|
+
}
|
196
|
+
token->end = parser->pos + 1;
|
197
|
+
parser->toksuper = token->parent;
|
198
|
+
break;
|
199
|
+
}
|
200
|
+
if (token->parent == -1) {
|
201
|
+
if(token->type != type || parser->toksuper == -1) {
|
202
|
+
return JSMN_ERROR_INVAL;
|
203
|
+
}
|
204
|
+
break;
|
205
|
+
}
|
206
|
+
token = &tokens[token->parent];
|
207
|
+
}
|
208
|
+
#else
|
209
|
+
for (i = parser->toknext - 1; i >= 0; i--) {
|
210
|
+
token = &tokens[i];
|
211
|
+
if (token->start != -1 && token->end == -1) {
|
212
|
+
if (token->type != type) {
|
213
|
+
return JSMN_ERROR_INVAL;
|
214
|
+
}
|
215
|
+
parser->toksuper = -1;
|
216
|
+
token->end = parser->pos + 1;
|
217
|
+
break;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
/* Error if unmatched closing bracket */
|
221
|
+
if (i == -1) return JSMN_ERROR_INVAL;
|
222
|
+
for (; i >= 0; i--) {
|
223
|
+
token = &tokens[i];
|
224
|
+
if (token->start != -1 && token->end == -1) {
|
225
|
+
parser->toksuper = i;
|
226
|
+
break;
|
227
|
+
}
|
228
|
+
}
|
229
|
+
#endif
|
230
|
+
break;
|
231
|
+
case '\"':
|
232
|
+
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
233
|
+
if (r < 0) return r;
|
234
|
+
count++;
|
235
|
+
if (parser->toksuper != -1 && tokens != NULL)
|
236
|
+
tokens[parser->toksuper].size++;
|
237
|
+
break;
|
238
|
+
case '\t' : case '\r' : case '\n' : case ' ':
|
239
|
+
break;
|
240
|
+
case ':':
|
241
|
+
parser->toksuper = parser->toknext - 1;
|
242
|
+
break;
|
243
|
+
case ',':
|
244
|
+
if (tokens != NULL && parser->toksuper != -1 &&
|
245
|
+
tokens[parser->toksuper].type != JSMN_ARRAY &&
|
246
|
+
tokens[parser->toksuper].type != JSMN_OBJECT) {
|
247
|
+
#ifdef JSMN_PARENT_LINKS
|
248
|
+
parser->toksuper = tokens[parser->toksuper].parent;
|
249
|
+
#else
|
250
|
+
for (i = parser->toknext - 1; i >= 0; i--) {
|
251
|
+
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
|
252
|
+
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
253
|
+
parser->toksuper = i;
|
254
|
+
break;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
}
|
258
|
+
#endif
|
259
|
+
}
|
260
|
+
break;
|
261
|
+
#ifdef JSMN_STRICT
|
262
|
+
/* In strict mode primitives are: numbers and booleans */
|
263
|
+
case '-': case '0': case '1' : case '2': case '3' : case '4':
|
264
|
+
case '5': case '6': case '7' : case '8': case '9':
|
265
|
+
case 't': case 'f': case 'n' :
|
266
|
+
/* And they must not be keys of the object */
|
267
|
+
if (tokens != NULL && parser->toksuper != -1) {
|
268
|
+
jsmntok_t *t = &tokens[parser->toksuper];
|
269
|
+
if (t->type == JSMN_OBJECT ||
|
270
|
+
(t->type == JSMN_STRING && t->size != 0)) {
|
271
|
+
return JSMN_ERROR_INVAL;
|
272
|
+
}
|
273
|
+
}
|
274
|
+
#else
|
275
|
+
/* In non-strict mode every unquoted value is a primitive */
|
276
|
+
default:
|
277
|
+
#endif
|
278
|
+
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
279
|
+
if (r < 0) return r;
|
280
|
+
count++;
|
281
|
+
if (parser->toksuper != -1 && tokens != NULL)
|
282
|
+
tokens[parser->toksuper].size++;
|
283
|
+
break;
|
284
|
+
|
285
|
+
#ifdef JSMN_STRICT
|
286
|
+
/* Unexpected char in strict mode */
|
287
|
+
default:
|
288
|
+
return JSMN_ERROR_INVAL;
|
289
|
+
#endif
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
if (tokens != NULL) {
|
294
|
+
for (i = parser->toknext - 1; i >= 0; i--) {
|
295
|
+
/* Unmatched opened object or array */
|
296
|
+
if (tokens[i].start != -1 && tokens[i].end == -1) {
|
297
|
+
return JSMN_ERROR_PART;
|
298
|
+
}
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
return count;
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Creates a new parser based over a given buffer with an array of tokens
|
307
|
+
* available.
|
308
|
+
*/
|
309
|
+
void jsmn_init(jsmn_parser *parser) {
|
310
|
+
parser->pos = 0;
|
311
|
+
parser->toknext = 0;
|
312
|
+
parser->toksuper = -1;
|
313
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#ifndef __JSMN_H_
|
2
|
+
#define __JSMN_H_
|
3
|
+
|
4
|
+
#include <stddef.h>
|
5
|
+
|
6
|
+
#ifdef __cplusplus
|
7
|
+
extern "C" {
|
8
|
+
#endif
|
9
|
+
|
10
|
+
/**
|
11
|
+
* JSON type identifier. Basic types are:
|
12
|
+
* o Object
|
13
|
+
* o Array
|
14
|
+
* o String
|
15
|
+
* o Other primitive: number, boolean (true/false) or null
|
16
|
+
*/
|
17
|
+
typedef enum {
|
18
|
+
JSMN_UNDEFINED = 0,
|
19
|
+
JSMN_OBJECT = 1,
|
20
|
+
JSMN_ARRAY = 2,
|
21
|
+
JSMN_STRING = 3,
|
22
|
+
JSMN_PRIMITIVE = 4
|
23
|
+
} jsmntype_t;
|
24
|
+
|
25
|
+
enum jsmnerr {
|
26
|
+
/* Not enough tokens were provided */
|
27
|
+
JSMN_ERROR_NOMEM = -1,
|
28
|
+
/* Invalid character inside JSON string */
|
29
|
+
JSMN_ERROR_INVAL = -2,
|
30
|
+
/* The string is not a full JSON packet, more bytes expected */
|
31
|
+
JSMN_ERROR_PART = -3
|
32
|
+
};
|
33
|
+
|
34
|
+
/**
|
35
|
+
* JSON token description.
|
36
|
+
* type type (object, array, string etc.)
|
37
|
+
* start start position in JSON data string
|
38
|
+
* end end position in JSON data string
|
39
|
+
*/
|
40
|
+
typedef struct {
|
41
|
+
jsmntype_t type;
|
42
|
+
int start;
|
43
|
+
int end;
|
44
|
+
int size;
|
45
|
+
#ifdef JSMN_PARENT_LINKS
|
46
|
+
int parent;
|
47
|
+
#endif
|
48
|
+
} jsmntok_t;
|
49
|
+
|
50
|
+
/**
|
51
|
+
* JSON parser. Contains an array of token blocks available. Also stores
|
52
|
+
* the string being parsed now and current position in that string
|
53
|
+
*/
|
54
|
+
typedef struct {
|
55
|
+
unsigned int pos; /* offset in the JSON string */
|
56
|
+
unsigned int toknext; /* next token to allocate */
|
57
|
+
int toksuper; /* superior token node, e.g parent object or array */
|
58
|
+
} jsmn_parser;
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Create JSON parser over an array of tokens
|
62
|
+
*/
|
63
|
+
void jsmn_init(jsmn_parser *parser);
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
|
67
|
+
* a single JSON object.
|
68
|
+
*/
|
69
|
+
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
|
70
|
+
jsmntok_t *tokens, unsigned int num_tokens);
|
71
|
+
|
72
|
+
#ifdef __cplusplus
|
73
|
+
}
|
74
|
+
#endif
|
75
|
+
|
76
|
+
#endif /* __JSMN_H_ */
|
data/lib/fast_mmaped_file.bundle
CHANGED
Binary file
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'prometheus/client/uses_value_type'
|
2
2
|
require 'prometheus/client/helper/json_parser'
|
3
|
+
require 'prometheus/client/helper/plain_file'
|
4
|
+
require 'prometheus/client/helper/metrics_processing'
|
5
|
+
require 'prometheus/client/helper/metrics_representation'
|
3
6
|
|
4
7
|
module Prometheus
|
5
8
|
module Client
|
@@ -10,166 +13,68 @@ module Prometheus
|
|
10
13
|
VERSION = '0.0.4'.freeze
|
11
14
|
CONTENT_TYPE = "#{MEDIA_TYPE}; version=#{VERSION}".freeze
|
12
15
|
|
13
|
-
METRIC_LINE = '%s%s %s'.freeze
|
14
|
-
TYPE_LINE = '# TYPE %s %s'.freeze
|
15
|
-
HELP_LINE = '# HELP %s %s'.freeze
|
16
|
-
|
17
|
-
LABEL = '%s="%s"'.freeze
|
18
|
-
SEPARATOR = ','.freeze
|
19
|
-
DELIMITER = "\n".freeze
|
20
|
-
|
21
|
-
REGEX = { doc: /[\n\\]/, label: /[\n\\"]/ }.freeze
|
22
|
-
REPLACE = { "\n" => '\n', '\\' => '\\\\', '"' => '\"' }.freeze
|
23
|
-
|
24
16
|
class << self
|
25
17
|
def marshal(registry)
|
26
|
-
lines = []
|
27
18
|
|
28
|
-
registry.metrics.
|
29
|
-
|
30
|
-
|
31
|
-
metric.values.each do |label_set, value|
|
32
|
-
representation(metric, label_set, value) { |l| lines << l }
|
19
|
+
metrics = registry.metrics.map do |metric|
|
20
|
+
samples = metric.values.flat_map do |label_set, value|
|
21
|
+
representation(metric, label_set, value)
|
33
22
|
end
|
23
|
+
|
24
|
+
[metric.name, { type: metric.type, help: metric.docstring, samples: samples }]
|
34
25
|
end
|
35
26
|
|
36
|
-
|
37
|
-
(lines << nil).join(DELIMITER)
|
27
|
+
Helper::MetricsRepresentation.to_text(metrics)
|
38
28
|
end
|
39
29
|
|
40
30
|
def marshal_multiprocess(path = Prometheus::Client.configuration.multiprocess_files_dir)
|
41
31
|
metrics = load_metrics(path)
|
42
32
|
|
43
|
-
|
44
|
-
merge_metrics(metrics).each do |name, metric|
|
45
|
-
lines << format(HELP_LINE, name, escape(metric[:help]))
|
46
|
-
lines << format(TYPE_LINE, name, metric[:type])
|
47
|
-
|
48
|
-
metric[:samples].each do |metric_name, labels, value|
|
49
|
-
lines << metric(metric_name, format_labels(labels), value)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
(lines << nil).join(DELIMITER)
|
33
|
+
Helper::MetricsRepresentation.to_text(Helper::MetricsProcessing.merge_metrics(metrics))
|
53
34
|
end
|
54
35
|
|
55
36
|
private
|
56
37
|
|
57
|
-
def merge_metrics(metrics)
|
58
|
-
metrics.each_value do |metric|
|
59
|
-
metric[:samples] = merge_samples(metric[:samples], metric[:type], metric[:multiprocess_mode]).map do |(name, labels), value|
|
60
|
-
[name, labels.to_h, value]
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def merge_samples(raw_samples, metric_type, multiprocess_mode)
|
66
|
-
samples = {}
|
67
|
-
raw_samples.each do |name, labels, value|
|
68
|
-
without_pid = labels.reject { |l| l[0] == 'pid' }
|
69
|
-
|
70
|
-
case metric_type
|
71
|
-
when :gauge
|
72
|
-
case multiprocess_mode
|
73
|
-
when 'min'
|
74
|
-
s = samples.fetch([name, without_pid], value)
|
75
|
-
samples[[name, without_pid]] = [s, value].min
|
76
|
-
when 'max'
|
77
|
-
s = samples.fetch([name, without_pid], value)
|
78
|
-
samples[[name, without_pid]] = [s, value].max
|
79
|
-
when 'livesum'
|
80
|
-
s = samples.fetch([name, without_pid], 0.0)
|
81
|
-
samples[[name, without_pid]] = s + value
|
82
|
-
else # all/liveall
|
83
|
-
samples[[name, labels]] = value
|
84
|
-
end
|
85
|
-
else
|
86
|
-
# Counter, Histogram and Summary.
|
87
|
-
s = samples.fetch([name, without_pid], 0.0)
|
88
|
-
samples[[name, without_pid]] = s + value
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
samples
|
93
|
-
end
|
94
|
-
|
95
38
|
def load_metrics(path)
|
96
39
|
metrics = {}
|
97
40
|
Dir.glob(File.join(path, '*.db')).sort.each do |f|
|
98
|
-
|
99
|
-
parts[-1].gsub!(/(?<=.)-\d+/, '') # remove trailing file number
|
100
|
-
type = parts[0].to_sym
|
101
|
-
|
102
|
-
MmapedDict.read_all_values(f).each do |key, value|
|
103
|
-
metric_name, name, labelnames, labelvalues = Helper::JsonParser.load(key)
|
104
|
-
metric = metrics.fetch(metric_name,
|
105
|
-
metric_name: metric_name,
|
106
|
-
help: 'Multiprocess metric',
|
107
|
-
type: type,
|
108
|
-
samples: []
|
109
|
-
)
|
110
|
-
if type == :gauge
|
111
|
-
pid = parts[2..-1].join('_')
|
112
|
-
metric[:multiprocess_mode] = parts[1]
|
113
|
-
metric[:samples] += [[name, labelnames.zip(labelvalues) + [['pid', pid]], value]]
|
114
|
-
else
|
115
|
-
# The duplicates and labels are fixed in the next for.
|
116
|
-
metric[:samples] += [[name, labelnames.zip(labelvalues), value]]
|
117
|
-
end
|
118
|
-
metrics[metric_name] = metric
|
119
|
-
end
|
41
|
+
Helper::PlainFile.new(f).to_metrics(metrics)
|
120
42
|
end
|
121
43
|
metrics
|
122
44
|
end
|
123
45
|
|
124
|
-
def representation(metric, label_set, value
|
125
|
-
|
46
|
+
def representation(metric, label_set, value)
|
47
|
+
labels = metric.base_labels.merge(label_set)
|
126
48
|
|
127
49
|
if metric.type == :summary
|
128
|
-
summary(metric.name,
|
50
|
+
summary(metric.name, labels, value)
|
129
51
|
elsif metric.type == :histogram
|
130
|
-
histogram(metric.name,
|
52
|
+
histogram(metric.name, labels, value)
|
131
53
|
else
|
132
|
-
|
54
|
+
[[metric.name, labels, value.get]]
|
133
55
|
end
|
134
56
|
end
|
135
57
|
|
136
58
|
def summary(name, set, value)
|
137
|
-
value.get.
|
138
|
-
|
59
|
+
rv = value.get.map do |q, v|
|
60
|
+
[name, set.merge(quantile: q), v]
|
139
61
|
end
|
140
62
|
|
141
|
-
|
142
|
-
|
143
|
-
|
63
|
+
rv << ["#{name}_sum", set, value.get.sum]
|
64
|
+
rv << ["#{name}_count", set, value.get.total]
|
65
|
+
rv
|
144
66
|
end
|
145
67
|
|
146
68
|
def histogram(name, set, value)
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
yield metric(name, format_labels(set.merge(le: '+Inf')), value.get.total)
|
151
|
-
|
152
|
-
l = format_labels(set)
|
153
|
-
yield metric("#{name}_sum", l, value.get.sum)
|
154
|
-
yield metric("#{name}_count", l, value.get.total)
|
155
|
-
end
|
156
|
-
|
157
|
-
def metric(name, labels, value)
|
158
|
-
format(METRIC_LINE, name, labels, value)
|
159
|
-
end
|
160
|
-
|
161
|
-
def format_labels(set)
|
162
|
-
return if set.empty?
|
163
|
-
|
164
|
-
strings = set.each_with_object([]) do |(key, value), memo|
|
165
|
-
memo << format(LABEL, key, escape(value, :label))
|
69
|
+
# |metric_name, labels, value|
|
70
|
+
rv = value.get.map do |q, v|
|
71
|
+
[name, set.merge(le: q), v]
|
166
72
|
end
|
167
73
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
string.to_s.gsub(REGEX[format], REPLACE)
|
74
|
+
rv << [name, set.merge(le: '+Inf'), value.get.total]
|
75
|
+
rv << ["#{name}_sum", set, value.get.sum]
|
76
|
+
rv << ["#{name}_count", set, value.get.total]
|
77
|
+
rv
|
173
78
|
end
|
174
79
|
end
|
175
80
|
end
|
@@ -1,7 +1,12 @@
|
|
1
|
+
require 'prometheus/client/helper/json_parser'
|
2
|
+
require 'fast_mmaped_file'
|
3
|
+
|
1
4
|
module Prometheus
|
2
5
|
module Client
|
3
6
|
module Helper
|
4
7
|
module EntryParser
|
8
|
+
class ParsingError < RuntimeError;
|
9
|
+
end
|
5
10
|
MINIMUM_SIZE = 8
|
6
11
|
START_POSITION = 8
|
7
12
|
|
@@ -9,6 +14,24 @@ module Prometheus
|
|
9
14
|
slice(0..3).unpack('l')[0]
|
10
15
|
end
|
11
16
|
|
17
|
+
def parts
|
18
|
+
@parts ||= File.basename(filepath, '.db')
|
19
|
+
.split('_')
|
20
|
+
.map { |e| e.gsub(/-\d+$/, '') } # remove trailing -number
|
21
|
+
end
|
22
|
+
|
23
|
+
def type
|
24
|
+
parts[0].to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
def pid
|
28
|
+
parts[2..-1].join('_')
|
29
|
+
end
|
30
|
+
|
31
|
+
def multiprocess_mode
|
32
|
+
parts[1]
|
33
|
+
end
|
34
|
+
|
12
35
|
def empty?
|
13
36
|
size < MINIMUM_SIZE || used.zero?
|
14
37
|
end
|
@@ -38,6 +61,101 @@ module Prometheus
|
|
38
61
|
end
|
39
62
|
end
|
40
63
|
end
|
64
|
+
|
65
|
+
def parsed_entries(ignore_errors = false)
|
66
|
+
result = entries.map do |data, encoded_len, value_offset, _|
|
67
|
+
begin
|
68
|
+
encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
|
69
|
+
[encoded, value]
|
70
|
+
rescue ArgumentError => e
|
71
|
+
Prometheus::Client.logger.debug("Error processing data: #{bin_to_hex(data[0, 7])} len: #{encoded_len} value_offset: #{value_offset}")
|
72
|
+
raise ParsingError(e) unless ignore_errors
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
result.reject { |e| e.nil? } if ignore_errors
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_metrics(metrics = {}, ignore_errors = false)
|
81
|
+
FastMmapedFile.fast_entries(data) do |metric_name, name, labels, value|
|
82
|
+
begin
|
83
|
+
metric = metrics.fetch(metric_name,
|
84
|
+
metric_name: metric_name,
|
85
|
+
help: 'Multiprocess metric',
|
86
|
+
type: type,
|
87
|
+
samples: {}
|
88
|
+
)
|
89
|
+
if type == :gauge
|
90
|
+
metric[:multiprocess_mode] = multiprocess_mode
|
91
|
+
merge_samples(name, labels, value, metric[:samples])
|
92
|
+
else
|
93
|
+
merge_samples(name, labels, value, metric[:samples])
|
94
|
+
# The duplicates and labels are fixed in the next for.
|
95
|
+
end
|
96
|
+
metrics[metric_name] = metric
|
97
|
+
rescue JSON::ParserError => e
|
98
|
+
raise ParsingError(e) unless ignore_errors
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
metrics.reject { |e| e.nil? } if ignore_errors
|
103
|
+
metrics
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def min(a, b)
|
108
|
+
if a < b
|
109
|
+
a
|
110
|
+
else
|
111
|
+
b
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def max(a, b)
|
116
|
+
if a > b
|
117
|
+
a
|
118
|
+
else
|
119
|
+
b
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def merge_samples(name, labels, value, samples)
|
124
|
+
samples ||= {}
|
125
|
+
case type
|
126
|
+
when :gauge
|
127
|
+
case multiprocess_mode
|
128
|
+
when 'min'
|
129
|
+
key = labels.merge!(__key_name: name)
|
130
|
+
s = samples.fetch(key, value)
|
131
|
+
samples[key] = min(s, value)
|
132
|
+
when 'max'
|
133
|
+
key = labels.merge!(__key_name: name)
|
134
|
+
s = samples.fetch(key, value)
|
135
|
+
samples[key] = max(s, value)
|
136
|
+
when 'livesum'
|
137
|
+
key = labels.merge!(__key_name: name)
|
138
|
+
s = samples.fetch(key, 0.0)
|
139
|
+
samples[key] = s + value
|
140
|
+
else # all/liveall
|
141
|
+
key = labels.merge!(pid: pid, __key_name: name)
|
142
|
+
samples[key] = value
|
143
|
+
end
|
144
|
+
else
|
145
|
+
key = labels.merge!(__key_name: name)
|
146
|
+
# Counter, Histogram and Summary.
|
147
|
+
s = samples.fetch(key, 0.0)
|
148
|
+
samples[key] = s + value
|
149
|
+
end
|
150
|
+
|
151
|
+
samples
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def bin_to_hex(s)
|
157
|
+
s.each_byte.map { |b| b.to_s(16) }.join
|
158
|
+
end
|
41
159
|
end
|
42
160
|
end
|
43
161
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Prometheus
|
2
|
+
module Client
|
3
|
+
module Helper
|
4
|
+
module MetricsProcessing
|
5
|
+
def self.merge_metrics(metrics)
|
6
|
+
metrics
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.min(a, b)
|
10
|
+
if a < b
|
11
|
+
a
|
12
|
+
else
|
13
|
+
b
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.max(a, b)
|
18
|
+
if a > b
|
19
|
+
a
|
20
|
+
else
|
21
|
+
b
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.merge_samples(raw_samples, metric_type, multiprocess_mode)
|
26
|
+
samples = {}
|
27
|
+
raw_samples.each do |name, labels, value|
|
28
|
+
key = { name: name, labels: labels }
|
29
|
+
# without_pid = labels.delete(:pid)
|
30
|
+
|
31
|
+
case metric_type
|
32
|
+
when :gauge
|
33
|
+
case multiprocess_mode
|
34
|
+
when 'min'
|
35
|
+
labels.delete(:pid)
|
36
|
+
s = samples.fetch(key, value)
|
37
|
+
samples[key] = min(s, value)
|
38
|
+
when 'max'
|
39
|
+
labels.delete(:pid)
|
40
|
+
s = samples.fetch(key, value)
|
41
|
+
samples[key] = max(s, value)
|
42
|
+
when 'livesum'
|
43
|
+
labels.delete(:pid)
|
44
|
+
s = samples.fetch(key, 0.0)
|
45
|
+
samples[key] = s + value
|
46
|
+
else # all/liveall
|
47
|
+
samples[key] = value
|
48
|
+
end
|
49
|
+
else
|
50
|
+
labels.delete(:pid)
|
51
|
+
# Counter, Histogram and Summary.
|
52
|
+
s = samples.fetch(key, 0.0)
|
53
|
+
samples[key] = s + value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
samples
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Prometheus
|
2
|
+
module Client
|
3
|
+
module Helper
|
4
|
+
module MetricsRepresentation
|
5
|
+
METRIC_LINE = '%s%s %s'.freeze
|
6
|
+
TYPE_LINE = '# TYPE %s %s'.freeze
|
7
|
+
HELP_LINE = '# HELP %s %s'.freeze
|
8
|
+
|
9
|
+
LABEL = '%s="%s"'.freeze
|
10
|
+
SEPARATOR = ','.freeze
|
11
|
+
DELIMITER = "\n".freeze
|
12
|
+
|
13
|
+
REGEX = { doc: /[\n\\]/, label: /[\n\\"]/ }.freeze
|
14
|
+
REPLACE = { "\n" => '\n', '\\' => '\\\\', '"' => '\"' }.freeze
|
15
|
+
|
16
|
+
def self.to_text(metrics)
|
17
|
+
lines = []
|
18
|
+
|
19
|
+
metrics.each do |name, metric|
|
20
|
+
lines << format(HELP_LINE, name, escape(metric[:help]))
|
21
|
+
lines << format(TYPE_LINE, name, metric[:type])
|
22
|
+
metric[:samples].each do |labels, value|
|
23
|
+
key = labels.delete(:__key_name)
|
24
|
+
lines << metric(key, format_labels(labels), value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# there must be a trailing delimiter
|
29
|
+
(lines << nil).join(DELIMITER)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.metric(name, labels, value)
|
33
|
+
format(METRIC_LINE, name, labels, value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.format_labels(set) #TODO: rename set
|
37
|
+
return if set.empty?
|
38
|
+
|
39
|
+
strings = set.each_with_object([]) do |(key, value), memo|
|
40
|
+
memo << format(LABEL, key, escape(value, :label))
|
41
|
+
end
|
42
|
+
|
43
|
+
"{#{strings.join(SEPARATOR)}}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.escape(string, format = :doc)
|
47
|
+
string.to_s.gsub(REGEX[format], REPLACE)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prometheus-client-mmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.beta31
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Schmidt
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-11-
|
12
|
+
date: 2017-11-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mmap2
|
@@ -71,6 +71,8 @@ files:
|
|
71
71
|
- README.md
|
72
72
|
- ext/fast_mmaped_file/extconf.rb
|
73
73
|
- ext/fast_mmaped_file/fast_mmaped_file.c
|
74
|
+
- ext/fast_mmaped_file/jsmn.c
|
75
|
+
- ext/fast_mmaped_file/jsmn.h
|
74
76
|
- ext/fast_mmaped_file/mmap.h
|
75
77
|
- lib/fast_mmaped_file.bundle
|
76
78
|
- lib/prometheus.rb
|
@@ -82,6 +84,8 @@ files:
|
|
82
84
|
- lib/prometheus/client/helper/entry_parser.rb
|
83
85
|
- lib/prometheus/client/helper/file_locker.rb
|
84
86
|
- lib/prometheus/client/helper/json_parser.rb
|
87
|
+
- lib/prometheus/client/helper/metrics_processing.rb
|
88
|
+
- lib/prometheus/client/helper/metrics_representation.rb
|
85
89
|
- lib/prometheus/client/helper/mmaped_file.rb
|
86
90
|
- lib/prometheus/client/helper/plain_file.rb
|
87
91
|
- lib/prometheus/client/histogram.rb
|
@@ -118,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
122
|
version: 1.3.1
|
119
123
|
requirements: []
|
120
124
|
rubyforge_project:
|
121
|
-
rubygems_version: 2.6.
|
125
|
+
rubygems_version: 2.6.14
|
122
126
|
signing_key:
|
123
127
|
specification_version: 4
|
124
128
|
summary: A suite of instrumentation metric primitivesthat can be exposed through a
|