src/dynbuf.o src/wdt.o src/pipe.o src/init.o src/http_acl.o \
src/hpack-huff.o src/hpack-enc.o src/dict.o src/freq_ctr.o \
src/ebtree.o src/hash.o src/dgram.o src/version.o src/proto_rhttp.o \
- src/guid.o src/stats-html.o src/stats-json.o
+ src/guid.o src/stats-html.o src/stats-json.o src/stats-file.o
ifneq ($(TRACE),)
OBJS += src/calltrace.o
9.3. Unix Socket commands
9.4. Master CLI
9.4.1. Master CLI commands
+9.5. Stats-file
10. Tricks for easier configuration management
11. Well-known traps to avoid
12. Debugging and performance issues
This command is restricted and can only be issued on sockets configured for
level "admin".
+dump stats-file
+ Generate a stats-file which can be used to preload haproxy counters values on
+ startup. See "Stats-file" section for more detail.
+
enable agent <backend>/<server>
Resume auxiliary agent check that was temporarily stopped.
Those messages are also dumped with the "reload" command.
+
+9.5. Stats-file
+--------------
+
+A so-called stats-file can be used to preload internal haproxy counters on
+process startup with non-null values. Its main purpose is to preserve
+statistics for worker processes accross reloads. Only an excerpt of all the
+exposed haproxy statistics is present in a stats-file as it only makes sense to
+preload metric-type values.
+
+For the moment, only proxy counters are supported in stats-file. This allows to
+preload values for frontends, backends, servers and listeners. However only
+objects instances with a non-empty GUID are stored in a stats-file. This
+guarantees that value will be preloaded for object with matching type and GUID,
+even if other parameters differ.
+
+The CLI command "dump stats-file" purpose is to generate a stats-file. Format
+of the stats-file is internally defined and freely subject to future changes
+and extension. It is designed to be compatible at least accross adjacent
+haproxy stable branch releases, but may require optional extra configuration
+when loading a stats-file to a process running on an older version.
+
+
10. Tricks for easier configuration management
----------------------------------------------
--- /dev/null
+#ifndef _HAPROXY_STATS_FILE_H
+#define _HAPROXY_STATS_FILE_H
+
+#include <sys/types.h>
+
+#include <haproxy/buf-t.h>
+#include <haproxy/stats-t.h>
+
+int stats_dump_fields_file(struct buffer *out,
+ const struct field *stats, size_t stats_count,
+ struct show_stat_ctx *ctx);
+
+void stats_dump_file_header(int type, struct buffer *out);
+
+#endif /* _HAPROXY_STATS_FILE_H */
--- /dev/null
+#include <haproxy/stats-file.h>
+
+#include <haproxy/api.h>
+#include <haproxy/buf.h>
+#include <haproxy/chunk.h>
+#include <haproxy/guid-t.h>
+#include <haproxy/list.h>
+#include <haproxy/listener-t.h>
+#include <haproxy/obj_type.h>
+#include <haproxy/proxy-t.h>
+#include <haproxy/server-t.h>
+#include <haproxy/stats.h>
+
+/* Dump all fields from <stats> into <out> for stats-file. */
+int stats_dump_fields_file(struct buffer *out,
+ const struct field *line, size_t stats_count,
+ struct show_stat_ctx *ctx)
+{
+ struct guid_node *guid;
+ struct listener *l;
+ int i;
+
+ switch (ctx->px_st) {
+ case STAT_PX_ST_FE:
+ case STAT_PX_ST_BE:
+ guid = &__objt_proxy(ctx->obj1)->guid;
+ break;
+
+ case STAT_PX_ST_LI:
+ l = LIST_ELEM(ctx->obj2, struct listener *, by_fe);
+ guid = &l->guid;
+ break;
+
+ case STAT_PX_ST_SV:
+ guid = &__objt_server(ctx->obj2)->guid;
+ break;
+
+ default:
+ ABORT_NOW();
+ return 1;
+ }
+
+ /* Skip objects without GUID. */
+ if (!guid->node.key)
+ return 1;
+
+ chunk_appendf(out, "%s,", (char *)guid->node.key);
+
+ for (i = 0; i < stats_count; ++i) {
+ /* Empty field for stats-file is used to skip its output,
+ * including any separator.
+ */
+ if (field_format(line, i) == FF_EMPTY)
+ continue;
+
+ if (!stats_emit_raw_data_field(out, &line[i]))
+ return 0;
+ if (!chunk_strcat(out, ","))
+ return 0;
+ }
+
+ chunk_strcat(out, "\n");
+ return 1;
+}
+
+void stats_dump_file_header(int type, struct buffer *out)
+{
+ const struct stat_col *col;
+ int i;
+
+ /* Caller must specified ither FE or BE. */
+ BUG_ON(!(type & ((1 << STATS_TYPE_FE) | (1 << STATS_TYPE_BE))));
+
+ if (type & (1 << STATS_TYPE_FE)) {
+ chunk_strcat(out, "#fe guid,");
+ for (i = 0; i < ST_I_PX_MAX; ++i) {
+ col = &stat_cols_px[i];
+ if (stcol_nature(col) == FN_COUNTER && (col->cap & (STATS_PX_CAP_FE|STATS_PX_CAP_LI)))
+ chunk_appendf(out, "%s,", col->name);
+ }
+ }
+ else {
+ chunk_appendf(out, "#be guid,");
+ for (i = 0; i < ST_I_PX_MAX; ++i) {
+ col = &stat_cols_px[i];
+ if (stcol_nature(col) == FN_COUNTER && (col->cap & (STATS_PX_CAP_BE|STATS_PX_CAP_SRV)))
+ chunk_appendf(out, "%s,", col->name);
+ }
+ }
+
+ chunk_strcat(out, "\n");
+}
#include <haproxy/server.h>
#include <haproxy/session.h>
#include <haproxy/stats.h>
+#include <haproxy/stats-file.h>
#include <haproxy/stats-html.h>
#include <haproxy/stats-json.h>
#include <haproxy/stconn.h>
ret = stats_dump_fields_typed(chk, line, stats_count, ctx);
else if (ctx->flags & STAT_F_FMT_JSON)
ret = stats_dump_fields_json(chk, line, stats_count, ctx);
+ else if (ctx->flags & STAT_F_FMT_FILE)
+ ret = stats_dump_fields_file(chk, line, stats_count, ctx);
else
ret = stats_dump_fields_csv(chk, line, stats_count, ctx);
list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
void *counters;
+ if (ctx->flags & STAT_F_FMT_FILE)
+ continue;
+
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE)) {
stats_count += mod->stats_count;
continue;
list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
void *counters;
+ if (ctx->flags & STAT_F_FMT_FILE)
+ continue;
+
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI)) {
stats_count += mod->stats_count;
continue;
list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
void *counters;
+ if (ctx->flags & STAT_F_FMT_FILE)
+ continue;
+
if (stats_get_domain(mod->domain_flags) != STATS_DOMAIN_PROXY)
continue;
list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
struct extra_counters *counters;
+ if (ctx->flags & STAT_F_FMT_FILE)
+ continue;
+
if (stats_get_domain(mod->domain_flags) != STATS_DOMAIN_PROXY)
continue;
stats_dump_json_schema(chk);
else if (ctx->flags & STAT_F_FMT_JSON)
stats_dump_json_header(chk);
+ else if (ctx->flags & STAT_F_FMT_FILE)
+ stats_dump_file_header(ctx->type, chk);
else if (!(ctx->flags & STAT_F_FMT_TYPED))
stats_dump_csv_header(ctx->domain, chk);
return stats_dump_json_schema_to_buffer(appctx);
}
+static int cli_parse_dump_stat_file(char **args, char *payload,
+ struct appctx *appctx, void *private)
+{
+ struct show_stat_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
+
+ ctx->chunk = b_make(trash.area, trash.size, 0, 0);
+ ctx->domain = STATS_DOMAIN_PROXY;
+ ctx->flags |= STAT_F_FMT_FILE;
+
+ return 0;
+}
+
+/* Returns 1 on completion else 0. */
+static int cli_io_handler_dump_stat_file(struct appctx *appctx)
+{
+ struct show_stat_ctx *ctx = appctx->svcctx;
+ int ret;
+
+ /* Frontend and backend sides are ouputted separatedly on stats-file.
+ * As such, use STAT_F_BOUND to restrict proxies looping over frontend
+ * side first before first stats_dump_stat_to_buffer(). A second
+ * iteration is conducted for backend side after.
+ */
+ ctx->flags |= STAT_F_BOUND;
+
+ if (!(ctx->type & (1 << STATS_TYPE_BE))) {
+ /* Restrict to frontend side. */
+ ctx->type = (1 << STATS_TYPE_FE) | (1 << STATS_TYPE_SO);
+ ctx->iid = ctx->sid = -1;
+
+ ret = stats_dump_stat_to_buffer(appctx_sc(appctx), NULL, NULL);
+ if (!ret)
+ return 0;
+
+ chunk_strcat(&ctx->chunk, "\n");
+ if (!stats_putchk(appctx, NULL, NULL))
+ return 0;
+
+ /* Switch to backend side. */
+ ctx->state = STAT_STATE_INIT;
+ ctx->type = (1 << STATS_TYPE_BE) | (1 << STATS_TYPE_SV);
+ }
+
+ return stats_dump_stat_to_buffer(appctx_sc(appctx), NULL, NULL);
+}
+
+static void cli_io_handler_release_dump_stat_file(struct appctx *appctx)
+{
+}
+
int stats_allocate_proxy_counters_internal(struct extra_counters **counters,
int type, int px_cap)
{
{ { "show", "info", NULL }, "show info [desc|json|typed|float]* : report information about the running process", cli_parse_show_info, cli_io_handler_dump_info, NULL },
{ { "show", "stat", NULL }, "show stat [desc|json|no-maint|typed|up]*: report counters for each proxy and server", cli_parse_show_stat, cli_io_handler_dump_stat, cli_io_handler_release_stat },
{ { "show", "schema", "json", NULL }, "show schema json : report schema used for stats", NULL, cli_io_handler_dump_json_schema, NULL },
+ { { "dump", "stats-file", NULL }, "dump stats-file : dump stats for restore", cli_parse_dump_stat_file, cli_io_handler_dump_stat_file, cli_io_handler_release_dump_stat_file },
{{},}
}};