mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 23:27:04 +02:00
REORG: stats: extract JSON related functions
This commit is similar to the previous one. This time it deals with functions related to stats JSON output.
This commit is contained in:
parent
b8c1fdf24e
commit
0109c0658d
2
Makefile
2
Makefile
@ -975,7 +975,7 @@ OBJS += src/mux_h2.o src/mux_fcgi.o src/mux_h1.o src/tcpcheck.o \
|
|||||||
src/dynbuf.o src/wdt.o src/pipe.o src/init.o src/http_acl.o \
|
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/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/ebtree.o src/hash.o src/dgram.o src/version.o src/proto_rhttp.o \
|
||||||
src/guid.o src/stats-html.o
|
src/guid.o src/stats-html.o src/stats-json.o
|
||||||
|
|
||||||
ifneq ($(TRACE),)
|
ifneq ($(TRACE),)
|
||||||
OBJS += src/calltrace.o
|
OBJS += src/calltrace.o
|
||||||
|
24
include/haproxy/stats-json.h
Normal file
24
include/haproxy/stats-json.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef _HAPROXY_STATS_JSON_H
|
||||||
|
#define _HAPROXY_STATS_JSON_H
|
||||||
|
|
||||||
|
#include <haproxy/applet-t.h>
|
||||||
|
#include <haproxy/buf-t.h>
|
||||||
|
#include <haproxy/stats-t.h>
|
||||||
|
|
||||||
|
void stats_dump_json_header(struct buffer *out);
|
||||||
|
|
||||||
|
int stats_dump_fields_json(struct buffer *out,
|
||||||
|
const struct field *stats, size_t stats_count,
|
||||||
|
struct show_stat_ctx *ctx);
|
||||||
|
|
||||||
|
void stats_dump_json_end(struct buffer *out);
|
||||||
|
|
||||||
|
int stats_dump_json_info_fields(struct buffer *out,
|
||||||
|
const struct field *info,
|
||||||
|
struct show_stat_ctx *ctx);
|
||||||
|
|
||||||
|
void stats_dump_json_schema(struct buffer *out);
|
||||||
|
|
||||||
|
int stats_dump_json_schema_to_buffer(struct appctx *appctx);
|
||||||
|
|
||||||
|
#endif /* _HAPROXY_STATS_JSON_H */
|
@ -45,6 +45,7 @@ extern struct applet http_stats_applet;
|
|||||||
extern struct list stats_module_list[];
|
extern struct list stats_module_list[];
|
||||||
extern THREAD_LOCAL struct field info[];
|
extern THREAD_LOCAL struct field info[];
|
||||||
extern THREAD_LOCAL struct field *stat_l[];
|
extern THREAD_LOCAL struct field *stat_l[];
|
||||||
|
extern struct name_desc *stat_f[STATS_DOMAIN_COUNT];
|
||||||
|
|
||||||
struct htx;
|
struct htx;
|
||||||
int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx);
|
int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx);
|
||||||
|
533
src/stats-json.c
Normal file
533
src/stats-json.c
Normal file
@ -0,0 +1,533 @@
|
|||||||
|
#include <haproxy/stats-json.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <haproxy/applet.h>
|
||||||
|
#include <haproxy/buf.h>
|
||||||
|
#include <haproxy/chunk.h>
|
||||||
|
#include <haproxy/stats.h>
|
||||||
|
|
||||||
|
/* Emits an encoding of the field type as JSON.
|
||||||
|
* Returns non-zero on success, 0 if the buffer is full.
|
||||||
|
*/
|
||||||
|
static int stats_emit_json_field_tags(struct buffer *out, const struct field *f)
|
||||||
|
{
|
||||||
|
const char *origin, *nature, *scope;
|
||||||
|
int old_len;
|
||||||
|
|
||||||
|
switch (field_origin(f, 0)) {
|
||||||
|
case FO_METRIC: origin = "Metric"; break;
|
||||||
|
case FO_STATUS: origin = "Status"; break;
|
||||||
|
case FO_KEY: origin = "Key"; break;
|
||||||
|
case FO_CONFIG: origin = "Config"; break;
|
||||||
|
case FO_PRODUCT: origin = "Product"; break;
|
||||||
|
default: origin = "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field_nature(f, 0)) {
|
||||||
|
case FN_GAUGE: nature = "Gauge"; break;
|
||||||
|
case FN_LIMIT: nature = "Limit"; break;
|
||||||
|
case FN_MIN: nature = "Min"; break;
|
||||||
|
case FN_MAX: nature = "Max"; break;
|
||||||
|
case FN_RATE: nature = "Rate"; break;
|
||||||
|
case FN_COUNTER: nature = "Counter"; break;
|
||||||
|
case FN_DURATION: nature = "Duration"; break;
|
||||||
|
case FN_AGE: nature = "Age"; break;
|
||||||
|
case FN_TIME: nature = "Time"; break;
|
||||||
|
case FN_NAME: nature = "Name"; break;
|
||||||
|
case FN_OUTPUT: nature = "Output"; break;
|
||||||
|
case FN_AVG: nature = "Avg"; break;
|
||||||
|
default: nature = "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field_scope(f, 0)) {
|
||||||
|
case FS_PROCESS: scope = "Process"; break;
|
||||||
|
case FS_SERVICE: scope = "Service"; break;
|
||||||
|
case FS_SYSTEM: scope = "System"; break;
|
||||||
|
case FS_CLUSTER: scope = "Cluster"; break;
|
||||||
|
default: scope = "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_len = out->data;
|
||||||
|
chunk_appendf(out, "\"tags\":{"
|
||||||
|
"\"origin\":\"%s\","
|
||||||
|
"\"nature\":\"%s\","
|
||||||
|
"\"scope\":\"%s\""
|
||||||
|
"}", origin, nature, scope);
|
||||||
|
return !(old_len == out->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per
|
||||||
|
* the recommendation for interoperable integers in section 6 of RFC 7159.
|
||||||
|
*/
|
||||||
|
#define JSON_INT_MAX ((1ULL << 53) - 1)
|
||||||
|
#define JSON_INT_MIN (0 - JSON_INT_MAX)
|
||||||
|
|
||||||
|
/* Emits a stats field value and its type in JSON.
|
||||||
|
* Returns non-zero on success, 0 on error.
|
||||||
|
*/
|
||||||
|
static int stats_emit_json_data_field(struct buffer *out, const struct field *f)
|
||||||
|
{
|
||||||
|
int old_len;
|
||||||
|
char buf[20];
|
||||||
|
const char *type, *value = buf, *quote = "";
|
||||||
|
|
||||||
|
switch (field_format(f, 0)) {
|
||||||
|
case FF_EMPTY: return 1;
|
||||||
|
case FF_S32: type = "\"s32\"";
|
||||||
|
snprintf(buf, sizeof(buf), "%d", f->u.s32);
|
||||||
|
break;
|
||||||
|
case FF_U32: type = "\"u32\"";
|
||||||
|
snprintf(buf, sizeof(buf), "%u", f->u.u32);
|
||||||
|
break;
|
||||||
|
case FF_S64: type = "\"s64\"";
|
||||||
|
if (f->u.s64 < JSON_INT_MIN || f->u.s64 > JSON_INT_MAX)
|
||||||
|
return 0;
|
||||||
|
type = "\"s64\"";
|
||||||
|
snprintf(buf, sizeof(buf), "%lld", (long long)f->u.s64);
|
||||||
|
break;
|
||||||
|
case FF_U64: if (f->u.u64 > JSON_INT_MAX)
|
||||||
|
return 0;
|
||||||
|
type = "\"u64\"";
|
||||||
|
snprintf(buf, sizeof(buf), "%llu",
|
||||||
|
(unsigned long long) f->u.u64);
|
||||||
|
break;
|
||||||
|
case FF_FLT: type = "\"flt\"";
|
||||||
|
flt_trim(buf, 0, snprintf(buf, sizeof(buf), "%f", f->u.flt));
|
||||||
|
break;
|
||||||
|
case FF_STR: type = "\"str\"";
|
||||||
|
value = field_str(f, 0);
|
||||||
|
quote = "\"";
|
||||||
|
break;
|
||||||
|
default: snprintf(buf, sizeof(buf), "%u", f->type);
|
||||||
|
type = buf;
|
||||||
|
value = "unknown";
|
||||||
|
quote = "\"";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_len = out->data;
|
||||||
|
chunk_appendf(out, ",\"value\":{\"type\":%s,\"value\":%s%s%s}",
|
||||||
|
type, quote, value, quote);
|
||||||
|
return !(old_len == out->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stats_print_proxy_field_json(struct buffer *out,
|
||||||
|
const struct field *stat,
|
||||||
|
const char *name,
|
||||||
|
int pos,
|
||||||
|
uint32_t field_type,
|
||||||
|
uint32_t iid,
|
||||||
|
uint32_t sid,
|
||||||
|
uint32_t pid)
|
||||||
|
{
|
||||||
|
const char *obj_type;
|
||||||
|
switch (field_type) {
|
||||||
|
case STATS_TYPE_FE: obj_type = "Frontend"; break;
|
||||||
|
case STATS_TYPE_BE: obj_type = "Backend"; break;
|
||||||
|
case STATS_TYPE_SO: obj_type = "Listener"; break;
|
||||||
|
case STATS_TYPE_SV: obj_type = "Server"; break;
|
||||||
|
default: obj_type = "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_appendf(out,
|
||||||
|
"{"
|
||||||
|
"\"objType\":\"%s\","
|
||||||
|
"\"proxyId\":%u,"
|
||||||
|
"\"id\":%u,"
|
||||||
|
"\"field\":{\"pos\":%d,\"name\":\"%s\"},"
|
||||||
|
"\"processNum\":%u,",
|
||||||
|
obj_type, iid, sid, pos, name, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stats_print_rslv_field_json(struct buffer *out,
|
||||||
|
const struct field *stat,
|
||||||
|
const char *name,
|
||||||
|
int pos)
|
||||||
|
{
|
||||||
|
chunk_appendf(out,
|
||||||
|
"{"
|
||||||
|
"\"field\":{\"pos\":%d,\"name\":\"%s\"},",
|
||||||
|
pos, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Dumps the stats JSON header to <out> buffer. The caller is responsible for
|
||||||
|
* clearing it if needed.
|
||||||
|
*/
|
||||||
|
void stats_dump_json_header(struct buffer *out)
|
||||||
|
{
|
||||||
|
chunk_strcat(out, "[");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump all fields from <stats> into <out> using a typed "field:desc:type:value" format */
|
||||||
|
int stats_dump_fields_json(struct buffer *out,
|
||||||
|
const struct field *stats, size_t stats_count,
|
||||||
|
struct show_stat_ctx *ctx)
|
||||||
|
{
|
||||||
|
int flags = ctx->flags;
|
||||||
|
int domain = ctx->domain;
|
||||||
|
int started = (ctx->field) ? 1 : 0;
|
||||||
|
int ready_data = 0;
|
||||||
|
|
||||||
|
if (!started && (flags & STAT_STARTED) && !chunk_strcat(out, ","))
|
||||||
|
return 0;
|
||||||
|
if (!started && !chunk_strcat(out, "["))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (; ctx->field < stats_count; ctx->field++) {
|
||||||
|
int old_len;
|
||||||
|
int field = ctx->field;
|
||||||
|
|
||||||
|
if (!stats[field].type)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (started && !chunk_strcat(out, ","))
|
||||||
|
goto err;
|
||||||
|
started = 1;
|
||||||
|
|
||||||
|
old_len = out->data;
|
||||||
|
if (domain == STATS_DOMAIN_PROXY) {
|
||||||
|
stats_print_proxy_field_json(out, &stats[field],
|
||||||
|
stat_f[domain][field].name,
|
||||||
|
field,
|
||||||
|
stats[ST_F_TYPE].u.u32,
|
||||||
|
stats[ST_F_IID].u.u32,
|
||||||
|
stats[ST_F_SID].u.u32,
|
||||||
|
stats[ST_F_PID].u.u32);
|
||||||
|
} else if (domain == STATS_DOMAIN_RESOLVERS) {
|
||||||
|
stats_print_rslv_field_json(out, &stats[field],
|
||||||
|
stat_f[domain][field].name,
|
||||||
|
field);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_len == out->data)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!stats_emit_json_field_tags(out, &stats[field]))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!stats_emit_json_data_field(out, &stats[field]))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!chunk_strcat(out, "}"))
|
||||||
|
goto err;
|
||||||
|
ready_data = out->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chunk_strcat(out, "]"))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
ctx->field = 0; /* we're done */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (!ready_data) {
|
||||||
|
/* not enough buffer space for a single entry.. */
|
||||||
|
chunk_reset(out);
|
||||||
|
if (ctx->flags & STAT_STARTED)
|
||||||
|
chunk_strcat(out, ",");
|
||||||
|
chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}");
|
||||||
|
return 0; /* hard error */
|
||||||
|
}
|
||||||
|
/* push ready data and wait for a new buffer to complete the dump */
|
||||||
|
out->data = ready_data;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dumps the JSON stats trailer block to <out> buffer. The caller is
|
||||||
|
* responsible for clearing it if needed.
|
||||||
|
*/
|
||||||
|
void stats_dump_json_end(struct buffer *out)
|
||||||
|
{
|
||||||
|
chunk_strcat(out, "]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump all fields from <stats> into <out> using the "show info json" format */
|
||||||
|
int stats_dump_json_info_fields(struct buffer *out,
|
||||||
|
const struct field *info,
|
||||||
|
struct show_stat_ctx *ctx)
|
||||||
|
{
|
||||||
|
int started = (ctx->field) ? 1 : 0;
|
||||||
|
int ready_data = 0;
|
||||||
|
|
||||||
|
if (!started && !chunk_strcat(out, "["))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (; ctx->field < INF_TOTAL_FIELDS; ctx->field++) {
|
||||||
|
int old_len;
|
||||||
|
int field = ctx->field;
|
||||||
|
|
||||||
|
if (!field_format(info, field))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (started && !chunk_strcat(out, ","))
|
||||||
|
goto err;
|
||||||
|
started = 1;
|
||||||
|
|
||||||
|
old_len = out->data;
|
||||||
|
chunk_appendf(out,
|
||||||
|
"{\"field\":{\"pos\":%d,\"name\":\"%s\"},"
|
||||||
|
"\"processNum\":%u,",
|
||||||
|
field, info_fields[field].name,
|
||||||
|
info[INF_PROCESS_NUM].u.u32);
|
||||||
|
if (old_len == out->data)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!stats_emit_json_field_tags(out, &info[field]))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!stats_emit_json_data_field(out, &info[field]))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!chunk_strcat(out, "}"))
|
||||||
|
goto err;
|
||||||
|
ready_data = out->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chunk_strcat(out, "]\n"))
|
||||||
|
goto err;
|
||||||
|
ctx->field = 0; /* we're done */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (!ready_data) {
|
||||||
|
/* not enough buffer space for a single entry.. */
|
||||||
|
chunk_reset(out);
|
||||||
|
chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}\n");
|
||||||
|
return 0; /* hard error */
|
||||||
|
}
|
||||||
|
/* push ready data and wait for a new buffer to complete the dump */
|
||||||
|
out->data = ready_data;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function dumps the schema onto the stream connector's read buffer.
|
||||||
|
* It returns 0 as long as it does not complete, non-zero upon completion.
|
||||||
|
* No state is used.
|
||||||
|
*
|
||||||
|
* Integer values bounded to the range [-(2**53)+1, (2**53)-1] as
|
||||||
|
* per the recommendation for interoperable integers in section 6 of RFC 7159.
|
||||||
|
*/
|
||||||
|
void stats_dump_json_schema(struct buffer *out)
|
||||||
|
{
|
||||||
|
|
||||||
|
int old_len = out->data;
|
||||||
|
|
||||||
|
chunk_strcat(out,
|
||||||
|
"{"
|
||||||
|
"\"$schema\":\"http://json-schema.org/draft-04/schema#\","
|
||||||
|
"\"oneOf\":["
|
||||||
|
"{"
|
||||||
|
"\"title\":\"Info\","
|
||||||
|
"\"type\":\"array\","
|
||||||
|
"\"items\":{"
|
||||||
|
"\"title\":\"InfoItem\","
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"field\":{\"$ref\":\"#/definitions/field\"},"
|
||||||
|
"\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
|
||||||
|
"\"tags\":{\"$ref\":\"#/definitions/tags\"},"
|
||||||
|
"\"value\":{\"$ref\":\"#/definitions/typedValue\"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"field\",\"processNum\",\"tags\","
|
||||||
|
"\"value\"]"
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"{"
|
||||||
|
"\"title\":\"Stat\","
|
||||||
|
"\"type\":\"array\","
|
||||||
|
"\"items\":{"
|
||||||
|
"\"title\":\"InfoItem\","
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"objType\":{"
|
||||||
|
"\"enum\":[\"Frontend\",\"Backend\",\"Listener\","
|
||||||
|
"\"Server\",\"Unknown\"]"
|
||||||
|
"},"
|
||||||
|
"\"proxyId\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":0"
|
||||||
|
"},"
|
||||||
|
"\"id\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":0"
|
||||||
|
"},"
|
||||||
|
"\"field\":{\"$ref\":\"#/definitions/field\"},"
|
||||||
|
"\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
|
||||||
|
"\"tags\":{\"$ref\":\"#/definitions/tags\"},"
|
||||||
|
"\"typedValue\":{\"$ref\":\"#/definitions/typedValue\"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"objType\",\"proxyId\",\"id\","
|
||||||
|
"\"field\",\"processNum\",\"tags\","
|
||||||
|
"\"value\"]"
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"{"
|
||||||
|
"\"title\":\"Error\","
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"errorStr\":{"
|
||||||
|
"\"type\":\"string\""
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"errorStr\"]"
|
||||||
|
"}"
|
||||||
|
"],"
|
||||||
|
"\"definitions\":{"
|
||||||
|
"\"field\":{"
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"pos\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":0"
|
||||||
|
"},"
|
||||||
|
"\"name\":{"
|
||||||
|
"\"type\":\"string\""
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"pos\",\"name\"]"
|
||||||
|
"},"
|
||||||
|
"\"processNum\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":1"
|
||||||
|
"},"
|
||||||
|
"\"tags\":{"
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"origin\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"Metric\",\"Status\",\"Key\","
|
||||||
|
"\"Config\",\"Product\",\"Unknown\"]"
|
||||||
|
"},"
|
||||||
|
"\"nature\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"Gauge\",\"Limit\",\"Min\",\"Max\","
|
||||||
|
"\"Rate\",\"Counter\",\"Duration\","
|
||||||
|
"\"Age\",\"Time\",\"Name\",\"Output\","
|
||||||
|
"\"Avg\", \"Unknown\"]"
|
||||||
|
"},"
|
||||||
|
"\"scope\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"Cluster\",\"Process\",\"Service\","
|
||||||
|
"\"System\",\"Unknown\"]"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"origin\",\"nature\",\"scope\"]"
|
||||||
|
"},"
|
||||||
|
"\"typedValue\":{"
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"oneOf\":["
|
||||||
|
"{\"$ref\":\"#/definitions/typedValue/definitions/s32Value\"},"
|
||||||
|
"{\"$ref\":\"#/definitions/typedValue/definitions/s64Value\"},"
|
||||||
|
"{\"$ref\":\"#/definitions/typedValue/definitions/u32Value\"},"
|
||||||
|
"{\"$ref\":\"#/definitions/typedValue/definitions/u64Value\"},"
|
||||||
|
"{\"$ref\":\"#/definitions/typedValue/definitions/strValue\"}"
|
||||||
|
"],"
|
||||||
|
"\"definitions\":{"
|
||||||
|
"\"s32Value\":{"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"type\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"s32\"]"
|
||||||
|
"},"
|
||||||
|
"\"value\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":-2147483648,"
|
||||||
|
"\"maximum\":2147483647"
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"type\",\"value\"]"
|
||||||
|
"},"
|
||||||
|
"\"s64Value\":{"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"type\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"s64\"]"
|
||||||
|
"},"
|
||||||
|
"\"value\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":-9007199254740991,"
|
||||||
|
"\"maximum\":9007199254740991"
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"type\",\"value\"]"
|
||||||
|
"},"
|
||||||
|
"\"u32Value\":{"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"type\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"u32\"]"
|
||||||
|
"},"
|
||||||
|
"\"value\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":0,"
|
||||||
|
"\"maximum\":4294967295"
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"type\",\"value\"]"
|
||||||
|
"},"
|
||||||
|
"\"u64Value\":{"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"type\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"u64\"]"
|
||||||
|
"},"
|
||||||
|
"\"value\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":0,"
|
||||||
|
"\"maximum\":9007199254740991"
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"type\",\"value\"]"
|
||||||
|
"},"
|
||||||
|
"\"strValue\":{"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"type\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"str\"]"
|
||||||
|
"},"
|
||||||
|
"\"value\":{\"type\":\"string\"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"type\",\"value\"]"
|
||||||
|
"},"
|
||||||
|
"\"unknownValue\":{"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"type\":{"
|
||||||
|
"\"type\":\"integer\","
|
||||||
|
"\"minimum\":0"
|
||||||
|
"},"
|
||||||
|
"\"value\":{"
|
||||||
|
"\"type\":\"string\","
|
||||||
|
"\"enum\":[\"unknown\"]"
|
||||||
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"required\":[\"type\",\"value\"]"
|
||||||
|
"}"
|
||||||
|
"}"
|
||||||
|
"}"
|
||||||
|
"}"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
if (old_len == out->data) {
|
||||||
|
chunk_reset(out);
|
||||||
|
chunk_appendf(out,
|
||||||
|
"{\"errorStr\":\"output buffer too short\"}");
|
||||||
|
}
|
||||||
|
chunk_appendf(out, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function dumps the schema onto the stream connector's read buffer.
|
||||||
|
* It returns 0 as long as it does not complete, non-zero upon completion.
|
||||||
|
* No state is used.
|
||||||
|
*/
|
||||||
|
int stats_dump_json_schema_to_buffer(struct appctx *appctx)
|
||||||
|
{
|
||||||
|
struct show_stat_ctx *ctx = appctx->svcctx;
|
||||||
|
struct buffer *chk = &ctx->chunk;
|
||||||
|
|
||||||
|
chunk_reset(chk);
|
||||||
|
|
||||||
|
stats_dump_json_schema(chk);
|
||||||
|
|
||||||
|
if (applet_putchk(appctx, chk) == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
530
src/stats.c
530
src/stats.c
@ -58,6 +58,7 @@
|
|||||||
#include <haproxy/session.h>
|
#include <haproxy/session.h>
|
||||||
#include <haproxy/stats.h>
|
#include <haproxy/stats.h>
|
||||||
#include <haproxy/stats-html.h>
|
#include <haproxy/stats-html.h>
|
||||||
|
#include <haproxy/stats-json.h>
|
||||||
#include <haproxy/stconn.h>
|
#include <haproxy/stconn.h>
|
||||||
#include <haproxy/stream.h>
|
#include <haproxy/stream.h>
|
||||||
#include <haproxy/task.h>
|
#include <haproxy/task.h>
|
||||||
@ -283,7 +284,7 @@ const struct name_desc stat_fields[ST_F_TOTAL_FIELDS] = {
|
|||||||
THREAD_LOCAL struct field info[INF_TOTAL_FIELDS];
|
THREAD_LOCAL struct field info[INF_TOTAL_FIELDS];
|
||||||
|
|
||||||
/* description of statistics (static and dynamic) */
|
/* description of statistics (static and dynamic) */
|
||||||
static struct name_desc *stat_f[STATS_DOMAIN_COUNT];
|
struct name_desc *stat_f[STATS_DOMAIN_COUNT];
|
||||||
static size_t stat_count[STATS_DOMAIN_COUNT];
|
static size_t stat_count[STATS_DOMAIN_COUNT];
|
||||||
|
|
||||||
/* one line for stats */
|
/* one line for stats */
|
||||||
@ -298,8 +299,6 @@ struct list stats_module_list[STATS_DOMAIN_COUNT] = {
|
|||||||
THREAD_LOCAL void *trash_counters;
|
THREAD_LOCAL void *trash_counters;
|
||||||
|
|
||||||
|
|
||||||
static void stats_dump_json_schema(struct buffer *out);
|
|
||||||
|
|
||||||
int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx)
|
int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx)
|
||||||
{
|
{
|
||||||
struct show_stat_ctx *ctx = appctx->svcctx;
|
struct show_stat_ctx *ctx = appctx->svcctx;
|
||||||
@ -458,61 +457,6 @@ int stats_emit_typed_data_field(struct buffer *out, const struct field *f)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per
|
|
||||||
* the recommendation for interoperable integers in section 6 of RFC 7159.
|
|
||||||
*/
|
|
||||||
#define JSON_INT_MAX ((1ULL << 53) - 1)
|
|
||||||
#define JSON_INT_MIN (0 - JSON_INT_MAX)
|
|
||||||
|
|
||||||
/* Emits a stats field value and its type in JSON.
|
|
||||||
* Returns non-zero on success, 0 on error.
|
|
||||||
*/
|
|
||||||
int stats_emit_json_data_field(struct buffer *out, const struct field *f)
|
|
||||||
{
|
|
||||||
int old_len;
|
|
||||||
char buf[20];
|
|
||||||
const char *type, *value = buf, *quote = "";
|
|
||||||
|
|
||||||
switch (field_format(f, 0)) {
|
|
||||||
case FF_EMPTY: return 1;
|
|
||||||
case FF_S32: type = "\"s32\"";
|
|
||||||
snprintf(buf, sizeof(buf), "%d", f->u.s32);
|
|
||||||
break;
|
|
||||||
case FF_U32: type = "\"u32\"";
|
|
||||||
snprintf(buf, sizeof(buf), "%u", f->u.u32);
|
|
||||||
break;
|
|
||||||
case FF_S64: type = "\"s64\"";
|
|
||||||
if (f->u.s64 < JSON_INT_MIN || f->u.s64 > JSON_INT_MAX)
|
|
||||||
return 0;
|
|
||||||
type = "\"s64\"";
|
|
||||||
snprintf(buf, sizeof(buf), "%lld", (long long)f->u.s64);
|
|
||||||
break;
|
|
||||||
case FF_U64: if (f->u.u64 > JSON_INT_MAX)
|
|
||||||
return 0;
|
|
||||||
type = "\"u64\"";
|
|
||||||
snprintf(buf, sizeof(buf), "%llu",
|
|
||||||
(unsigned long long) f->u.u64);
|
|
||||||
break;
|
|
||||||
case FF_FLT: type = "\"flt\"";
|
|
||||||
flt_trim(buf, 0, snprintf(buf, sizeof(buf), "%f", f->u.flt));
|
|
||||||
break;
|
|
||||||
case FF_STR: type = "\"str\"";
|
|
||||||
value = field_str(f, 0);
|
|
||||||
quote = "\"";
|
|
||||||
break;
|
|
||||||
default: snprintf(buf, sizeof(buf), "%u", f->type);
|
|
||||||
type = buf;
|
|
||||||
value = "unknown";
|
|
||||||
quote = "\"";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
old_len = out->data;
|
|
||||||
chunk_appendf(out, ",\"value\":{\"type\":%s,\"value\":%s%s%s}",
|
|
||||||
type, quote, value, quote);
|
|
||||||
return !(old_len == out->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Emits an encoding of the field type on 3 characters followed by a delimiter.
|
/* Emits an encoding of the field type on 3 characters followed by a delimiter.
|
||||||
* Returns non-zero on success, 0 if the buffer is full.
|
* Returns non-zero on success, 0 if the buffer is full.
|
||||||
*/
|
*/
|
||||||
@ -557,56 +501,6 @@ int stats_emit_field_tags(struct buffer *out, const struct field *f,
|
|||||||
return chunk_appendf(out, "%c%c%c%c", origin, nature, scope, delim);
|
return chunk_appendf(out, "%c%c%c%c", origin, nature, scope, delim);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Emits an encoding of the field type as JSON.
|
|
||||||
* Returns non-zero on success, 0 if the buffer is full.
|
|
||||||
*/
|
|
||||||
int stats_emit_json_field_tags(struct buffer *out, const struct field *f)
|
|
||||||
{
|
|
||||||
const char *origin, *nature, *scope;
|
|
||||||
int old_len;
|
|
||||||
|
|
||||||
switch (field_origin(f, 0)) {
|
|
||||||
case FO_METRIC: origin = "Metric"; break;
|
|
||||||
case FO_STATUS: origin = "Status"; break;
|
|
||||||
case FO_KEY: origin = "Key"; break;
|
|
||||||
case FO_CONFIG: origin = "Config"; break;
|
|
||||||
case FO_PRODUCT: origin = "Product"; break;
|
|
||||||
default: origin = "Unknown"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (field_nature(f, 0)) {
|
|
||||||
case FN_GAUGE: nature = "Gauge"; break;
|
|
||||||
case FN_LIMIT: nature = "Limit"; break;
|
|
||||||
case FN_MIN: nature = "Min"; break;
|
|
||||||
case FN_MAX: nature = "Max"; break;
|
|
||||||
case FN_RATE: nature = "Rate"; break;
|
|
||||||
case FN_COUNTER: nature = "Counter"; break;
|
|
||||||
case FN_DURATION: nature = "Duration"; break;
|
|
||||||
case FN_AGE: nature = "Age"; break;
|
|
||||||
case FN_TIME: nature = "Time"; break;
|
|
||||||
case FN_NAME: nature = "Name"; break;
|
|
||||||
case FN_OUTPUT: nature = "Output"; break;
|
|
||||||
case FN_AVG: nature = "Avg"; break;
|
|
||||||
default: nature = "Unknown"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (field_scope(f, 0)) {
|
|
||||||
case FS_PROCESS: scope = "Process"; break;
|
|
||||||
case FS_SERVICE: scope = "Service"; break;
|
|
||||||
case FS_SYSTEM: scope = "System"; break;
|
|
||||||
case FS_CLUSTER: scope = "Cluster"; break;
|
|
||||||
default: scope = "Unknown"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
old_len = out->data;
|
|
||||||
chunk_appendf(out, "\"tags\":{"
|
|
||||||
"\"origin\":\"%s\","
|
|
||||||
"\"nature\":\"%s\","
|
|
||||||
"\"scope\":\"%s\""
|
|
||||||
"}", origin, nature, scope);
|
|
||||||
return !(old_len == out->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dump all fields from <stats> into <out> using CSV format */
|
/* Dump all fields from <stats> into <out> using CSV format */
|
||||||
static int stats_dump_fields_csv(struct buffer *out,
|
static int stats_dump_fields_csv(struct buffer *out,
|
||||||
const struct field *stats, size_t stats_count,
|
const struct field *stats, size_t stats_count,
|
||||||
@ -686,179 +580,6 @@ static int stats_dump_fields_typed(struct buffer *out,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dump all fields from <stats> into <out> using the "show info json" format */
|
|
||||||
static int stats_dump_json_info_fields(struct buffer *out,
|
|
||||||
const struct field *info,
|
|
||||||
struct show_stat_ctx *ctx)
|
|
||||||
{
|
|
||||||
int started = (ctx->field) ? 1 : 0;
|
|
||||||
int ready_data = 0;
|
|
||||||
|
|
||||||
if (!started && !chunk_strcat(out, "["))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (; ctx->field < INF_TOTAL_FIELDS; ctx->field++) {
|
|
||||||
int old_len;
|
|
||||||
int field = ctx->field;
|
|
||||||
|
|
||||||
if (!field_format(info, field))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (started && !chunk_strcat(out, ","))
|
|
||||||
goto err;
|
|
||||||
started = 1;
|
|
||||||
|
|
||||||
old_len = out->data;
|
|
||||||
chunk_appendf(out,
|
|
||||||
"{\"field\":{\"pos\":%d,\"name\":\"%s\"},"
|
|
||||||
"\"processNum\":%u,",
|
|
||||||
field, info_fields[field].name,
|
|
||||||
info[INF_PROCESS_NUM].u.u32);
|
|
||||||
if (old_len == out->data)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (!stats_emit_json_field_tags(out, &info[field]))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (!stats_emit_json_data_field(out, &info[field]))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (!chunk_strcat(out, "}"))
|
|
||||||
goto err;
|
|
||||||
ready_data = out->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chunk_strcat(out, "]\n"))
|
|
||||||
goto err;
|
|
||||||
ctx->field = 0; /* we're done */
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
err:
|
|
||||||
if (!ready_data) {
|
|
||||||
/* not enough buffer space for a single entry.. */
|
|
||||||
chunk_reset(out);
|
|
||||||
chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}\n");
|
|
||||||
return 0; /* hard error */
|
|
||||||
}
|
|
||||||
/* push ready data and wait for a new buffer to complete the dump */
|
|
||||||
out->data = ready_data;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stats_print_proxy_field_json(struct buffer *out,
|
|
||||||
const struct field *stat,
|
|
||||||
const char *name,
|
|
||||||
int pos,
|
|
||||||
uint32_t field_type,
|
|
||||||
uint32_t iid,
|
|
||||||
uint32_t sid,
|
|
||||||
uint32_t pid)
|
|
||||||
{
|
|
||||||
const char *obj_type;
|
|
||||||
switch (field_type) {
|
|
||||||
case STATS_TYPE_FE: obj_type = "Frontend"; break;
|
|
||||||
case STATS_TYPE_BE: obj_type = "Backend"; break;
|
|
||||||
case STATS_TYPE_SO: obj_type = "Listener"; break;
|
|
||||||
case STATS_TYPE_SV: obj_type = "Server"; break;
|
|
||||||
default: obj_type = "Unknown"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_appendf(out,
|
|
||||||
"{"
|
|
||||||
"\"objType\":\"%s\","
|
|
||||||
"\"proxyId\":%u,"
|
|
||||||
"\"id\":%u,"
|
|
||||||
"\"field\":{\"pos\":%d,\"name\":\"%s\"},"
|
|
||||||
"\"processNum\":%u,",
|
|
||||||
obj_type, iid, sid, pos, name, pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stats_print_rslv_field_json(struct buffer *out,
|
|
||||||
const struct field *stat,
|
|
||||||
const char *name,
|
|
||||||
int pos)
|
|
||||||
{
|
|
||||||
chunk_appendf(out,
|
|
||||||
"{"
|
|
||||||
"\"field\":{\"pos\":%d,\"name\":\"%s\"},",
|
|
||||||
pos, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Dump all fields from <stats> into <out> using a typed "field:desc:type:value" format */
|
|
||||||
static int stats_dump_fields_json(struct buffer *out,
|
|
||||||
const struct field *stats, size_t stats_count,
|
|
||||||
struct show_stat_ctx *ctx)
|
|
||||||
{
|
|
||||||
int flags = ctx->flags;
|
|
||||||
int domain = ctx->domain;
|
|
||||||
int started = (ctx->field) ? 1 : 0;
|
|
||||||
int ready_data = 0;
|
|
||||||
|
|
||||||
if (!started && (flags & STAT_STARTED) && !chunk_strcat(out, ","))
|
|
||||||
return 0;
|
|
||||||
if (!started && !chunk_strcat(out, "["))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (; ctx->field < stats_count; ctx->field++) {
|
|
||||||
int old_len;
|
|
||||||
int field = ctx->field;
|
|
||||||
|
|
||||||
if (!stats[field].type)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (started && !chunk_strcat(out, ","))
|
|
||||||
goto err;
|
|
||||||
started = 1;
|
|
||||||
|
|
||||||
old_len = out->data;
|
|
||||||
if (domain == STATS_DOMAIN_PROXY) {
|
|
||||||
stats_print_proxy_field_json(out, &stats[field],
|
|
||||||
stat_f[domain][field].name,
|
|
||||||
field,
|
|
||||||
stats[ST_F_TYPE].u.u32,
|
|
||||||
stats[ST_F_IID].u.u32,
|
|
||||||
stats[ST_F_SID].u.u32,
|
|
||||||
stats[ST_F_PID].u.u32);
|
|
||||||
} else if (domain == STATS_DOMAIN_RESOLVERS) {
|
|
||||||
stats_print_rslv_field_json(out, &stats[field],
|
|
||||||
stat_f[domain][field].name,
|
|
||||||
field);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old_len == out->data)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (!stats_emit_json_field_tags(out, &stats[field]))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (!stats_emit_json_data_field(out, &stats[field]))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (!chunk_strcat(out, "}"))
|
|
||||||
goto err;
|
|
||||||
ready_data = out->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chunk_strcat(out, "]"))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
ctx->field = 0; /* we're done */
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
err:
|
|
||||||
if (!ready_data) {
|
|
||||||
/* not enough buffer space for a single entry.. */
|
|
||||||
chunk_reset(out);
|
|
||||||
if (ctx->flags & STAT_STARTED)
|
|
||||||
chunk_strcat(out, ",");
|
|
||||||
chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}");
|
|
||||||
return 0; /* hard error */
|
|
||||||
}
|
|
||||||
/* push ready data and wait for a new buffer to complete the dump */
|
|
||||||
out->data = ready_data;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stats_dump_one_line(const struct field *stats, size_t stats_count,
|
int stats_dump_one_line(const struct field *stats, size_t stats_count,
|
||||||
struct appctx *appctx)
|
struct appctx *appctx)
|
||||||
@ -2408,23 +2129,6 @@ int stats_dump_proxy_to_buffer(struct stconn *sc, struct buffer *buf, struct htx
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dumps the stats JSON header to <out> buffer. The caller is responsible for
|
|
||||||
* clearing it if needed.
|
|
||||||
*/
|
|
||||||
static void stats_dump_json_header(struct buffer *out)
|
|
||||||
{
|
|
||||||
chunk_strcat(out, "[");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Dumps the JSON stats trailer block to <out> buffer. The caller is
|
|
||||||
* responsible for clearing it if needed.
|
|
||||||
*/
|
|
||||||
static void stats_dump_json_end(struct buffer *out)
|
|
||||||
{
|
|
||||||
chunk_strcat(out, "]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Uses <appctx.ctx.stats.obj1> as a pointer to the current proxy and <obj2> as
|
/* Uses <appctx.ctx.stats.obj1> as a pointer to the current proxy and <obj2> as
|
||||||
* a pointer to the current server/listener.
|
* a pointer to the current server/listener.
|
||||||
*/
|
*/
|
||||||
@ -2793,236 +2497,6 @@ static int stats_dump_info_to_buffer(struct stconn *sc)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function dumps the schema onto the stream connector's read buffer.
|
|
||||||
* It returns 0 as long as it does not complete, non-zero upon completion.
|
|
||||||
* No state is used.
|
|
||||||
*
|
|
||||||
* Integer values bounded to the range [-(2**53)+1, (2**53)-1] as
|
|
||||||
* per the recommendation for interoperable integers in section 6 of RFC 7159.
|
|
||||||
*/
|
|
||||||
static void stats_dump_json_schema(struct buffer *out)
|
|
||||||
{
|
|
||||||
|
|
||||||
int old_len = out->data;
|
|
||||||
|
|
||||||
chunk_strcat(out,
|
|
||||||
"{"
|
|
||||||
"\"$schema\":\"http://json-schema.org/draft-04/schema#\","
|
|
||||||
"\"oneOf\":["
|
|
||||||
"{"
|
|
||||||
"\"title\":\"Info\","
|
|
||||||
"\"type\":\"array\","
|
|
||||||
"\"items\":{"
|
|
||||||
"\"title\":\"InfoItem\","
|
|
||||||
"\"type\":\"object\","
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"field\":{\"$ref\":\"#/definitions/field\"},"
|
|
||||||
"\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
|
|
||||||
"\"tags\":{\"$ref\":\"#/definitions/tags\"},"
|
|
||||||
"\"value\":{\"$ref\":\"#/definitions/typedValue\"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"field\",\"processNum\",\"tags\","
|
|
||||||
"\"value\"]"
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"{"
|
|
||||||
"\"title\":\"Stat\","
|
|
||||||
"\"type\":\"array\","
|
|
||||||
"\"items\":{"
|
|
||||||
"\"title\":\"InfoItem\","
|
|
||||||
"\"type\":\"object\","
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"objType\":{"
|
|
||||||
"\"enum\":[\"Frontend\",\"Backend\",\"Listener\","
|
|
||||||
"\"Server\",\"Unknown\"]"
|
|
||||||
"},"
|
|
||||||
"\"proxyId\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":0"
|
|
||||||
"},"
|
|
||||||
"\"id\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":0"
|
|
||||||
"},"
|
|
||||||
"\"field\":{\"$ref\":\"#/definitions/field\"},"
|
|
||||||
"\"processNum\":{\"$ref\":\"#/definitions/processNum\"},"
|
|
||||||
"\"tags\":{\"$ref\":\"#/definitions/tags\"},"
|
|
||||||
"\"typedValue\":{\"$ref\":\"#/definitions/typedValue\"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"objType\",\"proxyId\",\"id\","
|
|
||||||
"\"field\",\"processNum\",\"tags\","
|
|
||||||
"\"value\"]"
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"{"
|
|
||||||
"\"title\":\"Error\","
|
|
||||||
"\"type\":\"object\","
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"errorStr\":{"
|
|
||||||
"\"type\":\"string\""
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"errorStr\"]"
|
|
||||||
"}"
|
|
||||||
"],"
|
|
||||||
"\"definitions\":{"
|
|
||||||
"\"field\":{"
|
|
||||||
"\"type\":\"object\","
|
|
||||||
"\"pos\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":0"
|
|
||||||
"},"
|
|
||||||
"\"name\":{"
|
|
||||||
"\"type\":\"string\""
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"pos\",\"name\"]"
|
|
||||||
"},"
|
|
||||||
"\"processNum\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":1"
|
|
||||||
"},"
|
|
||||||
"\"tags\":{"
|
|
||||||
"\"type\":\"object\","
|
|
||||||
"\"origin\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"Metric\",\"Status\",\"Key\","
|
|
||||||
"\"Config\",\"Product\",\"Unknown\"]"
|
|
||||||
"},"
|
|
||||||
"\"nature\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"Gauge\",\"Limit\",\"Min\",\"Max\","
|
|
||||||
"\"Rate\",\"Counter\",\"Duration\","
|
|
||||||
"\"Age\",\"Time\",\"Name\",\"Output\","
|
|
||||||
"\"Avg\", \"Unknown\"]"
|
|
||||||
"},"
|
|
||||||
"\"scope\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"Cluster\",\"Process\",\"Service\","
|
|
||||||
"\"System\",\"Unknown\"]"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"origin\",\"nature\",\"scope\"]"
|
|
||||||
"},"
|
|
||||||
"\"typedValue\":{"
|
|
||||||
"\"type\":\"object\","
|
|
||||||
"\"oneOf\":["
|
|
||||||
"{\"$ref\":\"#/definitions/typedValue/definitions/s32Value\"},"
|
|
||||||
"{\"$ref\":\"#/definitions/typedValue/definitions/s64Value\"},"
|
|
||||||
"{\"$ref\":\"#/definitions/typedValue/definitions/u32Value\"},"
|
|
||||||
"{\"$ref\":\"#/definitions/typedValue/definitions/u64Value\"},"
|
|
||||||
"{\"$ref\":\"#/definitions/typedValue/definitions/strValue\"}"
|
|
||||||
"],"
|
|
||||||
"\"definitions\":{"
|
|
||||||
"\"s32Value\":{"
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"type\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"s32\"]"
|
|
||||||
"},"
|
|
||||||
"\"value\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":-2147483648,"
|
|
||||||
"\"maximum\":2147483647"
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"type\",\"value\"]"
|
|
||||||
"},"
|
|
||||||
"\"s64Value\":{"
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"type\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"s64\"]"
|
|
||||||
"},"
|
|
||||||
"\"value\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":-9007199254740991,"
|
|
||||||
"\"maximum\":9007199254740991"
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"type\",\"value\"]"
|
|
||||||
"},"
|
|
||||||
"\"u32Value\":{"
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"type\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"u32\"]"
|
|
||||||
"},"
|
|
||||||
"\"value\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":0,"
|
|
||||||
"\"maximum\":4294967295"
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"type\",\"value\"]"
|
|
||||||
"},"
|
|
||||||
"\"u64Value\":{"
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"type\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"u64\"]"
|
|
||||||
"},"
|
|
||||||
"\"value\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":0,"
|
|
||||||
"\"maximum\":9007199254740991"
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"type\",\"value\"]"
|
|
||||||
"},"
|
|
||||||
"\"strValue\":{"
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"type\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"str\"]"
|
|
||||||
"},"
|
|
||||||
"\"value\":{\"type\":\"string\"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"type\",\"value\"]"
|
|
||||||
"},"
|
|
||||||
"\"unknownValue\":{"
|
|
||||||
"\"properties\":{"
|
|
||||||
"\"type\":{"
|
|
||||||
"\"type\":\"integer\","
|
|
||||||
"\"minimum\":0"
|
|
||||||
"},"
|
|
||||||
"\"value\":{"
|
|
||||||
"\"type\":\"string\","
|
|
||||||
"\"enum\":[\"unknown\"]"
|
|
||||||
"}"
|
|
||||||
"},"
|
|
||||||
"\"required\":[\"type\",\"value\"]"
|
|
||||||
"}"
|
|
||||||
"}"
|
|
||||||
"}"
|
|
||||||
"}"
|
|
||||||
"}");
|
|
||||||
|
|
||||||
if (old_len == out->data) {
|
|
||||||
chunk_reset(out);
|
|
||||||
chunk_appendf(out,
|
|
||||||
"{\"errorStr\":\"output buffer too short\"}");
|
|
||||||
}
|
|
||||||
chunk_appendf(out, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function dumps the schema onto the stream connector's read buffer.
|
|
||||||
* It returns 0 as long as it does not complete, non-zero upon completion.
|
|
||||||
* No state is used.
|
|
||||||
*/
|
|
||||||
static int stats_dump_json_schema_to_buffer(struct appctx *appctx)
|
|
||||||
{
|
|
||||||
struct show_stat_ctx *ctx = appctx->svcctx;
|
|
||||||
struct buffer *chk = &ctx->chunk;
|
|
||||||
|
|
||||||
chunk_reset(chk);
|
|
||||||
|
|
||||||
stats_dump_json_schema(chk);
|
|
||||||
|
|
||||||
if (applet_putchk(appctx, chk) == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cli_parse_clear_counters(char **args, char *payload, struct appctx *appctx, void *private)
|
static int cli_parse_clear_counters(char **args, char *payload, struct appctx *appctx, void *private)
|
||||||
{
|
{
|
||||||
struct proxy *px;
|
struct proxy *px;
|
||||||
|
Loading…
Reference in New Issue
Block a user