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 0 : fd_prom_render_create( fd_http_server_t * http ) {
17 0 : return (fd_prom_render_t) {
18 0 : .http = http,
19 0 : .last_name_hash = 0UL
20 0 : };
21 0 : }
22 :
23 : /* Counters are exported with a "_total" suffix per Prometheus naming
24 : conventions. Gauges and histograms keep their bare name (histograms
25 : additionally emit the standard _bucket/_sum/_count series). */
26 :
27 : static char const *
28 : metric_export_name( fd_metrics_meta_t const * metric,
29 : char * buf,
30 0 : ulong buf_sz ) {
31 0 : if( metric->type==FD_METRICS_TYPE_COUNTER ) {
32 0 : FD_TEST( fd_cstr_printf_check( buf, buf_sz, NULL, "%s_total", metric->name ) );
33 0 : return buf;
34 0 : }
35 0 : return metric->name;
36 0 : }
37 :
38 : static void
39 : render_header( fd_prom_render_t * r,
40 0 : fd_metrics_meta_t const * metric ) {
41 : /* Only render header once per metric name */
42 0 : ulong hash = fd_cstr_hash( metric->name );
43 0 : if( r->last_name_hash != hash ) {
44 0 : if( r->last_name_hash ) {
45 0 : fd_http_server_printf( r->http, "\n" );
46 0 : }
47 0 : char name_buf[ 256 ];
48 0 : char const * name = metric_export_name( metric, name_buf, sizeof(name_buf) );
49 0 : fd_http_server_printf( r->http, "# HELP %s %s\n# TYPE %s %s\n", name, metric->desc, name, fd_metrics_meta_type_str( metric ) );
50 0 : r->last_name_hash = hash;
51 0 : }
52 0 : }
53 :
54 : static void
55 : render_link( fd_prom_render_t * r,
56 : fd_metrics_meta_t const * metric,
57 : fd_topo_tile_t const * tile,
58 : fd_topo_link_t const * link,
59 0 : ulong value ) {
60 0 : render_header( r, metric );
61 0 : switch( metric->converter ) {
62 0 : case FD_METRICS_CONVERTER_NANOSECONDS:
63 0 : value = fd_metrics_convert_ticks_to_nanoseconds( value );
64 0 : break;
65 0 : case FD_METRICS_CONVERTER_NONE:
66 0 : break;
67 0 : default:
68 0 : FD_LOG_ERR(( "unknown converter %i", metric->converter ));
69 0 : }
70 0 : char name_buf[ 256 ];
71 0 : char const * name = metric_export_name( metric, name_buf, sizeof(name_buf) );
72 0 : fd_http_server_printf( r->http, "%s{kind=\"%s\",kind_id=\"%lu\",link_kind=\"%s\",link_kind_id=\"%lu\"} %lu\n", name, tile->name, tile->kind_id, link->name, link->kind_id, value );
73 0 : }
74 :
75 : static void
76 : render_histogram( fd_prom_render_t * r,
77 : fd_metrics_meta_t const * metric,
78 0 : fd_topo_tile_t const * tile ) {
79 0 : render_header( r, metric );
80 :
81 0 : fd_histf_t hist[1];
82 0 : if( FD_LIKELY( metric->converter==FD_METRICS_CONVERTER_NANOSECONDS ) )
83 0 : FD_TEST( fd_histf_new_edges( hist, metric->histogram.ticks.edges_ns ) );
84 0 : else if( FD_LIKELY( metric->converter==FD_METRICS_CONVERTER_NONE ) )
85 0 : FD_TEST( fd_histf_new( hist, metric->histogram.none.min, metric->histogram.none.max ) );
86 0 : else FD_LOG_ERR(( "unknown converter %i", metric->converter ));
87 :
88 0 : ulong value = 0;
89 0 : char value_str[ 64 ];
90 0 : for( ulong k=0; k<FD_HISTF_BUCKET_CNT; k++ ) {
91 0 : value += *(fd_metrics_tile( tile->metrics ) + metric->offset + k);
92 :
93 0 : char * le; /* le here means "less then or equal" not "left edge" */
94 0 : char le_str[ 64 ];
95 0 : if( FD_UNLIKELY( k==FD_HISTF_BUCKET_CNT-1UL ) ) le = "+Inf";
96 0 : else {
97 0 : ulong edge = fd_histf_right( hist, k );
98 0 : FD_TEST( fd_cstr_printf_check( le_str, sizeof( le_str ), NULL, "%lu", edge-1 ) );
99 0 : le = le_str;
100 0 : }
101 :
102 0 : FD_TEST( fd_cstr_printf_check( value_str, sizeof( value_str ), NULL, "%lu", value ));
103 0 : 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 );
104 0 : }
105 :
106 0 : char sum_str[ 64 ];
107 0 : ulong sum = *(fd_metrics_tile( tile->metrics ) + metric->offset + FD_HISTF_BUCKET_CNT);
108 0 : if( FD_LIKELY( metric->converter==FD_METRICS_CONVERTER_NANOSECONDS ) ) sum = fd_metrics_convert_ticks_to_nanoseconds( sum );
109 0 : FD_TEST( fd_cstr_printf_check( sum_str, sizeof( sum_str ), NULL, "%lu", sum ) );
110 :
111 0 : fd_http_server_printf( r->http, "%s_sum{kind=\"%s\",kind_id=\"%lu\"} %s\n", metric->name, tile->name, tile->kind_id, sum_str );
112 0 : fd_http_server_printf( r->http, "%s_count{kind=\"%s\",kind_id=\"%lu\"} %s\n", metric->name, tile->name, tile->kind_id, value_str );
113 0 : }
114 :
115 : static void
116 : render_counter( fd_prom_render_t * r,
117 : fd_metrics_meta_t const * metric,
118 0 : fd_topo_tile_t const * tile ) {
119 0 : render_header( r, metric );
120 0 : ulong raw_value = *(fd_metrics_tile( tile->metrics ) + metric->offset);
121 :
122 0 : char name_buf[ 256 ];
123 0 : char const * name = metric_export_name( metric, name_buf, sizeof(name_buf) );
124 0 : fd_http_server_printf( r->http, "%s{kind=\"%s\",kind_id=\"%lu\"", name, tile->name, tile->kind_id );
125 0 : if( metric->enum_name ) {
126 0 : fd_http_server_printf( r->http, ",%s=\"%s\"", metric->enum_name, metric->enum_variant );
127 0 : }
128 0 : switch( metric->converter ) {
129 0 : case FD_METRICS_CONVERTER_NANOSECONDS:
130 0 : fd_http_server_printf( r->http, "} %lu\n", fd_metrics_convert_ticks_to_nanoseconds( raw_value ) );
131 0 : break;
132 0 : case FD_METRICS_CONVERTER_NONE:
133 0 : fd_http_server_printf( r->http, "} %lu\n", raw_value );
134 0 : break;
135 0 : default:
136 0 : FD_LOG_ERR(( "unknown converter %i", metric->converter ));
137 0 : }
138 0 : }
139 :
140 : static void
141 : render_links_in( fd_prom_render_t * r,
142 : fd_topo_t const * topo,
143 : ulong metrics_cnt,
144 0 : fd_metrics_meta_t const * metrics ) {
145 0 : for( ulong i=0UL; i<metrics_cnt; i++ ) {
146 0 : fd_metrics_meta_t const * metric = &metrics[ i ];
147 0 : for( ulong j=0UL; j<topo->tile_cnt; j++ ) {
148 0 : fd_topo_tile_t const * tile = &topo->tiles[ j ];
149 0 : ulong polled_in_idx = 0UL;
150 0 : for( ulong k=0UL; k<tile->in_cnt; k++ ) {
151 0 : if( FD_UNLIKELY( !tile->in_link_poll[ k ] ) ) continue;
152 0 : fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ k ] ];
153 0 : ulong value = *(fd_metrics_link_in( tile->metrics, polled_in_idx ) + metric->offset );
154 0 : render_link( r, metric, tile, link, value );
155 0 : polled_in_idx++;
156 0 : }
157 0 : }
158 0 : }
159 0 : }
160 :
161 : static void
162 : render_tile_metric( fd_prom_render_t * r,
163 : fd_topo_tile_t const * tile,
164 0 : fd_metrics_meta_t const * metric ) {
165 0 : if( FD_LIKELY( metric->type==FD_METRICS_TYPE_COUNTER || metric->type==FD_METRICS_TYPE_GAUGE ) ) {
166 0 : render_counter( r, metric, tile );
167 0 : } else if( FD_LIKELY( metric->type==FD_METRICS_TYPE_HISTOGRAM ) ) {
168 0 : render_histogram( r, metric, tile );
169 0 : }
170 0 : }
171 :
172 : static void
173 : render_tile( fd_prom_render_t * r,
174 : fd_topo_t const * topo,
175 : char const * tile_name,
176 : ulong metrics_cnt,
177 0 : fd_metrics_meta_t const * metrics ) {
178 0 : for( ulong i=0UL; i<metrics_cnt; i++ ) {
179 0 : for( ulong j=0UL; j<topo->tile_cnt; j++ ) {
180 : /* FIXME: This is O(n^2) rather than O(n). */
181 0 : if( FD_LIKELY( tile_name!=NULL && 0!=strcmp( topo->tiles[ j ].name, tile_name ) ) ) continue;
182 0 : render_tile_metric( r, topo->tiles+j, metrics+i );
183 0 : }
184 0 : }
185 0 : }
186 :
187 : void
188 : fd_prometheus_render_tile( fd_http_server_t * http,
189 : fd_topo_tile_t const * tile,
190 : fd_metrics_meta_t const * metrics,
191 0 : ulong metrics_cnt ) {
192 0 : fd_prom_render_t r = fd_prom_render_create( http );
193 0 : for( ulong i=0UL; i<metrics_cnt; i++ ) {
194 0 : render_tile_metric( &r, tile, metrics+i );
195 0 : }
196 0 : }
197 :
198 : void
199 : fd_prometheus_render_all( fd_topo_t const * topo,
200 0 : fd_http_server_t * http ) {
201 0 : fd_prom_render_t r = fd_prom_render_create( http );
202 0 : render_tile( &r, topo, NULL, FD_METRICS_ALL_TOTAL, FD_METRICS_ALL );
203 0 : render_links_in( &r, topo, FD_METRICS_ALL_LINK_IN_TOTAL, FD_METRICS_ALL_LINK_IN );
204 0 : for( ulong i=0UL; i<FD_METRICS_TILE_KIND_CNT; i++ ) {
205 0 : render_tile( &r, topo, FD_METRICS_TILE_KIND_NAMES[ i ], FD_METRICS_TILE_KIND_SIZES[ i ], FD_METRICS_TILE_KIND_METRICS[ i ] );
206 0 : }
207 0 : }
|