--- /dev/null
+#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;
+}
#include <haproxy/session.h>
#include <haproxy/stats.h>
#include <haproxy/stats-html.h>
+#include <haproxy/stats-json.h>
#include <haproxy/stconn.h>
#include <haproxy/stream.h>
#include <haproxy/task.h>
THREAD_LOCAL struct field info[INF_TOTAL_FIELDS];
/* 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];
/* one line for stats */
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)
{
struct show_stat_ctx *ctx = appctx->svcctx;
}
}
-/* 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.
* Returns non-zero on success, 0 if the buffer is full.
*/
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 */
static int stats_dump_fields_csv(struct buffer *out,
const struct field *stats, size_t stats_count,
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,
struct appctx *appctx)
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
* a pointer to the current server/listener.
*/
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)
{
struct proxy *px;