blob: ef217007d28693c0be7d93144dcfaeb39516a512 [file] [log] [blame]
Christopher Fauletf959d082019-02-07 15:38:42 +01001/*
2 * Promex is a Prometheus exporter for HAProxy
3 *
4 * It is highly inspired by the official Prometheus exporter.
5 * See: https://github.com/prometheus/haproxy_exporter
6 *
7 * Copyright 2019 Christopher Faulet <cfaulet@haproxy.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 *
14 */
15
Willy Tarreau122eba92020-06-04 10:15:32 +020016#include <haproxy/action-t.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020017#include <haproxy/api.h>
Willy Tarreau3f0f82e2020-06-04 19:42:41 +020018#include <haproxy/applet.h>
Willy Tarreau49801602020-06-04 22:50:02 +020019#include <haproxy/backend.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020020#include <haproxy/cfgparse.h>
William Dauchyde3c3262021-02-01 13:11:51 +010021#include <haproxy/check.h>
Willy Tarreau762d7a52020-06-04 11:23:07 +020022#include <haproxy/frontend.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020023#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020024#include <haproxy/http.h>
Willy Tarreaub7fc4c42021-10-06 18:56:42 +020025#include <haproxy/http_ana.h>
Willy Tarreau87735332020-06-04 09:08:41 +020026#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020027#include <haproxy/htx.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020028#include <haproxy/list.h>
Willy Tarreau213e9902020-06-04 14:58:24 +020029#include <haproxy/listener.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020030#include <haproxy/log.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020031#include <haproxy/proxy.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020032#include <haproxy/sample.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020033#include <haproxy/server.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020034#include <haproxy/stats.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020035#include <haproxy/stream.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020036#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020037#include <haproxy/task.h>
Willy Tarreau0fd04fd2021-05-08 12:58:12 +020038#include <haproxy/tools.h>
William Dauchy5a982a72021-01-08 13:18:06 +010039#include <haproxy/version.h>
Christopher Fauletf959d082019-02-07 15:38:42 +010040
41/* Prometheus exporter applet states (appctx->st0) */
42enum {
43 PROMEX_ST_INIT = 0, /* initialized */
44 PROMEX_ST_HEAD, /* send headers before dump */
45 PROMEX_ST_DUMP, /* dumping stats */
46 PROMEX_ST_DONE, /* finished */
Christopher Faulet9744f7c2019-03-27 15:48:53 +010047 PROMEX_ST_END, /* treatment terminated */
Christopher Fauletf959d082019-02-07 15:38:42 +010048};
49
50/* Prometheus exporter dumper states (appctx->st1) */
51enum {
William Dauchy69164222021-02-07 20:42:38 +010052 PROMEX_DUMPER_INIT = 0, /* initialized */
53 PROMEX_DUMPER_GLOBAL, /* dump metrics of globals */
54 PROMEX_DUMPER_FRONT, /* dump metrics of frontend proxies */
55 PROMEX_DUMPER_BACK, /* dump metrics of backend proxies */
56 PROMEX_DUMPER_LI, /* dump metrics of listeners */
57 PROMEX_DUMPER_SRV, /* dump metrics of servers */
58 PROMEX_DUMPER_STICKTABLE, /* dump metrics of stick tables */
59 PROMEX_DUMPER_DONE, /* finished */
Christopher Fauletf959d082019-02-07 15:38:42 +010060};
61
62/* Prometheus exporter flags (appctx->ctx.stats.flags) */
William Dauchy69164222021-02-07 20:42:38 +010063#define PROMEX_FL_METRIC_HDR 0x00000001
64#define PROMEX_FL_INFO_METRIC 0x00000002
65#define PROMEX_FL_FRONT_METRIC 0x00000004
66#define PROMEX_FL_BACK_METRIC 0x00000008
67#define PROMEX_FL_SRV_METRIC 0x00000010
William Dauchye3f7bd52021-02-14 23:22:56 +010068#define PROMEX_FL_LI_METRIC 0x00000020
69#define PROMEX_FL_STICKTABLE_METRIC 0x00000040
70#define PROMEX_FL_SCOPE_GLOBAL 0x00000080
71#define PROMEX_FL_SCOPE_FRONT 0x00000100
72#define PROMEX_FL_SCOPE_BACK 0x00000200
73#define PROMEX_FL_SCOPE_SERVER 0x00000400
74#define PROMEX_FL_SCOPE_LI 0x00000800
75#define PROMEX_FL_SCOPE_STICKTABLE 0x00001000
76#define PROMEX_FL_NO_MAINT_SRV 0x00002000
Christopher Faulet78407ce2019-11-18 14:47:08 +010077
William Dauchy69164222021-02-07 20:42:38 +010078#define PROMEX_FL_SCOPE_ALL (PROMEX_FL_SCOPE_GLOBAL | PROMEX_FL_SCOPE_FRONT | \
William Dauchye3f7bd52021-02-14 23:22:56 +010079 PROMEX_FL_SCOPE_LI | PROMEX_FL_SCOPE_BACK | \
80 PROMEX_FL_SCOPE_SERVER | PROMEX_FL_SCOPE_STICKTABLE)
Christopher Fauletf959d082019-02-07 15:38:42 +010081
Christopher Faulet0312c0d2021-01-20 15:19:12 +010082/* Promtheus metric type (gauge or counter) */
83enum promex_mt_type {
84 PROMEX_MT_GAUGE = 1,
85 PROMEX_MT_COUNTER = 2,
86};
87
Christopher Fauletf959d082019-02-07 15:38:42 +010088/* The max length for metrics name. It is a hard limit but it should be
Ilya Shipitsince7b00f2020-03-23 22:28:40 +050089 * enough.
Christopher Fauletf959d082019-02-07 15:38:42 +010090 */
91#define PROMEX_MAX_NAME_LEN 128
92
93/* The expected max length for a metric dump, including its header lines. It is
94 * just a soft limit to avoid extra work. We don't try to dump a metric if less
95 * than this size is available in the HTX.
96 */
97#define PROMEX_MAX_METRIC_LENGTH 512
98
Christopher Faulet5a2f9382021-01-28 11:24:17 +010099/* The max number of labels per metric */
100#define PROMEX_MAX_LABELS 8
William Dauchy5a982a72021-01-08 13:18:06 +0100101
Christopher Faulet0312c0d2021-01-20 15:19:12 +0100102/* Describe a prometheus metric */
103struct promex_metric {
104 const struct ist n; /* The metric name */
105 enum promex_mt_type type; /* The metric type (gauge or counter) */
106 unsigned int flags; /* PROMEX_FL_* flags */
107};
108
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100109/* Describe a prometheus metric label. It is just a key/value pair */
110struct promex_label {
111 struct ist name;
112 struct ist value;
113};
114
Christopher Faulet37286a52021-01-20 15:20:53 +0100115/* Global metrics */
116const struct promex_metric promex_global_metrics[INF_TOTAL_FIELDS] = {
117 //[INF_NAME] ignored
118 //[INF_VERSION], ignored
119 //[INF_RELEASE_DATE] ignored
Christopher Faulet37286a52021-01-20 15:20:53 +0100120 [INF_NBTHREAD] = { .n = IST("nbthread"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
121 [INF_NBPROC] = { .n = IST("nbproc"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
122 [INF_PROCESS_NUM] = { .n = IST("relative_process_id"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
123 //[INF_PID] ignored
124 //[INF_UPTIME] ignored
125 [INF_UPTIME_SEC] = { .n = IST("uptime_seconds"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
126 [INF_START_TIME_SEC] = { .n = IST("start_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
William Dauchydf9a05d2021-02-01 13:11:59 +0100127 //[INF_MEMMAX_MB] ignored
Christopher Faulet37286a52021-01-20 15:20:53 +0100128 [INF_MEMMAX_BYTES] = { .n = IST("max_memory_bytes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
William Dauchydf9a05d2021-02-01 13:11:59 +0100129 //[INF_POOL_ALLOC_MB] ignored
Christopher Faulet37286a52021-01-20 15:20:53 +0100130 [INF_POOL_ALLOC_BYTES] = { .n = IST("pool_allocated_bytes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
William Dauchydf9a05d2021-02-01 13:11:59 +0100131 //[INF_POOL_USED_MB] ignored
Christopher Faulet37286a52021-01-20 15:20:53 +0100132 [INF_POOL_USED_BYTES] = { .n = IST("pool_used_bytes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
133 [INF_POOL_FAILED] = { .n = IST("pool_failures_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
134 [INF_ULIMIT_N] = { .n = IST("max_fds"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
135 [INF_MAXSOCK] = { .n = IST("max_sockets"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
136 [INF_MAXCONN] = { .n = IST("max_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
137 [INF_HARD_MAXCONN] = { .n = IST("hard_max_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
138 [INF_CURR_CONN] = { .n = IST("current_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
139 [INF_CUM_CONN] = { .n = IST("connections_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
140 [INF_CUM_REQ] = { .n = IST("requests_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
141 [INF_MAX_SSL_CONNS] = { .n = IST("max_ssl_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
142 [INF_CURR_SSL_CONNS] = { .n = IST("current_ssl_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
143 [INF_CUM_SSL_CONNS] = { .n = IST("ssl_connections_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
144 [INF_MAXPIPES] = { .n = IST("max_pipes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
145 [INF_PIPES_USED] = { .n = IST("pipes_used_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
146 [INF_PIPES_FREE] = { .n = IST("pipes_free_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
147 [INF_CONN_RATE] = { .n = IST("current_connection_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
148 [INF_CONN_RATE_LIMIT] = { .n = IST("limit_connection_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
149 [INF_MAX_CONN_RATE] = { .n = IST("max_connection_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
150 [INF_SESS_RATE] = { .n = IST("current_session_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
151 [INF_SESS_RATE_LIMIT] = { .n = IST("limit_session_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
152 [INF_MAX_SESS_RATE] = { .n = IST("max_session_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
153 [INF_SSL_RATE] = { .n = IST("current_ssl_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
154 [INF_SSL_RATE_LIMIT] = { .n = IST("limit_ssl_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
155 [INF_MAX_SSL_RATE] = { .n = IST("max_ssl_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
156 [INF_SSL_FRONTEND_KEY_RATE] = { .n = IST("current_frontend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
157 [INF_SSL_FRONTEND_MAX_KEY_RATE] = { .n = IST("max_frontend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
158 [INF_SSL_FRONTEND_SESSION_REUSE_PCT] = { .n = IST("frontend_ssl_reuse"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
159 [INF_SSL_BACKEND_KEY_RATE] = { .n = IST("current_backend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
160 [INF_SSL_BACKEND_MAX_KEY_RATE] = { .n = IST("max_backend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
161 [INF_SSL_CACHE_LOOKUPS] = { .n = IST("ssl_cache_lookups_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
162 [INF_SSL_CACHE_MISSES] = { .n = IST("ssl_cache_misses_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
163 [INF_COMPRESS_BPS_IN] = { .n = IST("http_comp_bytes_in_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
164 [INF_COMPRESS_BPS_OUT] = { .n = IST("http_comp_bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
165 [INF_COMPRESS_BPS_RATE_LIM] = { .n = IST("limit_http_comp"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
166 [INF_ZLIB_MEM_USAGE] = { .n = IST("current_zlib_memory"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
167 [INF_MAX_ZLIB_MEM_USAGE] = { .n = IST("max_zlib_memory"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
168 [INF_TASKS] = { .n = IST("current_tasks"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
169 [INF_RUN_QUEUE] = { .n = IST("current_run_queue"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
170 [INF_IDLE_PCT] = { .n = IST("idle_time_percent"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
171 //[INF_NODE] ignored
172 //[INF_DESCRIPTION] ignored
173 [INF_STOPPING] = { .n = IST("stopping"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
174 [INF_JOBS] = { .n = IST("jobs"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
175 [INF_UNSTOPPABLE_JOBS] = { .n = IST("unstoppable_jobs"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
176 [INF_LISTENERS] = { .n = IST("listeners"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
177 [INF_ACTIVE_PEERS] = { .n = IST("active_peers"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
178 [INF_CONNECTED_PEERS] = { .n = IST("connected_peers"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
179 [INF_DROPPED_LOGS] = { .n = IST("dropped_logs_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
180 [INF_BUSY_POLLING] = { .n = IST("busy_polling_enabled"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
181 [INF_FAILED_RESOLUTIONS] = { .n = IST("failed_resolutions"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
182 [INF_TOTAL_BYTES_OUT] = { .n = IST("bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
183 [INF_TOTAL_SPLICED_BYTES_OUT] = { .n = IST("spliced_bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
184 [INF_BYTES_OUT_RATE] = { .n = IST("bytes_out_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
185 //[INF_DEBUG_COMMANDS_ISSUED] ignored
William Dauchy7741c332021-02-01 13:11:57 +0100186 [INF_CUM_LOG_MSGS] = { .n = IST("recv_logs_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
William Dauchydf9a05d2021-02-01 13:11:59 +0100187 [INF_BUILD_INFO] = { .n = IST("build_info"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
Christopher Fauletf959d082019-02-07 15:38:42 +0100188};
189
Christopher Faulet37286a52021-01-20 15:20:53 +0100190/* frontend/backend/server fields */
191const struct promex_metric promex_st_metrics[ST_F_TOTAL_FIELDS] = {
William Dauchy42d7c402021-11-07 10:18:47 +0100192 //[ST_F_PXNAME] ignored
193 //[ST_F_SVNAME] ignored
194 [ST_F_QCUR] = { .n = IST("current_queue"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
195 [ST_F_QMAX] = { .n = IST("max_queue"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
196 [ST_F_SCUR] = { .n = IST("current_sessions"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
197 [ST_F_SMAX] = { .n = IST("max_sessions"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
198 [ST_F_SLIM] = { .n = IST("limit_sessions"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
199 [ST_F_STOT] = { .n = IST("sessions_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
200 [ST_F_BIN] = { .n = IST("bytes_in_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
201 [ST_F_BOUT] = { .n = IST("bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
202 [ST_F_DREQ] = { .n = IST("requests_denied_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC ) },
203 [ST_F_DRESP] = { .n = IST("responses_denied_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
204 [ST_F_EREQ] = { .n = IST("request_errors_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC ) },
205 [ST_F_ECON] = { .n = IST("connection_errors_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
206 [ST_F_ERESP] = { .n = IST("response_errors_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
207 [ST_F_WRETR] = { .n = IST("retry_warnings_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
208 [ST_F_WREDIS] = { .n = IST("redispatch_warnings_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
209 [ST_F_STATUS] = { .n = IST("status"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
210 [ST_F_WEIGHT] = { .n = IST("weight"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
211 [ST_F_ACT] = { .n = IST("active_servers"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC ) },
212 [ST_F_BCK] = { .n = IST("backup_servers"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC ) },
213 [ST_F_CHKFAIL] = { .n = IST("check_failures_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_SRV_METRIC) },
214 [ST_F_CHKDOWN] = { .n = IST("check_up_down_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
215 [ST_F_LASTCHG] = { .n = IST("check_last_change_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
216 [ST_F_DOWNTIME] = { .n = IST("downtime_seconds_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
217 [ST_F_QLIMIT] = { .n = IST("queue_limit"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
218 //[ST_F_PID] ignored
219 //[ST_F_IID] ignored
220 //[ST_F_SID] ignored
221 [ST_F_THROTTLE] = { .n = IST("current_throttle"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
222 [ST_F_LBTOT] = { .n = IST("loadbalanced_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
223 //[ST_F_TRACKED] ignored
224 //[ST_F_TYPE] ignored
225 //[ST_F_RATE] ignored
226 [ST_F_RATE_LIM] = { .n = IST("limit_session_rate"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC ) },
227 [ST_F_RATE_MAX] = { .n = IST("max_session_rate"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
228 [ST_F_CHECK_STATUS] = { .n = IST("check_status"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
229 [ST_F_CHECK_CODE] = { .n = IST("check_code"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
230 [ST_F_CHECK_DURATION] = { .n = IST("check_duration_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
231 [ST_F_HRSP_1XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
232 [ST_F_HRSP_2XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
233 [ST_F_HRSP_3XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
234 [ST_F_HRSP_4XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
235 [ST_F_HRSP_5XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
236 [ST_F_HRSP_OTHER] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
237 //[ST_F_HANAFAIL] ignored
238 //[ST_F_REQ_RATE] ignored
239 [ST_F_REQ_RATE_MAX] = { .n = IST("http_requests_rate_max"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC ) },
240 [ST_F_REQ_TOT] = { .n = IST("http_requests_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
241 [ST_F_CLI_ABRT] = { .n = IST("client_aborts_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
242 [ST_F_SRV_ABRT] = { .n = IST("server_aborts_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
243 [ST_F_COMP_IN] = { .n = IST("http_comp_bytes_in_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
244 [ST_F_COMP_OUT] = { .n = IST("http_comp_bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
245 [ST_F_COMP_BYP] = { .n = IST("http_comp_bytes_bypassed_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
246 [ST_F_COMP_RSP] = { .n = IST("http_comp_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
247 [ST_F_LASTSESS] = { .n = IST("last_session_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
248 //[ST_F_LAST_CHK] ignored
249 //[ST_F_LAST_AGT] ignored
250 [ST_F_QTIME] = { .n = IST("queue_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
251 [ST_F_CTIME] = { .n = IST("connect_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
252 [ST_F_RTIME] = { .n = IST("response_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
253 [ST_F_TTIME] = { .n = IST("total_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
254 //[ST_F_AGENT_STATUS] ignored
255 //[ST_F_AGENT_CODE] ignored
256 //[ST_F_AGENT_DURATION] ignored
257 //[ST_F_CHECK_DESC] ignored
258 //[ST_F_AGENT_DESC] ignored
259 //[ST_F_CHECK_RISE] ignored
260 //[ST_F_CHECK_FALL] ignored
261 //[ST_F_CHECK_HEALTH] ignored
262 //[ST_F_AGENT_RISE] ignored
263 //[ST_F_AGENT_FALL] ignored
264 //[ST_F_AGENT_HEALTH] ignored
265 //[ST_F_ADDR] ignored
266 //[ST_F_COOKIE] ignored
267 //[ST_F_MODE] ignored
268 //[ST_F_ALGO] ignored
269 //[ST_F_CONN_RATE] ignored
270 [ST_F_CONN_RATE_MAX] = { .n = IST("connections_rate_max"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC ) },
271 [ST_F_CONN_TOT] = { .n = IST("connections_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC ) },
272 [ST_F_INTERCEPTED] = { .n = IST("intercepted_requests_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC ) },
273 [ST_F_DCON] = { .n = IST("denied_connections_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC ) },
274 [ST_F_DSES] = { .n = IST("denied_sessions_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC ) },
275 [ST_F_WREW] = { .n = IST("failed_header_rewriting_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
276 [ST_F_CONNECT] = { .n = IST("connection_attempts_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
277 [ST_F_REUSE] = { .n = IST("connection_reuses_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
278 [ST_F_CACHE_LOOKUPS] = { .n = IST("http_cache_lookups_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
279 [ST_F_CACHE_HITS] = { .n = IST("http_cache_hits_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
280 [ST_F_SRV_ICUR] = { .n = IST("idle_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
281 [ST_F_SRV_ILIM] = { .n = IST("idle_connections_limit"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
282 [ST_F_QT_MAX] = { .n = IST("max_queue_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
283 [ST_F_CT_MAX] = { .n = IST("max_connect_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
284 [ST_F_RT_MAX] = { .n = IST("max_response_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
285 [ST_F_TT_MAX] = { .n = IST("max_total_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
286 [ST_F_EINT] = { .n = IST("internal_errors_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
287 [ST_F_IDLE_CONN_CUR] = { .n = IST("unsafe_idle_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
288 [ST_F_SAFE_CONN_CUR] = { .n = IST("safe_idle_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
289 [ST_F_USED_CONN_CUR] = { .n = IST("used_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
290 [ST_F_NEED_CONN_EST] = { .n = IST("need_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
291 [ST_F_UWEIGHT] = { .n = IST("uweight"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
292 [ST_F_AGG_SRV_CHECK_STATUS] = { .n = IST("agg_server_check_status"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC ) },
Christopher Fauletf959d082019-02-07 15:38:42 +0100293};
294
Ilya Shipitsinacf84592021-02-06 22:29:08 +0500295/* Description of overridden stats fields */
Christopher Fauletf959d082019-02-07 15:38:42 +0100296const struct ist promex_st_metric_desc[ST_F_TOTAL_FIELDS] = {
William Dauchya1da7ba2021-02-01 13:11:52 +0100297 [ST_F_STATUS] = IST("Current status of the service, per state label value."),
William Dauchyde3c3262021-02-01 13:11:51 +0100298 [ST_F_CHECK_STATUS] = IST("Status of last health check, per state label value."),
Christopher Fauletf959d082019-02-07 15:38:42 +0100299 [ST_F_CHECK_CODE] = IST("layer5-7 code, if available of the last health check."),
Christopher Faulet2711e512020-02-27 16:12:07 +0100300 [ST_F_CHECK_DURATION] = IST("Total duration of the latest server health check, in seconds."),
Christopher Fauletf959d082019-02-07 15:38:42 +0100301 [ST_F_QTIME] = IST("Avg. queue time for last 1024 successful connections."),
302 [ST_F_CTIME] = IST("Avg. connect time for last 1024 successful connections."),
303 [ST_F_RTIME] = IST("Avg. response time for last 1024 successful connections."),
304 [ST_F_TTIME] = IST("Avg. total time for last 1024 successful connections."),
Christopher Faulet8fc027d2019-11-08 15:05:31 +0100305 [ST_F_QT_MAX] = IST("Maximum observed time spent in the queue"),
306 [ST_F_CT_MAX] = IST("Maximum observed time spent waiting for a connection to complete"),
307 [ST_F_RT_MAX] = IST("Maximum observed time spent waiting for a server response"),
308 [ST_F_TT_MAX] = IST("Maximum observed total request+response time (request+queue+connect+response+processing)"),
Christopher Fauletf959d082019-02-07 15:38:42 +0100309};
310
William Dauchy69164222021-02-07 20:42:38 +0100311/* stick table base fields */
312enum sticktable_field {
313 STICKTABLE_SIZE = 0,
314 STICKTABLE_USED,
315 /* must always be the last one */
316 STICKTABLE_TOTAL_FIELDS
317};
318
319const struct promex_metric promex_sticktable_metrics[STICKTABLE_TOTAL_FIELDS] = {
320 [STICKTABLE_SIZE] = { .n = IST("size"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_STICKTABLE_METRIC },
321 [STICKTABLE_USED] = { .n = IST("used"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_STICKTABLE_METRIC },
322};
323
324/* stick table base description */
325const struct ist promex_sticktable_metric_desc[STICKTABLE_TOTAL_FIELDS] = {
326 [STICKTABLE_SIZE] = IST("Stick table size."),
327 [STICKTABLE_USED] = IST("Number of entries used in this stick table."),
328};
329
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100330/* Specific labels for all ST_F_HRSP_* fields */
331const struct ist promex_hrsp_code[1 + ST_F_HRSP_OTHER - ST_F_HRSP_1XX] = {
332 [ST_F_HRSP_1XX - ST_F_HRSP_1XX] = IST("1xx"),
333 [ST_F_HRSP_2XX - ST_F_HRSP_1XX] = IST("2xx"),
334 [ST_F_HRSP_3XX - ST_F_HRSP_1XX] = IST("3xx"),
335 [ST_F_HRSP_4XX - ST_F_HRSP_1XX] = IST("4xx"),
336 [ST_F_HRSP_5XX - ST_F_HRSP_1XX] = IST("5xx"),
337 [ST_F_HRSP_OTHER - ST_F_HRSP_1XX] = IST("other"),
Christopher Fauletf959d082019-02-07 15:38:42 +0100338};
339
William Dauchy54938212021-01-27 22:40:16 +0100340enum promex_front_state {
341 PROMEX_FRONT_STATE_DOWN = 0,
342 PROMEX_FRONT_STATE_UP,
343
344 PROMEX_FRONT_STATE_COUNT /* must be last */
345};
346
347const struct ist promex_front_st[PROMEX_FRONT_STATE_COUNT] = {
348 [PROMEX_FRONT_STATE_DOWN] = IST("DOWN"),
349 [PROMEX_FRONT_STATE_UP] = IST("UP"),
350};
351
352enum promex_back_state {
353 PROMEX_BACK_STATE_DOWN = 0,
354 PROMEX_BACK_STATE_UP,
355
356 PROMEX_BACK_STATE_COUNT /* must be last */
357};
358
359const struct ist promex_back_st[PROMEX_BACK_STATE_COUNT] = {
360 [PROMEX_BACK_STATE_DOWN] = IST("DOWN"),
361 [PROMEX_BACK_STATE_UP] = IST("UP"),
362};
363
364enum promex_srv_state {
365 PROMEX_SRV_STATE_DOWN = 0,
366 PROMEX_SRV_STATE_UP,
367 PROMEX_SRV_STATE_MAINT,
368 PROMEX_SRV_STATE_DRAIN,
369 PROMEX_SRV_STATE_NOLB,
370
371 PROMEX_SRV_STATE_COUNT /* must be last */
372};
373
374const struct ist promex_srv_st[PROMEX_SRV_STATE_COUNT] = {
375 [PROMEX_SRV_STATE_DOWN] = IST("DOWN"),
376 [PROMEX_SRV_STATE_UP] = IST("UP"),
377 [PROMEX_SRV_STATE_MAINT] = IST("MAINT"),
378 [PROMEX_SRV_STATE_DRAIN] = IST("DRAIN"),
379 [PROMEX_SRV_STATE_NOLB] = IST("NOLB"),
380};
381
382/* Return the server status. */
383enum promex_srv_state promex_srv_status(struct server *sv)
Christopher Fauletf959d082019-02-07 15:38:42 +0100384{
William Dauchy54938212021-01-27 22:40:16 +0100385 int state = PROMEX_SRV_STATE_DOWN;
Christopher Fauletf959d082019-02-07 15:38:42 +0100386
Christopher Fauletf959d082019-02-07 15:38:42 +0100387 if (sv->cur_state == SRV_ST_RUNNING || sv->cur_state == SRV_ST_STARTING) {
William Dauchy54938212021-01-27 22:40:16 +0100388 state = PROMEX_SRV_STATE_UP;
Christopher Fauletf959d082019-02-07 15:38:42 +0100389 if (sv->cur_admin & SRV_ADMF_DRAIN)
William Dauchy54938212021-01-27 22:40:16 +0100390 state = PROMEX_SRV_STATE_DRAIN;
Christopher Fauletf959d082019-02-07 15:38:42 +0100391 }
Christopher Fauletd45d1052019-09-06 16:10:19 +0200392 else if (sv->cur_state == SRV_ST_STOPPING)
William Dauchy54938212021-01-27 22:40:16 +0100393 state = PROMEX_SRV_STATE_NOLB;
Christopher Fauletd45d1052019-09-06 16:10:19 +0200394
395 if (sv->cur_admin & SRV_ADMF_MAINT)
William Dauchy54938212021-01-27 22:40:16 +0100396 state = PROMEX_SRV_STATE_MAINT;
Christopher Fauletf959d082019-02-07 15:38:42 +0100397
398 return state;
399}
400
401/* Convert a field to its string representation and write it in <out>, followed
402 * by a newline, if there is enough space. non-numeric value are converted in
William Dauchy18a2c6e2021-01-22 21:09:47 +0100403 * "NaN" because Prometheus only support numerical values (but it is unexepceted
Christopher Fauletf959d082019-02-07 15:38:42 +0100404 * to process this kind of value). It returns 1 on success. Otherwise, it
405 * returns 0. The buffer's length must not exceed <max> value.
406 */
407static int promex_metric_to_str(struct buffer *out, struct field *f, size_t max)
408{
409 int ret = 0;
410
411 switch (field_format(f, 0)) {
William Dauchy18a2c6e2021-01-22 21:09:47 +0100412 case FF_EMPTY: ret = chunk_strcat(out, "NaN\n"); break;
Christopher Fauletf959d082019-02-07 15:38:42 +0100413 case FF_S32: ret = chunk_appendf(out, "%d\n", f->u.s32); break;
414 case FF_U32: ret = chunk_appendf(out, "%u\n", f->u.u32); break;
415 case FF_S64: ret = chunk_appendf(out, "%lld\n", (long long)f->u.s64); break;
416 case FF_U64: ret = chunk_appendf(out, "%llu\n", (unsigned long long)f->u.u64); break;
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200417 case FF_FLT: ret = chunk_appendf(out, "%f\n", f->u.flt); break;
William Dauchy18a2c6e2021-01-22 21:09:47 +0100418 case FF_STR: ret = chunk_strcat(out, "NaN\n"); break;
419 default: ret = chunk_strcat(out, "NaN\n"); break;
Christopher Fauletf959d082019-02-07 15:38:42 +0100420 }
421 if (!ret || out->data > max)
422 return 0;
423 return 1;
424}
425
Christopher Fauletf959d082019-02-07 15:38:42 +0100426/* Dump the header lines for <metric>. It is its #HELP and #TYPE strings. It
427 * returns 1 on success. Otherwise, if <out> length exceeds <max>, it returns 0.
428 */
429static int promex_dump_metric_header(struct appctx *appctx, struct htx *htx,
Christopher Faulet37286a52021-01-20 15:20:53 +0100430 const struct promex_metric *metric, const struct ist name,
431 struct ist *out, size_t max)
Christopher Fauletf959d082019-02-07 15:38:42 +0100432{
Christopher Faulet37286a52021-01-20 15:20:53 +0100433 struct ist type;
William Dauchy82b2ce22021-02-01 13:11:55 +0100434 struct ist desc;
Christopher Faulet37286a52021-01-20 15:20:53 +0100435
436 switch (metric->type) {
437 case PROMEX_MT_COUNTER:
438 type = ist("counter");
439 break;
440 default:
441 type = ist("gauge");
442 }
Christopher Fauletf959d082019-02-07 15:38:42 +0100443
William Dauchya191b772021-01-15 22:41:39 +0100444 if (istcat(out, ist("# HELP "), max) == -1 ||
445 istcat(out, name, max) == -1 ||
446 istcat(out, ist(" "), max) == -1)
447 goto full;
448
William Dauchy82b2ce22021-02-01 13:11:55 +0100449 if (metric->flags & PROMEX_FL_INFO_METRIC)
450 desc = ist(info_fields[appctx->st2].desc);
William Dauchy69164222021-02-07 20:42:38 +0100451 else if (metric->flags & PROMEX_FL_STICKTABLE_METRIC)
452 desc = promex_sticktable_metric_desc[appctx->st2];
William Dauchy82b2ce22021-02-01 13:11:55 +0100453 else if (!isttest(promex_st_metric_desc[appctx->st2]))
454 desc = ist(stat_fields[appctx->st2].desc);
455 else
456 desc = promex_st_metric_desc[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100457
William Dauchy82b2ce22021-02-01 13:11:55 +0100458 if (istcat(out, desc, max) == -1 ||
459 istcat(out, ist("\n# TYPE "), max) == -1 ||
Christopher Fauletf959d082019-02-07 15:38:42 +0100460 istcat(out, name, max) == -1 ||
461 istcat(out, ist(" "), max) == -1 ||
Christopher Faulet37286a52021-01-20 15:20:53 +0100462 istcat(out, type, max) == -1 ||
Christopher Fauletf959d082019-02-07 15:38:42 +0100463 istcat(out, ist("\n"), max) == -1)
464 goto full;
465
466 return 1;
467
468 full:
469 return 0;
470}
471
472/* Dump the line for <metric>. It starts by the metric name followed by its
473 * labels (proxy name, server name...) between braces and finally its value. If
474 * not already done, the header lines are dumped first. It returns 1 on
475 * success. Otherwise if <out> length exceeds <max>, it returns 0.
476 */
Christopher Faulet37286a52021-01-20 15:20:53 +0100477static int promex_dump_metric(struct appctx *appctx, struct htx *htx, struct ist prefix,
William Dauchyc6464592021-01-27 22:40:17 +0100478 const struct promex_metric *metric, struct field *val,
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100479 struct promex_label *labels, struct ist *out, size_t max)
Christopher Fauletf959d082019-02-07 15:38:42 +0100480{
481 struct ist name = { .ptr = (char[PROMEX_MAX_NAME_LEN]){ 0 }, .len = 0 };
482 size_t len = out->len;
483
484 if (out->len + PROMEX_MAX_METRIC_LENGTH > max)
485 return 0;
486
Christopher Faulet37286a52021-01-20 15:20:53 +0100487 /* Fill the metric name */
488 istcat(&name, prefix, PROMEX_MAX_NAME_LEN);
489 istcat(&name, metric->n, PROMEX_MAX_NAME_LEN);
490
491
Christopher Fauletf959d082019-02-07 15:38:42 +0100492 if ((appctx->ctx.stats.flags & PROMEX_FL_METRIC_HDR) &&
Christopher Faulet37286a52021-01-20 15:20:53 +0100493 !promex_dump_metric_header(appctx, htx, metric, name, out, max))
Christopher Fauletf959d082019-02-07 15:38:42 +0100494 goto full;
495
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100496 if (istcat(out, name, max) == -1)
497 goto full;
Christopher Fauletf959d082019-02-07 15:38:42 +0100498
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100499 if (isttest(labels[0].name)) {
500 int i;
501
502 if (istcat(out, ist("{"), max) == -1)
Christopher Fauletf959d082019-02-07 15:38:42 +0100503 goto full;
Christopher Fauletf959d082019-02-07 15:38:42 +0100504
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100505 for (i = 0; isttest(labels[i].name); i++) {
506 if (!isttest(labels[i].value))
507 continue;
508
509 if ((i && istcat(out, ist(","), max) == -1) ||
510 istcat(out, labels[i].name, max) == -1 ||
511 istcat(out, ist("=\""), max) == -1 ||
512 istcat(out, labels[i].value, max) == -1 ||
513 istcat(out, ist("\""), max) == -1)
514 goto full;
515 }
516
517 if (istcat(out, ist("}"), max) == -1)
Christopher Fauletf959d082019-02-07 15:38:42 +0100518 goto full;
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100519
Christopher Fauletf959d082019-02-07 15:38:42 +0100520 }
521
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100522 if (istcat(out, ist(" "), max) == -1)
523 goto full;
524
Christopher Fauletf959d082019-02-07 15:38:42 +0100525 trash.data = out->len;
Christopher Faulet37286a52021-01-20 15:20:53 +0100526 if (!promex_metric_to_str(&trash, val, max))
Christopher Fauletf959d082019-02-07 15:38:42 +0100527 goto full;
528 out->len = trash.data;
529
530 appctx->ctx.stats.flags &= ~PROMEX_FL_METRIC_HDR;
531 return 1;
532 full:
533 // Restore previous length
534 out->len = len;
535 return 0;
536
537}
538
539
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500540/* Dump global metrics (prefixed by "haproxy_process_"). It returns 1 on success,
Christopher Fauletf959d082019-02-07 15:38:42 +0100541 * 0 if <htx> is full and -1 in case of any error. */
542static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
543{
544 static struct ist prefix = IST("haproxy_process_");
Christopher Faulet37286a52021-01-20 15:20:53 +0100545 struct field val;
Christopher Fauletf959d082019-02-07 15:38:42 +0100546 struct channel *chn = si_ic(appctx->owner);
547 struct ist out = ist2(trash.area, 0);
Christopher Faulet11921e62019-07-03 11:43:17 +0200548 size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
Christopher Fauletf959d082019-02-07 15:38:42 +0100549 int ret = 1;
550
Willy Tarreau0b26b382021-05-08 07:43:53 +0200551 if (!stats_fill_info(info, INF_TOTAL_FIELDS, 0))
William Dauchy5d9b8f32021-01-11 20:07:49 +0100552 return -1;
Christopher Fauletf959d082019-02-07 15:38:42 +0100553
Christopher Faulet37286a52021-01-20 15:20:53 +0100554 for (; appctx->st2 < INF_TOTAL_FIELDS; appctx->st2++) {
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100555 struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
556
Christopher Faulet37286a52021-01-20 15:20:53 +0100557 if (!(promex_global_metrics[appctx->st2].flags & appctx->ctx.stats.flags))
558 continue;
559
Christopher Fauletf959d082019-02-07 15:38:42 +0100560 switch (appctx->st2) {
William Dauchy5a982a72021-01-08 13:18:06 +0100561 case INF_BUILD_INFO:
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100562 labels[0].name = ist("version");
563 labels[0].value = ist(HAPROXY_VERSION);
Christopher Faulet37286a52021-01-20 15:20:53 +0100564 val = mkf_u32(FN_GAUGE, 1);
William Dauchy5a982a72021-01-08 13:18:06 +0100565 break;
Christopher Fauletf959d082019-02-07 15:38:42 +0100566
567 default:
Christopher Faulet37286a52021-01-20 15:20:53 +0100568 val = info[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100569 }
570
William Dauchyc6464592021-01-27 22:40:17 +0100571 if (!promex_dump_metric(appctx, htx, prefix, &promex_global_metrics[appctx->st2],
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100572 &val, labels, &out, max))
Christopher Fauletf959d082019-02-07 15:38:42 +0100573 goto full;
574
Christopher Fauletf959d082019-02-07 15:38:42 +0100575 appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
Christopher Fauletf959d082019-02-07 15:38:42 +0100576 }
577
578 end:
Christopher Faulet0c55a152019-07-04 10:03:28 +0200579 if (out.len) {
580 if (!htx_add_data_atonce(htx, out))
581 return -1; /* Unexpected and unrecoverable error */
582 channel_add_input(chn, out.len);
583 }
Christopher Fauletf959d082019-02-07 15:38:42 +0100584 return ret;
585 full:
586 ret = 0;
587 goto end;
588}
589
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500590/* Dump frontends metrics (prefixed by "haproxy_frontend_"). It returns 1 on success,
Christopher Fauletf959d082019-02-07 15:38:42 +0100591 * 0 if <htx> is full and -1 in case of any error. */
592static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
593{
594 static struct ist prefix = IST("haproxy_frontend_");
595 struct proxy *px;
Christopher Faulet37286a52021-01-20 15:20:53 +0100596 struct field val;
Christopher Fauletf959d082019-02-07 15:38:42 +0100597 struct channel *chn = si_ic(appctx->owner);
598 struct ist out = ist2(trash.area, 0);
Christopher Faulet11921e62019-07-03 11:43:17 +0200599 size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
William Dauchyb9577452021-01-17 18:27:46 +0100600 struct field *stats = stat_l[STATS_DOMAIN_PROXY];
Christopher Fauletf959d082019-02-07 15:38:42 +0100601 int ret = 1;
William Dauchyc6464592021-01-27 22:40:17 +0100602 enum promex_front_state state;
Christopher Fauletf959d082019-02-07 15:38:42 +0100603
Christopher Faulet37286a52021-01-20 15:20:53 +0100604 for (;appctx->st2 < ST_F_TOTAL_FIELDS; appctx->st2++) {
605 if (!(promex_st_metrics[appctx->st2].flags & appctx->ctx.stats.flags))
606 continue;
607
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200608 while (appctx->ctx.stats.obj1) {
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100609 struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
610
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200611 px = appctx->ctx.stats.obj1;
Christopher Fauletf959d082019-02-07 15:38:42 +0100612
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100613 labels[0].name = ist("proxy");
614 labels[0].value = ist2(px->id, strlen(px->id));
615
Christopher Fauletf959d082019-02-07 15:38:42 +0100616 /* skip the disabled proxies, global frontend and non-networked ones */
Christopher Fauletdfd10ab2021-10-06 14:24:19 +0200617 if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_FE))
Christopher Fauletf959d082019-02-07 15:38:42 +0100618 goto next_px;
619
William Dauchyb9577452021-01-17 18:27:46 +0100620 if (!stats_fill_fe_stats(px, stats, ST_F_TOTAL_FIELDS, &(appctx->st2)))
621 return -1;
622
Christopher Fauletf959d082019-02-07 15:38:42 +0100623 switch (appctx->st2) {
624 case ST_F_STATUS:
Christopher Fauletdfd10ab2021-10-06 14:24:19 +0200625 state = !(px->flags & PR_FL_STOPPED);
Christopher Faulet040b1192021-02-01 15:05:21 +0100626 for (; appctx->ctx.stats.st_code < PROMEX_FRONT_STATE_COUNT; appctx->ctx.stats.st_code++) {
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100627 labels[1].name = ist("state");
Christopher Faulet040b1192021-02-01 15:05:21 +0100628 labels[1].value = promex_front_st[appctx->ctx.stats.st_code];
629 val = mkf_u32(FO_STATUS, state == appctx->ctx.stats.st_code);
William Dauchyc6464592021-01-27 22:40:17 +0100630 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100631 &val, labels, &out, max))
William Dauchyc6464592021-01-27 22:40:17 +0100632 goto full;
633 }
Christopher Faulet040b1192021-02-01 15:05:21 +0100634 appctx->ctx.stats.st_code = 0;
William Dauchyc6464592021-01-27 22:40:17 +0100635 goto next_px;
Christopher Fauletf959d082019-02-07 15:38:42 +0100636 case ST_F_REQ_RATE_MAX:
Christopher Fauletf959d082019-02-07 15:38:42 +0100637 case ST_F_REQ_TOT:
Christopher Fauletf959d082019-02-07 15:38:42 +0100638 case ST_F_INTERCEPTED:
Christopher Fauletf959d082019-02-07 15:38:42 +0100639 case ST_F_CACHE_LOOKUPS:
Christopher Fauletf959d082019-02-07 15:38:42 +0100640 case ST_F_CACHE_HITS:
Christopher Fauletf959d082019-02-07 15:38:42 +0100641 case ST_F_COMP_IN:
Christopher Fauletf959d082019-02-07 15:38:42 +0100642 case ST_F_COMP_OUT:
Christopher Fauletf959d082019-02-07 15:38:42 +0100643 case ST_F_COMP_BYP:
William Dauchyb9577452021-01-17 18:27:46 +0100644 case ST_F_COMP_RSP:
Christopher Fauletf959d082019-02-07 15:38:42 +0100645 if (px->mode != PR_MODE_HTTP)
646 goto next_px;
Christopher Faulet37286a52021-01-20 15:20:53 +0100647 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100648 break;
Christopher Faulet32ef48e2021-02-01 14:55:37 +0100649 case ST_F_HRSP_1XX:
William Dauchyb9577452021-01-17 18:27:46 +0100650 case ST_F_HRSP_2XX:
651 case ST_F_HRSP_3XX:
652 case ST_F_HRSP_4XX:
653 case ST_F_HRSP_5XX:
654 case ST_F_HRSP_OTHER:
Christopher Fauletf959d082019-02-07 15:38:42 +0100655 if (px->mode != PR_MODE_HTTP)
656 goto next_px;
Christopher Faulet32ef48e2021-02-01 14:55:37 +0100657 if (appctx->st2 != ST_F_HRSP_1XX)
658 appctx->ctx.stats.flags &= ~PROMEX_FL_METRIC_HDR;
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100659 labels[1].name = ist("code");
660 labels[1].value = promex_hrsp_code[appctx->st2 - ST_F_HRSP_1XX];
661 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100662 break;
663
664 default:
Christopher Faulet37286a52021-01-20 15:20:53 +0100665 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100666 }
667
William Dauchyc6464592021-01-27 22:40:17 +0100668 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100669 &val, labels, &out, max))
Christopher Fauletf959d082019-02-07 15:38:42 +0100670 goto full;
671 next_px:
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200672 appctx->ctx.stats.obj1 = px->next;
Christopher Fauletf959d082019-02-07 15:38:42 +0100673 }
Christopher Fauletf959d082019-02-07 15:38:42 +0100674 appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200675 appctx->ctx.stats.obj1 = proxies_list;
Christopher Fauletf959d082019-02-07 15:38:42 +0100676 }
677
678 end:
Christopher Faulet0c55a152019-07-04 10:03:28 +0200679 if (out.len) {
680 if (!htx_add_data_atonce(htx, out))
681 return -1; /* Unexpected and unrecoverable error */
682 channel_add_input(chn, out.len);
683 }
Christopher Fauletf959d082019-02-07 15:38:42 +0100684 return ret;
685 full:
686 ret = 0;
687 goto end;
688}
689
William Dauchye3f7bd52021-02-14 23:22:56 +0100690/* Dump listener metrics (prefixed by "haproxy_listen_"). It returns 1 on
691 * success, 0 if <htx> is full and -1 in case of any error. */
692static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
693{
694 static struct ist prefix = IST("haproxy_listener_");
695 struct proxy *px;
696 struct field val;
697 struct channel *chn = si_ic(appctx->owner);
698 struct ist out = ist2(trash.area, 0);
699 size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
700 struct field *stats = stat_l[STATS_DOMAIN_PROXY];
701 struct listener *li;
702 int ret = 1;
703 enum li_status status;
704
705 for (;appctx->st2 < ST_F_TOTAL_FIELDS; appctx->st2++) {
706 if (!(promex_st_metrics[appctx->st2].flags & appctx->ctx.stats.flags))
707 continue;
708
709 while (appctx->ctx.stats.obj1) {
710 struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
711
712 px = appctx->ctx.stats.obj1;
713
714 labels[0].name = ist("proxy");
715 labels[0].value = ist2(px->id, strlen(px->id));
716
717 /* skip the disabled proxies, global frontend and non-networked ones */
Christopher Fauletdfd10ab2021-10-06 14:24:19 +0200718 if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_FE))
William Dauchye3f7bd52021-02-14 23:22:56 +0100719 goto next_px;
720
721 li = appctx->ctx.stats.obj2;
722 list_for_each_entry_from(li, &px->conf.listeners, by_fe) {
723
William Dauchye3f7bd52021-02-14 23:22:56 +0100724 if (!li->counters)
725 continue;
726
William Dauchybaf22732021-02-25 00:53:13 +0100727 labels[1].name = ist("listener");
728 labels[1].value = ist2(li->name, strlen(li->name));
729
William Dauchye3f7bd52021-02-14 23:22:56 +0100730 if (!stats_fill_li_stats(px, li, 0, stats,
731 ST_F_TOTAL_FIELDS, &(appctx->st2)))
732 return -1;
733
734 switch (appctx->st2) {
735 case ST_F_STATUS:
736 status = get_li_status(li);
737 for (; appctx->ctx.stats.st_code < LI_STATE_COUNT; appctx->ctx.stats.st_code++) {
738 val = mkf_u32(FO_STATUS, status == appctx->ctx.stats.st_code);
739 labels[2].name = ist("state");
740 labels[2].value = ist(li_status_st[appctx->ctx.stats.st_code]);
741 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
742 &val, labels, &out, max))
743 goto full;
744 }
745 appctx->ctx.stats.st_code = 0;
746 continue;
747 default:
748 val = stats[appctx->st2];
749 }
750
751 if (!promex_dump_metric(appctx, htx, prefix,
752 &promex_st_metrics[appctx->st2],
753 &val, labels, &out, max))
754 goto full;
755 }
756
757 next_px:
758 px = px->next;
759 appctx->ctx.stats.obj1 = px;
760 appctx->ctx.stats.obj2 = (px ? LIST_NEXT(&px->conf.listeners, struct listener *, by_fe) : NULL);
761 }
762 appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
763 appctx->ctx.stats.obj1 = proxies_list;
764 appctx->ctx.stats.obj2 = LIST_NEXT(&proxies_list->conf.listeners, struct listener *, by_fe);
765 }
766
767 end:
768 if (out.len) {
769 if (!htx_add_data_atonce(htx, out))
770 return -1; /* Unexpected and unrecoverable error */
771 channel_add_input(chn, out.len);
772 }
773 return ret;
774 full:
775 appctx->ctx.stats.obj2 = li;
776 ret = 0;
777 goto end;
778}
779
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500780/* Dump backends metrics (prefixed by "haproxy_backend_"). It returns 1 on success,
Christopher Fauletf959d082019-02-07 15:38:42 +0100781 * 0 if <htx> is full and -1 in case of any error. */
782static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
783{
784 static struct ist prefix = IST("haproxy_backend_");
785 struct proxy *px;
William Dauchy42d7c402021-11-07 10:18:47 +0100786 struct server *sv;
Christopher Faulet37286a52021-01-20 15:20:53 +0100787 struct field val;
Christopher Fauletf959d082019-02-07 15:38:42 +0100788 struct channel *chn = si_ic(appctx->owner);
789 struct ist out = ist2(trash.area, 0);
Christopher Faulet11921e62019-07-03 11:43:17 +0200790 size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
William Dauchy3c6f0062021-01-25 17:29:02 +0100791 struct field *stats = stat_l[STATS_DOMAIN_PROXY];
Christopher Fauletf959d082019-02-07 15:38:42 +0100792 int ret = 1;
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200793 double secs;
William Dauchy42d7c402021-11-07 10:18:47 +0100794 enum promex_back_state bkd_state;
795 enum promex_srv_state srv_state;
Christopher Fauletf959d082019-02-07 15:38:42 +0100796
Christopher Faulet37286a52021-01-20 15:20:53 +0100797 for (;appctx->st2 < ST_F_TOTAL_FIELDS; appctx->st2++) {
798 if (!(promex_st_metrics[appctx->st2].flags & appctx->ctx.stats.flags))
799 continue;
800
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200801 while (appctx->ctx.stats.obj1) {
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100802 struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
William Dauchy42d7c402021-11-07 10:18:47 +0100803 unsigned int srv_state_count[PROMEX_SRV_STATE_COUNT] = { 0 };
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100804
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200805 px = appctx->ctx.stats.obj1;
Christopher Fauletf959d082019-02-07 15:38:42 +0100806
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100807 labels[0].name = ist("proxy");
808 labels[0].value = ist2(px->id, strlen(px->id));
809
Christopher Fauletf959d082019-02-07 15:38:42 +0100810 /* skip the disabled proxies, global frontend and non-networked ones */
Christopher Fauletdfd10ab2021-10-06 14:24:19 +0200811 if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
Christopher Fauletf959d082019-02-07 15:38:42 +0100812 goto next_px;
813
William Dauchy3c6f0062021-01-25 17:29:02 +0100814 if (!stats_fill_be_stats(px, 0, stats, ST_F_TOTAL_FIELDS, &(appctx->st2)))
815 return -1;
816
Christopher Fauletf959d082019-02-07 15:38:42 +0100817 switch (appctx->st2) {
William Dauchy42d7c402021-11-07 10:18:47 +0100818 case ST_F_AGG_SRV_CHECK_STATUS:
819 if (!px->srv)
820 goto next_px;
821 sv = px->srv;
822 while (sv) {
823 srv_state = promex_srv_status(sv);
824 srv_state_count[srv_state] += 1;
825 sv = sv->next;
826 }
827 for (; appctx->ctx.stats.st_code < PROMEX_SRV_STATE_COUNT; appctx->ctx.stats.st_code++) {
828 val = mkf_u32(FN_GAUGE, srv_state_count[appctx->ctx.stats.st_code]);
829 labels[1].name = ist("state");
830 labels[1].value = promex_srv_st[appctx->ctx.stats.st_code];
831 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
832 &val, labels, &out, max))
833 goto full;
834 }
835 appctx->ctx.stats.st_code = 0;
836 goto next_px;
Christopher Fauletf959d082019-02-07 15:38:42 +0100837 case ST_F_STATUS:
William Dauchy42d7c402021-11-07 10:18:47 +0100838 bkd_state = ((px->lbprm.tot_weight > 0 || !px->srv) ? 1 : 0);
Christopher Faulet040b1192021-02-01 15:05:21 +0100839 for (; appctx->ctx.stats.st_code < PROMEX_BACK_STATE_COUNT; appctx->ctx.stats.st_code++) {
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100840 labels[1].name = ist("state");
Christopher Faulet040b1192021-02-01 15:05:21 +0100841 labels[1].value = promex_back_st[appctx->ctx.stats.st_code];
William Dauchy42d7c402021-11-07 10:18:47 +0100842 val = mkf_u32(FO_STATUS, bkd_state == appctx->ctx.stats.st_code);
William Dauchyc6464592021-01-27 22:40:17 +0100843 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100844 &val, labels, &out, max))
William Dauchyc6464592021-01-27 22:40:17 +0100845 goto full;
846 }
Christopher Faulet040b1192021-02-01 15:05:21 +0100847 appctx->ctx.stats.st_code = 0;
William Dauchyc6464592021-01-27 22:40:17 +0100848 goto next_px;
Christopher Fauletf959d082019-02-07 15:38:42 +0100849 case ST_F_QTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200850 secs = (double)swrate_avg(px->be_counters.q_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100851 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +0100852 break;
853 case ST_F_CTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200854 secs = (double)swrate_avg(px->be_counters.c_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100855 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +0100856 break;
857 case ST_F_RTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200858 secs = (double)swrate_avg(px->be_counters.d_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100859 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +0100860 break;
861 case ST_F_TTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200862 secs = (double)swrate_avg(px->be_counters.t_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100863 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +0100864 break;
Christopher Faulet8fc027d2019-11-08 15:05:31 +0100865 case ST_F_QT_MAX:
866 secs = (double)px->be_counters.qtime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100867 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +0100868 break;
869 case ST_F_CT_MAX:
870 secs = (double)px->be_counters.ctime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100871 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +0100872 break;
873 case ST_F_RT_MAX:
874 secs = (double)px->be_counters.dtime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100875 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +0100876 break;
877 case ST_F_TT_MAX:
878 secs = (double)px->be_counters.ttime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100879 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +0100880 break;
Christopher Fauletf959d082019-02-07 15:38:42 +0100881 case ST_F_REQ_TOT:
William Dauchy3c6f0062021-01-25 17:29:02 +0100882 case ST_F_CACHE_LOOKUPS:
883 case ST_F_CACHE_HITS:
884 case ST_F_COMP_IN:
885 case ST_F_COMP_OUT:
886 case ST_F_COMP_BYP:
887 case ST_F_COMP_RSP:
Christopher Fauletf959d082019-02-07 15:38:42 +0100888 if (px->mode != PR_MODE_HTTP)
889 goto next_px;
William Dauchy3c6f0062021-01-25 17:29:02 +0100890 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100891 break;
Christopher Faulet32ef48e2021-02-01 14:55:37 +0100892 case ST_F_HRSP_1XX:
Christopher Fauletf959d082019-02-07 15:38:42 +0100893 case ST_F_HRSP_2XX:
Christopher Fauletf959d082019-02-07 15:38:42 +0100894 case ST_F_HRSP_3XX:
Christopher Fauletf959d082019-02-07 15:38:42 +0100895 case ST_F_HRSP_4XX:
Christopher Fauletf959d082019-02-07 15:38:42 +0100896 case ST_F_HRSP_5XX:
Christopher Fauletf959d082019-02-07 15:38:42 +0100897 case ST_F_HRSP_OTHER:
898 if (px->mode != PR_MODE_HTTP)
899 goto next_px;
Christopher Faulet32ef48e2021-02-01 14:55:37 +0100900 if (appctx->st2 != ST_F_HRSP_1XX)
901 appctx->ctx.stats.flags &= ~PROMEX_FL_METRIC_HDR;
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100902 labels[1].name = ist("code");
903 labels[1].value = promex_hrsp_code[appctx->st2 - ST_F_HRSP_1XX];
William Dauchy3c6f0062021-01-25 17:29:02 +0100904 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100905 break;
906
907 default:
William Dauchy3c6f0062021-01-25 17:29:02 +0100908 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +0100909 }
910
William Dauchy3c6f0062021-01-25 17:29:02 +0100911 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100912 &val, labels, &out, max))
Christopher Fauletf959d082019-02-07 15:38:42 +0100913 goto full;
914 next_px:
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200915 appctx->ctx.stats.obj1 = px->next;
Christopher Fauletf959d082019-02-07 15:38:42 +0100916 }
Christopher Fauletf959d082019-02-07 15:38:42 +0100917 appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200918 appctx->ctx.stats.obj1 = proxies_list;
Christopher Fauletf959d082019-02-07 15:38:42 +0100919 }
920
921 end:
Christopher Faulet0c55a152019-07-04 10:03:28 +0200922 if (out.len) {
923 if (!htx_add_data_atonce(htx, out))
924 return -1; /* Unexpected and unrecoverable error */
925 channel_add_input(chn, out.len);
926 }
Christopher Fauletf959d082019-02-07 15:38:42 +0100927 return ret;
928 full:
929 ret = 0;
930 goto end;
931}
932
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500933/* Dump servers metrics (prefixed by "haproxy_server_"). It returns 1 on success,
Christopher Fauletf959d082019-02-07 15:38:42 +0100934 * 0 if <htx> is full and -1 in case of any error. */
935static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
936{
937 static struct ist prefix = IST("haproxy_server_");
938 struct proxy *px;
939 struct server *sv;
Christopher Faulet37286a52021-01-20 15:20:53 +0100940 struct field val;
Christopher Fauletf959d082019-02-07 15:38:42 +0100941 struct channel *chn = si_ic(appctx->owner);
942 struct ist out = ist2(trash.area, 0);
Christopher Faulet11921e62019-07-03 11:43:17 +0200943 size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
William Dauchybde2bf62021-01-25 17:29:04 +0100944 struct field *stats = stat_l[STATS_DOMAIN_PROXY];
Christopher Fauletf959d082019-02-07 15:38:42 +0100945 int ret = 1;
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200946 double secs;
William Dauchyc6464592021-01-27 22:40:17 +0100947 enum promex_srv_state state;
William Dauchyde3c3262021-02-01 13:11:51 +0100948 const char *check_state;
Christopher Fauletf959d082019-02-07 15:38:42 +0100949
Christopher Faulet37286a52021-01-20 15:20:53 +0100950 for (;appctx->st2 < ST_F_TOTAL_FIELDS; appctx->st2++) {
951 if (!(promex_st_metrics[appctx->st2].flags & appctx->ctx.stats.flags))
952 continue;
953
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200954 while (appctx->ctx.stats.obj1) {
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100955 struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
956
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200957 px = appctx->ctx.stats.obj1;
Christopher Fauletf959d082019-02-07 15:38:42 +0100958
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100959 labels[0].name = ist("proxy");
960 labels[0].value = ist2(px->id, strlen(px->id));
961
Christopher Fauletf959d082019-02-07 15:38:42 +0100962 /* skip the disabled proxies, global frontend and non-networked ones */
Christopher Fauletdfd10ab2021-10-06 14:24:19 +0200963 if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
Christopher Fauletf959d082019-02-07 15:38:42 +0100964 goto next_px;
965
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +0200966 while (appctx->ctx.stats.obj2) {
967 sv = appctx->ctx.stats.obj2;
Christopher Fauletf959d082019-02-07 15:38:42 +0100968
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100969 labels[1].name = ist("server");
970 labels[1].value = ist2(sv->id, strlen(sv->id));
971
William Dauchybde2bf62021-01-25 17:29:04 +0100972 if (!stats_fill_sv_stats(px, sv, 0, stats, ST_F_TOTAL_FIELDS, &(appctx->st2)))
973 return -1;
974
Christopher Fauleteba22942019-11-19 14:18:24 +0100975 if ((appctx->ctx.stats.flags & PROMEX_FL_NO_MAINT_SRV) && (sv->cur_admin & SRV_ADMF_MAINT))
976 goto next_sv;
977
Christopher Fauletf959d082019-02-07 15:38:42 +0100978 switch (appctx->st2) {
979 case ST_F_STATUS:
William Dauchyc6464592021-01-27 22:40:17 +0100980 state = promex_srv_status(sv);
William Dauchy64a38052021-02-14 22:26:24 +0100981 for (; appctx->ctx.stats.st_code < PROMEX_SRV_STATE_COUNT; appctx->ctx.stats.st_code++) {
Christopher Faulet040b1192021-02-01 15:05:21 +0100982 val = mkf_u32(FO_STATUS, state == appctx->ctx.stats.st_code);
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100983 labels[2].name = ist("state");
Christopher Faulet040b1192021-02-01 15:05:21 +0100984 labels[2].value = promex_srv_st[appctx->ctx.stats.st_code];
William Dauchyc6464592021-01-27 22:40:17 +0100985 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
Christopher Faulet5a2f9382021-01-28 11:24:17 +0100986 &val, labels, &out, max))
William Dauchyc6464592021-01-27 22:40:17 +0100987 goto full;
988 }
Christopher Faulet040b1192021-02-01 15:05:21 +0100989 appctx->ctx.stats.st_code = 0;
William Dauchyc6464592021-01-27 22:40:17 +0100990 goto next_sv;
Christopher Fauletf959d082019-02-07 15:38:42 +0100991 case ST_F_QTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200992 secs = (double)swrate_avg(sv->counters.q_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100993 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +0100994 break;
995 case ST_F_CTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +0200996 secs = (double)swrate_avg(sv->counters.c_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +0100997 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +0100998 break;
999 case ST_F_RTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +02001000 secs = (double)swrate_avg(sv->counters.d_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001001 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +01001002 break;
1003 case ST_F_TTIME:
Christopher Fauletaf4bf142019-09-24 16:35:19 +02001004 secs = (double)swrate_avg(sv->counters.t_time, TIME_STATS_SAMPLES) / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001005 val = mkf_flt(FN_AVG, secs);
Christopher Fauletf959d082019-02-07 15:38:42 +01001006 break;
Christopher Faulet8fc027d2019-11-08 15:05:31 +01001007 case ST_F_QT_MAX:
1008 secs = (double)sv->counters.qtime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001009 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +01001010 break;
1011 case ST_F_CT_MAX:
1012 secs = (double)sv->counters.ctime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001013 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +01001014 break;
1015 case ST_F_RT_MAX:
1016 secs = (double)sv->counters.dtime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001017 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +01001018 break;
1019 case ST_F_TT_MAX:
1020 secs = (double)sv->counters.ttime_max / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001021 val = mkf_flt(FN_MAX, secs);
Christopher Faulet8fc027d2019-11-08 15:05:31 +01001022 break;
Christopher Fauletcf403f32019-11-21 14:35:46 +01001023 case ST_F_CHECK_STATUS:
1024 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
1025 goto next_sv;
William Dauchyde3c3262021-02-01 13:11:51 +01001026
Christopher Faulet040b1192021-02-01 15:05:21 +01001027 for (; appctx->ctx.stats.st_code < HCHK_STATUS_SIZE; appctx->ctx.stats.st_code++) {
1028 if (get_check_status_result(appctx->ctx.stats.st_code) < CHK_RES_FAILED)
William Dauchyde3c3262021-02-01 13:11:51 +01001029 continue;
Christopher Faulet040b1192021-02-01 15:05:21 +01001030 val = mkf_u32(FO_STATUS, sv->check.status == appctx->ctx.stats.st_code);
1031 check_state = get_check_status_info(appctx->ctx.stats.st_code);
William Dauchyde3c3262021-02-01 13:11:51 +01001032 labels[2].name = ist("state");
Tim Duesterhusb113b5c2021-09-15 13:58:44 +02001033 labels[2].value = ist(check_state);
William Dauchyde3c3262021-02-01 13:11:51 +01001034 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
1035 &val, labels, &out, max))
1036 goto full;
1037 }
Christopher Faulet040b1192021-02-01 15:05:21 +01001038 appctx->ctx.stats.st_code = 0;
William Dauchyde3c3262021-02-01 13:11:51 +01001039 goto next_sv;
Christopher Fauletcf403f32019-11-21 14:35:46 +01001040 case ST_F_CHECK_CODE:
1041 if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
1042 goto next_sv;
Christopher Faulet37286a52021-01-20 15:20:53 +01001043 val = mkf_u32(FN_OUTPUT, (sv->check.status < HCHK_STATUS_L57DATA) ? 0 : sv->check.code);
Christopher Fauletcf403f32019-11-21 14:35:46 +01001044 break;
Christopher Faulet2711e512020-02-27 16:12:07 +01001045 case ST_F_CHECK_DURATION:
1046 if (sv->check.status < HCHK_STATUS_CHECKED)
1047 goto next_sv;
1048 secs = (double)sv->check.duration / 1000.0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001049 val = mkf_flt(FN_DURATION, secs);
Christopher Faulet2711e512020-02-27 16:12:07 +01001050 break;
Marcin Deranek3c27dda2020-05-15 18:32:51 +02001051 case ST_F_REQ_TOT:
Christopher Fauletf959d082019-02-07 15:38:42 +01001052 if (px->mode != PR_MODE_HTTP)
1053 goto next_px;
William Dauchybde2bf62021-01-25 17:29:04 +01001054 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +01001055 break;
Christopher Faulet32ef48e2021-02-01 14:55:37 +01001056 case ST_F_HRSP_1XX:
Christopher Fauletf959d082019-02-07 15:38:42 +01001057 case ST_F_HRSP_2XX:
Christopher Fauletf959d082019-02-07 15:38:42 +01001058 case ST_F_HRSP_3XX:
Christopher Fauletf959d082019-02-07 15:38:42 +01001059 case ST_F_HRSP_4XX:
Christopher Fauletf959d082019-02-07 15:38:42 +01001060 case ST_F_HRSP_5XX:
Christopher Fauletf959d082019-02-07 15:38:42 +01001061 case ST_F_HRSP_OTHER:
1062 if (px->mode != PR_MODE_HTTP)
1063 goto next_px;
Christopher Faulet32ef48e2021-02-01 14:55:37 +01001064 if (appctx->st2 != ST_F_HRSP_1XX)
1065 appctx->ctx.stats.flags &= ~PROMEX_FL_METRIC_HDR;
Christopher Faulet5a2f9382021-01-28 11:24:17 +01001066 labels[2].name = ist("code");
1067 labels[2].value = promex_hrsp_code[appctx->st2 - ST_F_HRSP_1XX];
William Dauchybde2bf62021-01-25 17:29:04 +01001068 val = stats[appctx->st2];
Christopher Fauletc55a6262020-07-10 15:39:39 +02001069 break;
Christopher Fauletf959d082019-02-07 15:38:42 +01001070
1071 default:
William Dauchybde2bf62021-01-25 17:29:04 +01001072 val = stats[appctx->st2];
Christopher Fauletf959d082019-02-07 15:38:42 +01001073 }
1074
William Dauchyc6464592021-01-27 22:40:17 +01001075 if (!promex_dump_metric(appctx, htx, prefix, &promex_st_metrics[appctx->st2],
Christopher Faulet5a2f9382021-01-28 11:24:17 +01001076 &val, labels, &out, max))
Christopher Fauletf959d082019-02-07 15:38:42 +01001077 goto full;
Christopher Fauleteba22942019-11-19 14:18:24 +01001078 next_sv:
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001079 appctx->ctx.stats.obj2 = sv->next;
Christopher Fauletf959d082019-02-07 15:38:42 +01001080 }
1081
1082 next_px:
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001083 appctx->ctx.stats.obj1 = px->next;
1084 appctx->ctx.stats.obj2 = (appctx->ctx.stats.obj1 ? ((struct proxy *)appctx->ctx.stats.obj1)->srv : NULL);
Christopher Fauletf959d082019-02-07 15:38:42 +01001085 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001086 appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001087 appctx->ctx.stats.obj1 = proxies_list;
1088 appctx->ctx.stats.obj2 = (appctx->ctx.stats.obj1 ? ((struct proxy *)appctx->ctx.stats.obj1)->srv : NULL);
Christopher Fauletf959d082019-02-07 15:38:42 +01001089 }
1090
1091
1092 end:
Christopher Faulet0c55a152019-07-04 10:03:28 +02001093 if (out.len) {
1094 if (!htx_add_data_atonce(htx, out))
1095 return -1; /* Unexpected and unrecoverable error */
1096 channel_add_input(chn, out.len);
1097 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001098 return ret;
1099 full:
1100 ret = 0;
1101 goto end;
1102}
1103
William Dauchy69164222021-02-07 20:42:38 +01001104/* Dump stick table metrics (prefixed by "haproxy_sticktable_"). It returns 1 on success,
1105 * 0 if <htx> is full and -1 in case of any error. */
1106static int promex_dump_sticktable_metrics(struct appctx *appctx, struct htx *htx)
1107{
1108 static struct ist prefix = IST("haproxy_sticktable_");
1109 struct field val;
1110 struct channel *chn = si_ic(appctx->owner);
1111 struct ist out = ist2(trash.area, 0);
1112 size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
1113 int ret = 1;
1114 struct stktable *t;
1115
1116 for (; appctx->st2 < STICKTABLE_TOTAL_FIELDS; appctx->st2++) {
1117 if (!(promex_sticktable_metrics[appctx->st2].flags & appctx->ctx.stats.flags))
1118 continue;
1119
1120 while (appctx->ctx.stats.obj1) {
1121 struct promex_label labels[PROMEX_MAX_LABELS - 1] = {};
1122
1123 t = appctx->ctx.stats.obj1;
1124 if (!t->size)
1125 goto next_px;
1126
1127 labels[0].name = ist("name");
1128 labels[0].value = ist2(t->id, strlen(t->id));
1129 labels[1].name = ist("type");
1130 labels[1].value = ist2(stktable_types[t->type].kw, strlen(stktable_types[t->type].kw));
1131 switch (appctx->st2) {
1132 case STICKTABLE_SIZE:
1133 val = mkf_u32(FN_GAUGE, t->size);
1134 break;
1135 case STICKTABLE_USED:
1136 val = mkf_u32(FN_GAUGE, t->current);
1137 break;
1138 default:
1139 goto next_px;
1140 }
1141
1142 if (!promex_dump_metric(appctx, htx, prefix,
1143 &promex_sticktable_metrics[appctx->st2],
1144 &val, labels, &out, max))
1145 goto full;
1146
1147 next_px:
1148 appctx->ctx.stats.obj1 = t->next;
1149 }
1150 appctx->ctx.stats.flags |= PROMEX_FL_METRIC_HDR;
1151 appctx->ctx.stats.obj1 = stktables_list;
1152 }
1153
1154 end:
1155 if (out.len) {
1156 if (!htx_add_data_atonce(htx, out))
1157 return -1; /* Unexpected and unrecoverable error */
1158 channel_add_input(chn, out.len);
1159 }
1160 return ret;
1161 full:
1162 ret = 0;
1163 goto end;
1164}
1165
Christopher Fauletf959d082019-02-07 15:38:42 +01001166/* Dump all metrics (global, frontends, backends and servers) depending on the
1167 * dumper state (appctx->st1). It returns 1 on success, 0 if <htx> is full and
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001168 * -1 in case of any error.
1169 * Uses <appctx.ctx.stats.obj1> as a pointer to the current proxy and <obj2> as
1170 * a pointer to the current server/listener. */
Christopher Fauletf959d082019-02-07 15:38:42 +01001171static int promex_dump_metrics(struct appctx *appctx, struct stream_interface *si, struct htx *htx)
1172{
1173 int ret;
1174
1175 switch (appctx->st1) {
1176 case PROMEX_DUMPER_INIT:
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001177 appctx->ctx.stats.obj1 = NULL;
1178 appctx->ctx.stats.obj2 = NULL;
Christopher Fauletefde9552020-06-05 08:18:56 +02001179 appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_INFO_METRIC);
Christopher Faulet040b1192021-02-01 15:05:21 +01001180 appctx->ctx.stats.st_code = 0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001181 appctx->st2 = INF_NAME;
Christopher Fauletf959d082019-02-07 15:38:42 +01001182 appctx->st1 = PROMEX_DUMPER_GLOBAL;
1183 /* fall through */
1184
1185 case PROMEX_DUMPER_GLOBAL:
Christopher Faulet78407ce2019-11-18 14:47:08 +01001186 if (appctx->ctx.stats.flags & PROMEX_FL_SCOPE_GLOBAL) {
1187 ret = promex_dump_global_metrics(appctx, htx);
1188 if (ret <= 0) {
1189 if (ret == -1)
1190 goto error;
1191 goto full;
1192 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001193 }
1194
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001195 appctx->ctx.stats.obj1 = proxies_list;
1196 appctx->ctx.stats.obj2 = NULL;
Christopher Fauletefde9552020-06-05 08:18:56 +02001197 appctx->ctx.stats.flags &= ~PROMEX_FL_INFO_METRIC;
Christopher Fauletb713c4f2021-01-20 15:02:50 +01001198 appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_FRONT_METRIC);
Christopher Faulet040b1192021-02-01 15:05:21 +01001199 appctx->ctx.stats.st_code = 0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001200 appctx->st2 = ST_F_PXNAME;
Christopher Fauletf959d082019-02-07 15:38:42 +01001201 appctx->st1 = PROMEX_DUMPER_FRONT;
1202 /* fall through */
1203
1204 case PROMEX_DUMPER_FRONT:
Christopher Faulet78407ce2019-11-18 14:47:08 +01001205 if (appctx->ctx.stats.flags & PROMEX_FL_SCOPE_FRONT) {
1206 ret = promex_dump_front_metrics(appctx, htx);
1207 if (ret <= 0) {
1208 if (ret == -1)
1209 goto error;
1210 goto full;
1211 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001212 }
1213
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001214 appctx->ctx.stats.obj1 = proxies_list;
William Dauchye3f7bd52021-02-14 23:22:56 +01001215 appctx->ctx.stats.obj2 = LIST_NEXT(&proxies_list->conf.listeners, struct listener *, by_fe);
Christopher Fauletb713c4f2021-01-20 15:02:50 +01001216 appctx->ctx.stats.flags &= ~PROMEX_FL_FRONT_METRIC;
William Dauchye3f7bd52021-02-14 23:22:56 +01001217 appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_LI_METRIC);
1218 appctx->ctx.stats.st_code = 0;
1219 appctx->st2 = ST_F_PXNAME;
1220 appctx->st1 = PROMEX_DUMPER_LI;
1221 /* fall through */
1222
1223 case PROMEX_DUMPER_LI:
1224 if (appctx->ctx.stats.flags & PROMEX_FL_SCOPE_LI) {
1225 ret = promex_dump_listener_metrics(appctx, htx);
1226 if (ret <= 0) {
1227 if (ret == -1)
1228 goto error;
1229 goto full;
1230 }
1231 }
1232
1233 appctx->ctx.stats.obj1 = proxies_list;
1234 appctx->ctx.stats.obj2 = NULL;
1235 appctx->ctx.stats.flags &= ~PROMEX_FL_LI_METRIC;
Christopher Fauletb713c4f2021-01-20 15:02:50 +01001236 appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_BACK_METRIC);
Christopher Faulet040b1192021-02-01 15:05:21 +01001237 appctx->ctx.stats.st_code = 0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001238 appctx->st2 = ST_F_PXNAME;
Christopher Fauletf959d082019-02-07 15:38:42 +01001239 appctx->st1 = PROMEX_DUMPER_BACK;
1240 /* fall through */
1241
1242 case PROMEX_DUMPER_BACK:
Christopher Faulet78407ce2019-11-18 14:47:08 +01001243 if (appctx->ctx.stats.flags & PROMEX_FL_SCOPE_BACK) {
1244 ret = promex_dump_back_metrics(appctx, htx);
1245 if (ret <= 0) {
1246 if (ret == -1)
1247 goto error;
1248 goto full;
1249 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001250 }
1251
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001252 appctx->ctx.stats.obj1 = proxies_list;
1253 appctx->ctx.stats.obj2 = (appctx->ctx.stats.obj1 ? ((struct proxy *)appctx->ctx.stats.obj1)->srv : NULL);
Christopher Fauletb713c4f2021-01-20 15:02:50 +01001254 appctx->ctx.stats.flags &= ~PROMEX_FL_BACK_METRIC;
1255 appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_SRV_METRIC);
Christopher Faulet040b1192021-02-01 15:05:21 +01001256 appctx->ctx.stats.st_code = 0;
Christopher Faulet37286a52021-01-20 15:20:53 +01001257 appctx->st2 = ST_F_PXNAME;
Christopher Fauletf959d082019-02-07 15:38:42 +01001258 appctx->st1 = PROMEX_DUMPER_SRV;
1259 /* fall through */
1260
1261 case PROMEX_DUMPER_SRV:
Christopher Faulet78407ce2019-11-18 14:47:08 +01001262 if (appctx->ctx.stats.flags & PROMEX_FL_SCOPE_SERVER) {
1263 ret = promex_dump_srv_metrics(appctx, htx);
1264 if (ret <= 0) {
1265 if (ret == -1)
1266 goto error;
1267 goto full;
1268 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001269 }
1270
William Dauchy69164222021-02-07 20:42:38 +01001271 appctx->ctx.stats.obj1 = stktables_list;
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001272 appctx->ctx.stats.obj2 = NULL;
Christopher Fauletb713c4f2021-01-20 15:02:50 +01001273 appctx->ctx.stats.flags &= ~(PROMEX_FL_METRIC_HDR|PROMEX_FL_SRV_METRIC);
William Dauchy69164222021-02-07 20:42:38 +01001274 appctx->ctx.stats.flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_STICKTABLE_METRIC);
1275 appctx->st2 = STICKTABLE_SIZE;
1276 appctx->st1 = PROMEX_DUMPER_STICKTABLE;
1277 /* fall through */
1278
1279 case PROMEX_DUMPER_STICKTABLE:
1280 if (appctx->ctx.stats.flags & PROMEX_FL_SCOPE_STICKTABLE) {
1281 ret = promex_dump_sticktable_metrics(appctx, htx);
1282 if (ret <= 0) {
1283 if (ret == -1)
1284 goto error;
1285 goto full;
1286 }
1287 }
1288
1289 appctx->ctx.stats.obj1 = NULL;
1290 appctx->ctx.stats.obj2 = NULL;
1291 appctx->ctx.stats.flags &= ~(PROMEX_FL_METRIC_HDR|PROMEX_FL_STICKTABLE_METRIC);
Christopher Fauletf959d082019-02-07 15:38:42 +01001292 appctx->st2 = 0;
1293 appctx->st1 = PROMEX_DUMPER_DONE;
1294 /* fall through */
1295
1296 case PROMEX_DUMPER_DONE:
1297 default:
1298 break;
1299 }
1300
1301 return 1;
1302
1303 full:
1304 si_rx_room_blk(si);
1305 return 0;
1306 error:
1307 /* unrecoverable error */
Amaury Denoyelleda5b6d12020-10-02 18:32:02 +02001308 appctx->ctx.stats.obj1 = NULL;
1309 appctx->ctx.stats.obj2 = NULL;
Christopher Fauletf959d082019-02-07 15:38:42 +01001310 appctx->ctx.stats.flags = 0;
1311 appctx->st2 = 0;
1312 appctx->st1 = PROMEX_DUMPER_DONE;
1313 return -1;
1314}
1315
Ilya Shipitsince7b00f2020-03-23 22:28:40 +05001316/* Parse the query string of request URI to filter the metrics. It returns 1 on
Christopher Faulet78407ce2019-11-18 14:47:08 +01001317 * success and -1 on error. */
1318static int promex_parse_uri(struct appctx *appctx, struct stream_interface *si)
1319{
1320 struct channel *req = si_oc(si);
1321 struct channel *res = si_ic(si);
1322 struct htx *req_htx, *res_htx;
1323 struct htx_sl *sl;
William Dauchyc65f6562019-11-26 12:56:26 +01001324 char *p, *key, *value;
1325 const char *end;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001326 struct buffer *err;
1327 int default_scopes = PROMEX_FL_SCOPE_ALL;
1328 int len;
1329
1330 /* Get the query-string */
1331 req_htx = htxbuf(&req->buf);
1332 sl = http_get_stline(req_htx);
1333 if (!sl)
1334 goto error;
1335 p = http_find_param_list(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), '?');
1336 if (!p)
1337 goto end;
1338 end = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
Christopher Faulet78407ce2019-11-18 14:47:08 +01001339
William Dauchyc65f6562019-11-26 12:56:26 +01001340 /* copy the query-string */
1341 len = end - p;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001342 chunk_reset(&trash);
1343 memcpy(trash.area, p, len);
1344 trash.area[len] = 0;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001345 p = trash.area;
William Dauchyc65f6562019-11-26 12:56:26 +01001346 end = trash.area + len;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001347
1348 /* Parse the query-string */
William Dauchyc65f6562019-11-26 12:56:26 +01001349 while (p < end && *p && *p != '#') {
1350 value = NULL;
1351
1352 /* decode parameter name */
1353 key = p;
1354 while (p < end && *p != '=' && *p != '&' && *p != '#')
Christopher Faulet78407ce2019-11-18 14:47:08 +01001355 ++p;
William Dauchyc65f6562019-11-26 12:56:26 +01001356 /* found a value */
1357 if (*p == '=') {
1358 *(p++) = 0;
1359 value = p;
1360 }
1361 else if (*p == '&')
1362 *(p++) = 0;
1363 else if (*p == '#')
1364 *p = 0;
Willy Tarreau62ba9ba2020-04-23 17:54:47 +02001365 len = url_decode(key, 1);
William Dauchyc65f6562019-11-26 12:56:26 +01001366 if (len == -1)
1367 goto error;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001368
William Dauchyc65f6562019-11-26 12:56:26 +01001369 /* decode value */
1370 if (value) {
1371 while (p < end && *p != '=' && *p != '&' && *p != '#')
1372 ++p;
1373 if (*p == '=')
1374 goto error;
1375 if (*p == '&')
1376 *(p++) = 0;
1377 else if (*p == '#')
1378 *p = 0;
Willy Tarreau62ba9ba2020-04-23 17:54:47 +02001379 len = url_decode(value, 1);
William Dauchyc65f6562019-11-26 12:56:26 +01001380 if (len == -1)
1381 goto error;
1382 }
1383
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001384 if (strcmp(key, "scope") == 0) {
William Dauchyc65f6562019-11-26 12:56:26 +01001385 default_scopes = 0; /* at least a scope defined, unset default scopes */
1386 if (!value)
1387 goto error;
1388 else if (*value == 0)
Christopher Faulet78407ce2019-11-18 14:47:08 +01001389 appctx->ctx.stats.flags &= ~PROMEX_FL_SCOPE_ALL;
William Dauchyc65f6562019-11-26 12:56:26 +01001390 else if (*value == '*')
Christopher Faulet78407ce2019-11-18 14:47:08 +01001391 appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_ALL;
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001392 else if (strcmp(value, "global") == 0)
William Dauchyc65f6562019-11-26 12:56:26 +01001393 appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_GLOBAL;
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001394 else if (strcmp(value, "server") == 0)
William Dauchyc65f6562019-11-26 12:56:26 +01001395 appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_SERVER;
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001396 else if (strcmp(value, "backend") == 0)
Christopher Faulet78407ce2019-11-18 14:47:08 +01001397 appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_BACK;
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001398 else if (strcmp(value, "frontend") == 0)
Christopher Faulet78407ce2019-11-18 14:47:08 +01001399 appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_FRONT;
William Dauchye3f7bd52021-02-14 23:22:56 +01001400 else if (strcmp(value, "listener") == 0)
1401 appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_LI;
William Dauchy69164222021-02-07 20:42:38 +01001402 else if (strcmp(value, "sticktable") == 0)
1403 appctx->ctx.stats.flags |= PROMEX_FL_SCOPE_STICKTABLE;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001404 else
1405 goto error;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001406 }
Tim Duesterhus8cb12a82021-01-02 22:31:55 +01001407 else if (strcmp(key, "no-maint") == 0)
Christopher Fauleteba22942019-11-19 14:18:24 +01001408 appctx->ctx.stats.flags |= PROMEX_FL_NO_MAINT_SRV;
Christopher Faulet78407ce2019-11-18 14:47:08 +01001409 }
1410
1411 end:
1412 appctx->ctx.stats.flags |= default_scopes;
1413 return 1;
1414
1415 error:
1416 err = &http_err_chunks[HTTP_ERR_400];
1417 channel_erase(res);
1418 res->buf.data = b_data(err);
1419 memcpy(res->buf.area, b_head(err), b_data(err));
1420 res_htx = htx_from_buf(&res->buf);
1421 channel_add_input(res, res_htx->data);
1422 appctx->st0 = PROMEX_ST_END;
1423 return -1;
1424}
1425
Christopher Fauletf959d082019-02-07 15:38:42 +01001426/* Send HTTP headers of the response. It returns 1 on success and 0 if <htx> is
1427 * full. */
1428static int promex_send_headers(struct appctx *appctx, struct stream_interface *si, struct htx *htx)
1429{
Christopher Faulet9744f7c2019-03-27 15:48:53 +01001430 struct channel *chn = si_ic(appctx->owner);
Christopher Fauletf959d082019-02-07 15:38:42 +01001431 struct htx_sl *sl;
1432 unsigned int flags;
1433
1434 flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_ENC|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
1435 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("200"), ist("OK"));
1436 if (!sl)
1437 goto full;
1438 sl->info.res.status = 200;
1439 if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) ||
Christopher Fauletf959d082019-02-07 15:38:42 +01001440 !htx_add_header(htx, ist("Content-Type"), ist("text/plain; version=0.0.4")) ||
1441 !htx_add_header(htx, ist("Transfer-Encoding"), ist("chunked")) ||
1442 !htx_add_endof(htx, HTX_BLK_EOH))
1443 goto full;
1444
Christopher Faulet9744f7c2019-03-27 15:48:53 +01001445 channel_add_input(chn, htx->data);
Christopher Fauletf959d082019-02-07 15:38:42 +01001446 return 1;
1447 full:
1448 htx_reset(htx);
1449 si_rx_room_blk(si);
1450 return 0;
1451}
1452
1453/* The function returns 1 if the initialisation is complete, 0 if
1454 * an errors occurs and -1 if more data are required for initializing
1455 * the applet.
1456 */
1457static int promex_appctx_init(struct appctx *appctx, struct proxy *px, struct stream *strm)
1458{
1459 appctx->st0 = PROMEX_ST_INIT;
1460 return 1;
1461}
1462
1463/* The main I/O handler for the promex applet. */
1464static void promex_appctx_handle_io(struct appctx *appctx)
1465{
1466 struct stream_interface *si = appctx->owner;
1467 struct stream *s = si_strm(si);
1468 struct channel *req = si_oc(si);
1469 struct channel *res = si_ic(si);
1470 struct htx *req_htx, *res_htx;
1471 int ret;
1472
1473 res_htx = htx_from_buf(&res->buf);
Christopher Fauletf959d082019-02-07 15:38:42 +01001474 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
1475 goto out;
1476
Ilya Shipitsince7b00f2020-03-23 22:28:40 +05001477 /* Check if the input buffer is available. */
Christopher Fauletf959d082019-02-07 15:38:42 +01001478 if (!b_size(&res->buf)) {
1479 si_rx_room_blk(si);
1480 goto out;
1481 }
1482
1483 switch (appctx->st0) {
1484 case PROMEX_ST_INIT:
Christopher Faulet78407ce2019-11-18 14:47:08 +01001485 ret = promex_parse_uri(appctx, si);
1486 if (ret <= 0) {
1487 if (ret == -1)
1488 goto error;
1489 goto out;
1490 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001491 appctx->st0 = PROMEX_ST_HEAD;
1492 appctx->st1 = PROMEX_DUMPER_INIT;
1493 /* fall through */
1494
1495 case PROMEX_ST_HEAD:
1496 if (!promex_send_headers(appctx, si, res_htx))
1497 goto out;
1498 appctx->st0 = ((s->txn->meth == HTTP_METH_HEAD) ? PROMEX_ST_DONE : PROMEX_ST_DUMP);
1499 /* fall through */
1500
1501 case PROMEX_ST_DUMP:
1502 ret = promex_dump_metrics(appctx, si, res_htx);
1503 if (ret <= 0) {
1504 if (ret == -1)
1505 goto error;
1506 goto out;
1507 }
1508 appctx->st0 = PROMEX_ST_DONE;
1509 /* fall through */
1510
1511 case PROMEX_ST_DONE:
Christopher Fauletd1ac2b92020-12-02 19:12:22 +01001512 /* no more data are expected. Don't add TLR because mux-h1 will take care of it */
1513 res_htx->flags |= HTX_FL_EOM;
Christopher Faulet9744f7c2019-03-27 15:48:53 +01001514 appctx->st0 = PROMEX_ST_END;
1515 /* fall through */
Christopher Fauletf959d082019-02-07 15:38:42 +01001516
Christopher Faulet9744f7c2019-03-27 15:48:53 +01001517 case PROMEX_ST_END:
1518 if (!(res->flags & CF_SHUTR)) {
1519 res->flags |= CF_READ_NULL;
1520 si_shutr(si);
1521 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001522 }
1523
Christopher Fauletf959d082019-02-07 15:38:42 +01001524 out:
1525 htx_to_buf(res_htx, &res->buf);
Christopher Faulet9744f7c2019-03-27 15:48:53 +01001526
1527 /* eat the whole request */
1528 if (co_data(req)) {
1529 req_htx = htx_from_buf(&req->buf);
1530 co_htx_skip(req, req_htx, co_data(req));
1531 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001532 return;
1533
1534 error:
1535 res->flags |= CF_READ_NULL;
1536 si_shutr(si);
1537 si_shutw(si);
1538}
1539
1540struct applet promex_applet = {
1541 .obj_type = OBJ_TYPE_APPLET,
1542 .name = "<PROMEX>", /* used for logging */
1543 .init = promex_appctx_init,
1544 .fct = promex_appctx_handle_io,
1545};
1546
1547static enum act_parse_ret service_parse_prometheus_exporter(const char **args, int *cur_arg, struct proxy *px,
1548 struct act_rule *rule, char **err)
1549{
1550 /* Prometheus exporter service is only available on "http-request" rulesets */
1551 if (rule->from != ACT_F_HTTP_REQ) {
1552 memprintf(err, "Prometheus exporter service only available on 'http-request' rulesets");
1553 return ACT_RET_PRS_ERR;
1554 }
Christopher Fauletf959d082019-02-07 15:38:42 +01001555
1556 /* Add applet pointer in the rule. */
1557 rule->applet = promex_applet;
1558
1559 return ACT_RET_PRS_OK;
1560}
1561static void promex_register_build_options(void)
1562{
1563 char *ptr = NULL;
1564
1565 memprintf(&ptr, "Built with the Prometheus exporter as a service");
1566 hap_register_build_opts(ptr, 1);
1567}
1568
1569
1570static struct action_kw_list service_actions = { ILH, {
1571 { "prometheus-exporter", service_parse_prometheus_exporter },
1572 { /* END */ }
1573}};
1574
1575INITCALL1(STG_REGISTER, service_keywords_register, &service_actions);
1576INITCALL0(STG_REGISTER, promex_register_build_options);