Repository: trafficserver Updated Branches: refs/heads/master a28621e47 -> 454a2e5be
TS-3071: optionally emit JSON numbers in stats_over_http Add a new option to emit floats, counters and integers and JSON numbers rather than strings. Since some systems (*cough* Java) don't deal well with uint64_t, add another option to wrap integers and counters to int64_t. This closes #100. Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/454a2e5b Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/454a2e5b Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/454a2e5b Branch: refs/heads/master Commit: 454a2e5be5e012bbf6080794edbae8bce7fbf354 Parents: a28621e Author: James Peach <[email protected]> Authored: Thu Sep 11 09:30:33 2014 -0700 Committer: James Peach <[email protected]> Committed: Thu Sep 11 12:20:39 2014 -0700 ---------------------------------------------------------------------- CHANGES | 3 + doc/reference/plugins/stats_over_http.en.rst | 22 +++++- plugins/stats_over_http/stats_over_http.c | 85 ++++++++++++++++++----- 3 files changed, 90 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/454a2e5b/CHANGES ---------------------------------------------------------------------- diff --git a/CHANGES b/CHANGES index 64abe0f..0a2c782 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache Traffic Server 5.2.0 + *) [TS-3071] Optionally emit JSON numbers in stats_over_http. + Thanks to Saltuk Alakus <[email protected]> + *) [TS-3069] Add mysql_remap to autoconf *) [TS-3066] Fix various build issues for OmniOS, broken since 5.0.x. http://git-wip-us.apache.org/repos/asf/trafficserver/blob/454a2e5b/doc/reference/plugins/stats_over_http.en.rst ---------------------------------------------------------------------- diff --git a/doc/reference/plugins/stats_over_http.en.rst b/doc/reference/plugins/stats_over_http.en.rst index 89ec970..59ba7fc 100644 --- a/doc/reference/plugins/stats_over_http.en.rst +++ b/doc/reference/plugins/stats_over_http.en.rst @@ -35,10 +35,26 @@ default URL:: http://host:port/_stats +where host and port is the hostname/IP and port number of the server. -where host and port is the hostname/IP and port number of the server. You can -optionally modify the path to use, and this is highly recommended in a public -facing server. For example:: +Plugin Options +-------------- + +.. option:: --integer-counters + +This option causes the plugin to emit floating point and integral +metric values as JSON numbers, rather then JSON strings. This can +cause interoperability problems since integer metrics have a 64-bit +unsigned range. + +.. option:: --wrap-counters + +This option wraps 64-bit unsigned values to the 64-bit signed range. +This aids interoperability with Java, since prior to the Java SE 8 +release, Java did not have a 64-bit unsigned type. + +You can optionally modify the path to use, and this is highly +recommended in a public facing server. For example:: stats_over_http.so 81c075bc0cca1435ea899ba4ad72766b http://git-wip-us.apache.org/repos/asf/trafficserver/blob/454a2e5b/plugins/stats_over_http/stats_over_http.c ---------------------------------------------------------------------- diff --git a/plugins/stats_over_http/stats_over_http.c b/plugins/stats_over_http/stats_over_http.c index 7e6071a..96fb63a 100644 --- a/plugins/stats_over_http/stats_over_http.c +++ b/plugins/stats_over_http/stats_over_http.c @@ -26,19 +26,25 @@ #include <stdio.h> #include <stdlib.h> +#include <stdbool.h> #include <ctype.h> #include <limits.h> #include <ts/ts.h> #include <string.h> - #include <inttypes.h> +#include <getopt.h> #include "ink_defs.h" +#define PLUGIN_NAME "stats_over_http" + /* global holding the path used for access to this JSON data */ static const char* url_path = "_stats"; static int url_path_len; +static bool integer_counters = false; +static bool wrap_counters = false; + typedef struct stats_state_t { TSVConn net_vc; @@ -102,7 +108,7 @@ stats_add_resp_header(stats_state * my_state) static void stats_process_read(TSCont contp, TSEvent event, stats_state * my_state) { - TSDebug("istats", "stats_process_read(%d)", event); + TSDebug(PLUGIN_NAME, "stats_process_read(%d)", event); if (event == TS_EVENT_VCONN_READ_READY) { my_state->output_bytes = stats_add_resp_header(my_state); TSVConnShutdown(my_state->net_vc, 1, 0); @@ -126,6 +132,26 @@ stats_process_read(TSCont contp, TSEvent event, stats_state * my_state) if(snprintf(b, sizeof(b), "\"%s\": \"" fmt "\",\n", a, v) < sizeof(b)) \ APPEND(b); \ } while(0) +#define APPEND_STAT_NUMERIC(a, fmt, v) do { \ + char b[256]; \ + if (integer_counters) { \ + if (snprintf(b, sizeof(b), "\"%s\": " fmt ",\n", a, v) < sizeof(b)) { APPEND(b); } \ + } else { \ + if (snprintf(b, sizeof(b), "\"%s\": \"" fmt "\",\n", a, v) < sizeof(b)) { APPEND(b); } \ + } \ +} while(0) + +// This wraps uint64_t values to the int64_t range to fit into a Java long. Java 8 has an unsigned long which +// can interoperate with a full uint64_t, but it's unlikely that much of the ecosystem supports that yet. +static uint64_t +wrap_unsigned_counter(uint64_t value) +{ + if (wrap_counters) { + return (value > INT64_MAX) ? value % INT64_MAX : value; + } else { + return value; + } +} static void json_out_stat(TSRecordType rec_type ATS_UNUSED, void *edata, int registered ATS_UNUSED, @@ -135,15 +161,15 @@ json_out_stat(TSRecordType rec_type ATS_UNUSED, void *edata, int registered ATS_ switch(data_type) { case TS_RECORDDATATYPE_COUNTER: - APPEND_STAT(name, "%" PRIu64, datum->rec_counter); break; + APPEND_STAT_NUMERIC(name, "%" PRIu64, wrap_unsigned_counter(datum->rec_counter)); break; case TS_RECORDDATATYPE_INT: - APPEND_STAT(name, "%" PRIu64, datum->rec_int); break; + APPEND_STAT_NUMERIC(name, "%" PRIu64, wrap_unsigned_counter(datum->rec_int)); break; case TS_RECORDDATATYPE_FLOAT: - APPEND_STAT(name, "%f", datum->rec_float); break; + APPEND_STAT_NUMERIC(name, "%f", datum->rec_float); break; case TS_RECORDDATATYPE_STRING: APPEND_STAT(name, "%s", datum->rec_string); break; default: - TSDebug("istats", "unknown type for %s: %d", name, data_type); + TSDebug(PLUGIN_NAME, "unknown type for %s: %d", name, data_type); break; } } @@ -166,7 +192,7 @@ stats_process_write(TSCont contp, TSEvent event, stats_state * my_state) { if (event == TS_EVENT_VCONN_WRITE_READY) { if (my_state->body_written == 0) { - TSDebug("istats", "plugin adding response body"); + TSDebug(PLUGIN_NAME, "plugin adding response body"); my_state->body_written = 1; json_out_stats(my_state); TSVIONBytesSet(my_state->write_vio, my_state->output_bytes); @@ -208,7 +234,7 @@ stats_origin(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata) TSMLoc hdr_loc = NULL, url_loc = NULL; TSEvent reenable = TS_EVENT_HTTP_CONTINUE; - TSDebug("istats", "in the read stuff"); + TSDebug(PLUGIN_NAME, "in the read stuff"); if (TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc) != TS_SUCCESS) goto cleanup; @@ -218,7 +244,7 @@ stats_origin(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata) int path_len = 0; const char* path = TSUrlPathGet(reqp,url_loc,&path_len); - TSDebug("istats","Path: %.*s",path_len,path); + TSDebug(PLUGIN_NAME, "Path: %.*s", path_len,path); if (! (path_len != 0 && path_len == url_path_len && !memcmp(path,url_path,url_path_len)) ) { goto notforme; @@ -227,7 +253,7 @@ stats_origin(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata) TSSkipRemappingSet(txnp,1); //not strictly necessary, but speed is everything these days /* This is us -- register our intercept */ - TSDebug("istats", "Intercepting request"); + TSDebug(PLUGIN_NAME, "Intercepting request"); icontp = TSContCreate(stats_dostuff, TSMutexCreate()); my_state = (stats_state *) TSmalloc(sizeof(*my_state)); @@ -239,9 +265,6 @@ stats_origin(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata) notforme: cleanup: -#if (TS_VERSION_NUMBER < 2001005) - if(path) TSHandleStringRelease(reqp, url_loc, path); -#endif if(url_loc) TSHandleMLocRelease(reqp, hdr_loc, url_loc); if(hdr_loc) TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc); @@ -254,12 +277,40 @@ TSPluginInit(int argc, const char *argv[]) { TSPluginRegistrationInfo info; - info.plugin_name = "stats"; + static const char usage[] = PLUGIN_NAME ".so [--integer-counters] [PATH]"; + static const struct option longopts[] = { + { (char *)("integer-counters"), required_argument, NULL, 'i' }, + { (char *)("wrap-counters"), required_argument, NULL, 'w' }, + { NULL, 0, NULL, 0 } + }; + + info.plugin_name = PLUGIN_NAME; info.vendor_name = "Apache Software Foundation"; info.support_email = "[email protected]"; - if (TSPluginRegister(TS_SDK_VERSION_2_0, &info) != TS_SUCCESS) - TSError("Plugin registration failed. \n"); + if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) { + TSError("[%s] registration failed", PLUGIN_NAME); + } + + optind = 0; + for (;;) { + switch (getopt_long(argc, (char * const *)argv, "i", longopts, NULL)) { + case 'i': + integer_counters = true; + break; + case 'i': + wrap_counters = true; + break; + case -1: + goto init; + default: + TSError("[%s] usage: %s", PLUGIN_NAME, usage); + } + } + +init: + argc -= optind; + argv += optind; if (argc > 1) { url_path = TSstrdup(argv[1] + ('/' == argv[1][0] ? 1 : 0)); /* Skip leading / */ @@ -269,5 +320,5 @@ TSPluginInit(int argc, const char *argv[]) /* Create a continuation with a mutex as there is a shared global structure containing the headers to add */ TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, TSContCreate(stats_origin, NULL)); - TSDebug("istats", "stats module registered"); + TSDebug(PLUGIN_NAME, "stats module registered"); }
