diff --git a/include/types/stats.h b/include/types/stats.h index 2a71ff64e..d99d51911 100644 --- a/include/types/stats.h +++ b/include/types/stats.h @@ -28,9 +28,12 @@ #define STAT_NO_REFRESH 0x00000010 /* do not automatically refresh the stats page */ #define STAT_ADMIN 0x00000020 /* indicate a stats admin level */ #define STAT_CHUNKED 0x00000040 /* use chunked encoding (HTTP/1.1) */ +#define STAT_JSON_SCHM 0x00000080 /* dump the json schema */ #define STAT_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */ #define STAT_STARTED 0x01000000 /* some output has occurred */ +#define STAT_FMT_MASK 0x00000007 + #define STATS_TYPE_FE 0 #define STATS_TYPE_BE 1 #define STATS_TYPE_SV 2 diff --git a/src/http_ana.c b/src/http_ana.c index eef0c09ca..6fff39b30 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -4399,19 +4399,35 @@ static int http_handle_stats(struct stream *s, struct channel *req) for (h = lookup; h <= end - 4; h++) { if (memcmp(h, ";csv", 4) == 0) { - appctx->ctx.stats.flags &= ~STAT_FMT_HTML; + appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM); break; } } for (h = lookup; h <= end - 6; h++) { if (memcmp(h, ";typed", 6) == 0) { - appctx->ctx.stats.flags &= ~STAT_FMT_HTML; + appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM); appctx->ctx.stats.flags |= STAT_FMT_TYPED; break; } } + for (h = lookup; h <= end - 5; h++) { + if (memcmp(h, ";json", 5) == 0) { + appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM); + appctx->ctx.stats.flags |= STAT_FMT_JSON; + break; + } + } + + for (h = lookup; h <= end - 12; h++) { + if (memcmp(h, ";json-schema", 12) == 0) { + appctx->ctx.stats.flags &= ~STAT_FMT_MASK; + appctx->ctx.stats.flags |= STAT_JSON_SCHM; + break; + } + } + for (h = lookup; h <= end - 8; h++) { if (memcmp(h, ";st=", 4) == 0) { int i; diff --git a/src/stats.c b/src/stats.c index 34efbfbbb..e59ad10bb 100644 --- a/src/stats.c +++ b/src/stats.c @@ -251,6 +251,7 @@ static THREAD_LOCAL struct field info[INF_TOTAL_FIELDS]; /* one line of stats */ static THREAD_LOCAL struct field stats[ST_F_TOTAL_FIELDS]; +static void stats_dump_json_schema(struct buffer *out); static int stats_putchk(struct channel *chn, struct htx *htx, struct buffer *chk) { @@ -2526,6 +2527,12 @@ static void stats_dump_html_info(struct stream_interface *si, struct uri_auth *u (uri->refresh > 0) ? ";norefresh" : "", scope_txt); + chunk_appendf(&trash, + "
  • JSON export (schema)
    \n", + uri->uri_prefix, + (uri->refresh > 0) ? ";norefresh" : "", + scope_txt, uri->uri_prefix); + chunk_appendf(&trash, "" "" @@ -2682,6 +2689,8 @@ static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *ht case STAT_ST_HEAD: if (appctx->ctx.stats.flags & STAT_FMT_HTML) stats_dump_html_head(uri); + else if (appctx->ctx.stats.flags & STAT_JSON_SCHM) + stats_dump_json_schema(&trash); else if (appctx->ctx.stats.flags & STAT_FMT_JSON) stats_dump_json_header(); else if (!(appctx->ctx.stats.flags & STAT_FMT_TYPED)) @@ -2690,6 +2699,10 @@ static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *ht if (!stats_putchk(rep, htx, &trash)) goto full; + if (appctx->ctx.stats.flags & STAT_JSON_SCHM) { + appctx->st2 = STAT_ST_FIN; + return 1; + } appctx->st2 = STAT_ST_INFO; /* fall through */ @@ -3124,6 +3137,10 @@ static int stats_send_http_headers(struct stream_interface *si, struct htx *htx) if (!htx_add_header(htx, ist("Content-Type"), ist("text/html"))) goto full; } + else if (appctx->ctx.stats.flags & (STAT_FMT_JSON|STAT_JSON_SCHM)) { + if (!htx_add_header(htx, ist("Content-Type"), ist("application/json"))) + goto full; + } else { if (!htx_add_header(htx, ist("Content-Type"), ist("text/plain"))) goto full;