Line data Source code
1 : #include "fd_prometheus.h"
2 :
3 : #include "fd_metrics.h"
4 :
5 : #include "../topo/fd_topo.h"
6 : #include "../../waltz/http/fd_http_server.h"
7 :
8 : struct fd_prom_render {
9 : fd_http_server_t * http;
10 : ulong last_name_hash;
11 : };
12 :
13 : typedef struct fd_prom_render fd_prom_render_t;
14 :
15 : fd_prom_render_t
16 3 : fd_prom_render_create( fd_http_server_t * http ) {
17 3 : return (fd_prom_render_t) {
18 3 : .http = http,
19 3 : .last_name_hash = 0UL
20 3 : };
21 3 : }
22 :
23 : static void
24 : render_header( fd_prom_render_t * r,
25 273 : fd_metrics_meta_t const * metric ) {
26 : /* Only render header once per metric name */
27 273 : ulong hash = fd_cstr_hash( metric->name );
28 273 : if( r->last_name_hash != hash ) {
29 141 : if( r->last_name_hash ) {
30 138 : fd_http_server_printf( r->http, "\n" );
31 138 : }
32 141 : fd_http_server_printf( r->http, "# HELP %s %s\n# TYPE %s %s\n", metric->name, metric->desc, metric->name, fd_metrics_meta_type_str( metric ) );
33 141 : r->last_name_hash = hash;
34 141 : }
35 273 : }
36 :
37 : static void
38 : render_link( fd_prom_render_t * r,
39 : fd_metrics_meta_t const * metric,
40 : fd_topo_tile_t const * tile,
41 : fd_topo_link_t const * link,
42 0 : ulong value ) {
43 0 : render_header( r, metric );
44 0 : switch( metric->converter ) {
45 0 : case FD_METRICS_CONVERTER_NANOSECONDS:
46 0 : value = fd_metrics_convert_ticks_to_nanoseconds( value );
47 0 : break;
48 0 : case FD_METRICS_CONVERTER_NONE:
49 0 : break;
50 0 : default:
51 0 : FD_LOG_ERR(( "unknown converter %i", metric->converter ));
52 0 : }
53 0 : fd_http_server_printf( r->http, "%s{kind=\"%s\",kind_id=\"%lu\",link_kind=\"%s\",link_kind_id=\"%lu\"} %lu\n", metric->name, tile->name, tile->kind_id, link->name, link->kind_id, value );
54 0 : }
55 :
56 : static void
57 : render_histogram( fd_prom_render_t * r,
58 : fd_metrics_meta_t const * metric,
59 6 : fd_topo_tile_t const * tile ) {
60 6 : render_header( r, metric );
61 :
62 6 : fd_histf_t hist[1];
63 6 : if( FD_LIKELY( metric->converter==FD_METRICS_CONVERTER_SECONDS ) )
64 6 : FD_TEST( fd_histf_new( hist, fd_metrics_convert_seconds_to_ticks( metric->histogram.seconds.min ), fd_metrics_convert_seconds_to_ticks ( metric->histogram.seconds.max ) ) );
65 0 : else if( FD_LIKELY( metric->converter==FD_METRICS_CONVERTER_NONE ) )
66 0 : FD_TEST( fd_histf_new( hist, metric->histogram.none.min, metric->histogram.none.max ) );
67 0 : else FD_LOG_ERR(( "unknown converter %i", metric->converter ));
68 :
69 6 : ulong value = 0;
70 6 : char value_str[ 64 ];
71 102 : for( ulong k=0; k<FD_HISTF_BUCKET_CNT; k++ ) {
72 96 : value += *(fd_metrics_tile( tile->metrics ) + metric->offset + k);
73 :
74 96 : char * le; /* le here means "less then or equal" not "left edge" */
75 96 : char le_str[ 64 ];
76 96 : if( FD_UNLIKELY( k==FD_HISTF_BUCKET_CNT-1UL ) ) le = "+Inf";
77 90 : else {
78 90 : ulong edge = fd_histf_right( hist, k );
79 90 : if( FD_LIKELY( metric->converter==FD_METRICS_CONVERTER_SECONDS ) ) {
80 90 : double edgef = fd_metrics_convert_ticks_to_seconds( edge-1 );
81 90 : FD_TEST( fd_cstr_printf_check( le_str, sizeof( le_str ), NULL, "%.17g", edgef ) );
82 90 : } else {
83 0 : FD_TEST( fd_cstr_printf_check( le_str, sizeof( le_str ), NULL, "%lu", edge-1 ) );
84 0 : }
85 90 : le = le_str;
86 90 : }
87 :
88 96 : FD_TEST( fd_cstr_printf_check( value_str, sizeof( value_str ), NULL, "%lu", value ));
89 96 : fd_http_server_printf( r->http, "%s_bucket{kind=\"%s\",kind_id=\"%lu\",le=\"%s\"} %s\n", metric->name, tile->name, tile->kind_id, le, value_str );
90 96 : }
91 :
92 6 : char sum_str[ 64 ];
93 6 : if( FD_LIKELY( metric->converter==FD_METRICS_CONVERTER_SECONDS ) ) {
94 6 : double sumf = fd_metrics_convert_ticks_to_seconds( *(fd_metrics_tile( tile->metrics ) + metric->offset + FD_HISTF_BUCKET_CNT) );
95 6 : FD_TEST( fd_cstr_printf_check( sum_str, sizeof( sum_str ), NULL, "%.17g", sumf ) );
96 6 : } else {
97 0 : FD_TEST( fd_cstr_printf_check( sum_str, sizeof( sum_str ), NULL, "%lu", *(fd_metrics_tile( tile->metrics ) + metric->offset + FD_HISTF_BUCKET_CNT) ));
98 0 : }
99 :
100 6 : fd_http_server_printf( r->http, "%s_sum{kind=\"%s\",kind_id=\"%lu\"} %s\n", metric->name, tile->name, tile->kind_id, sum_str );
101 6 : fd_http_server_printf( r->http, "%s_count{kind=\"%s\",kind_id=\"%lu\"} %s\n", metric->name, tile->name, tile->kind_id, value_str );
102 6 : }
103 :
104 : static void
105 : render_counter( fd_prom_render_t * r,
106 : fd_metrics_meta_t const * metric,
107 267 : fd_topo_tile_t const * tile ) {
108 267 : render_header( r, metric );
109 267 : ulong value = *(fd_metrics_tile( tile->metrics ) + metric->offset);
110 :
111 267 : switch( metric->converter ) {
112 0 : case FD_METRICS_CONVERTER_NANOSECONDS:
113 0 : value = fd_metrics_convert_ticks_to_nanoseconds( value );
114 0 : break;
115 0 : case FD_METRICS_CONVERTER_SECONDS:
116 0 : value = (ulong)(fd_metrics_convert_ticks_to_seconds( value ) * 1e9);
117 0 : break;
118 267 : case FD_METRICS_CONVERTER_NONE:
119 267 : break;
120 0 : default:
121 0 : FD_LOG_ERR(( "unknown converter %i", metric->converter ));
122 267 : }
123 :
124 267 : fd_http_server_printf( r->http, "%s{kind=\"%s\",kind_id=\"%lu\"", metric->name, tile->name, tile->kind_id );
125 267 : if( metric->enum_name ) {
126 156 : fd_http_server_printf( r->http, ",%s=\"%s\"", metric->enum_name, metric->enum_variant );
127 156 : }
128 267 : fd_http_server_printf( r->http, "} %lu\n", value );
129 267 : }
130 :
131 : static void
132 : render_links_in( fd_prom_render_t * r,
133 : fd_topo_t const * topo,
134 : ulong metrics_cnt,
135 0 : fd_metrics_meta_t const * metrics ) {
136 0 : for( ulong i=0UL; i<metrics_cnt; i++ ) {
137 0 : fd_metrics_meta_t const * metric = &metrics[ i ];
138 0 : for( ulong j=0UL; j<topo->tile_cnt; j++ ) {
139 0 : fd_topo_tile_t const * tile = &topo->tiles[ j ];
140 0 : ulong polled_in_idx = 0UL;
141 0 : for( ulong k=0UL; k<tile->in_cnt; k++ ) {
142 0 : if( FD_UNLIKELY( !tile->in_link_poll[ k ] ) ) continue;
143 0 : fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ k ] ];
144 0 : ulong value = *(fd_metrics_link_in( tile->metrics, polled_in_idx ) + metric->offset );
145 0 : render_link( r, metric, tile, link, value );
146 0 : polled_in_idx++;
147 0 : }
148 0 : }
149 0 : }
150 0 : }
151 :
152 : static void
153 : render_links_out( fd_prom_render_t * r,
154 : fd_topo_t const * topo,
155 : ulong metrics_cnt,
156 0 : fd_metrics_meta_t const * metrics ) {
157 0 : for( ulong i=0UL; i<metrics_cnt; i++ ) {
158 0 : fd_metrics_meta_t const * metric = &metrics[ i ];
159 0 : for( ulong j=0UL; j<topo->tile_cnt; j++ ) {
160 0 : fd_topo_tile_t const * tile = &topo->tiles[ j ];
161 0 : ulong reliable_conns_idx = 0UL;
162 0 : for( ulong k=0UL; k<topo->tile_cnt; k++ ) {
163 0 : fd_topo_tile_t const * consumer_tile = &topo->tiles[ k ];
164 0 : for( ulong l=0UL; l<consumer_tile->in_cnt; l++ ) {
165 0 : for( ulong m=0UL; m<tile->out_cnt; m++ ) {
166 0 : if( FD_UNLIKELY( consumer_tile->in_link_id[ l ]==tile->out_link_id[ m ] && consumer_tile->in_link_reliable[ l ] ) ) {
167 0 : fd_topo_link_t const * link = &topo->links[ consumer_tile->in_link_id[ l ] ];
168 0 : ulong value = *(fd_metrics_link_out( tile->metrics, reliable_conns_idx ) + metric->offset );
169 0 : render_link( r, metric, consumer_tile, link, value );
170 0 : reliable_conns_idx++;
171 0 : }
172 0 : }
173 0 : }
174 0 : }
175 0 : }
176 0 : }
177 0 : }
178 :
179 : static void
180 : render_tile_metric( fd_prom_render_t * r,
181 : fd_topo_tile_t const * tile,
182 273 : fd_metrics_meta_t const * metric ) {
183 273 : if( FD_LIKELY( metric->type==FD_METRICS_TYPE_COUNTER || metric->type==FD_METRICS_TYPE_GAUGE ) ) {
184 267 : render_counter( r, metric, tile );
185 267 : } else if( FD_LIKELY( metric->type==FD_METRICS_TYPE_HISTOGRAM ) ) {
186 6 : render_histogram( r, metric, tile );
187 6 : }
188 273 : }
189 :
190 : static void
191 : render_tile( fd_prom_render_t * r,
192 : fd_topo_t const * topo,
193 : char const * tile_name,
194 : ulong metrics_cnt,
195 0 : fd_metrics_meta_t const * metrics ) {
196 0 : for( ulong i=0UL; i<metrics_cnt; i++ ) {
197 0 : for( ulong j=0UL; j<topo->tile_cnt; j++ ) {
198 : /* FIXME: This is O(n^2) rather than O(n). */
199 0 : if( FD_LIKELY( tile_name!=NULL && 0!=strcmp( topo->tiles[j].name, tile_name ) ) ) continue;
200 0 : render_tile_metric( r, topo->tiles+j, metrics+i );
201 0 : }
202 0 : }
203 0 : }
204 :
205 : void
206 : fd_prometheus_render_tile( fd_http_server_t * http,
207 : fd_topo_tile_t const * tile,
208 : fd_metrics_meta_t const * metrics,
209 3 : ulong metrics_cnt ) {
210 3 : fd_prom_render_t r = fd_prom_render_create( http );
211 276 : for( ulong i=0UL; i<metrics_cnt; i++ ) {
212 273 : render_tile_metric( &r, tile, metrics+i );
213 273 : }
214 3 : }
215 :
216 : void
217 : fd_prometheus_render_all( fd_topo_t const * topo,
218 0 : fd_http_server_t * http ) {
219 0 : fd_prom_render_t r = fd_prom_render_create( http );
220 0 : render_tile( &r, topo, NULL, FD_METRICS_ALL_TOTAL, FD_METRICS_ALL );
221 0 : render_links_in( &r, topo, FD_METRICS_ALL_LINK_IN_TOTAL, FD_METRICS_ALL_LINK_IN );
222 0 : render_links_out( &r, topo, FD_METRICS_ALL_LINK_OUT_TOTAL, FD_METRICS_ALL_LINK_OUT );
223 0 : for( ulong i=0UL; i<FD_METRICS_TILE_KIND_CNT; i++ ) {
224 0 : render_tile( &r, topo, FD_METRICS_TILE_KIND_NAMES[ i ], FD_METRICS_TILE_KIND_SIZES[ i ], FD_METRICS_TILE_KIND_METRICS[ i ] );
225 0 : }
226 0 : }
|