This is an automated email from the ASF dual-hosted git repository.

rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 0b4b024  Add csv as an output format option for astats via header 
(#4713)
0b4b024 is described below

commit 0b4b024f29ce3c92a0b56b42193e30336a1c3be4
Author: Evan Zelkowitz <[email protected]>
AuthorDate: Wed May 27 07:26:39 2020 -0700

    Add csv as an output format option for astats via header (#4713)
    
    This adds the ability to output CSV formatted stats via the `Accept` 
header.  If set to `text/csv` then the output will be in csv format.  Any other 
values will output in the old `text/json` format.  The `Content-Type` field has 
also been fixed since previously it was reported as `application/json`
---
 .../plugins/astats_over_http/astats_over_http.c    | 179 ++++++++++++++++++---
 1 file changed, 156 insertions(+), 23 deletions(-)

diff --git a/traffic_server/plugins/astats_over_http/astats_over_http.c 
b/traffic_server/plugins/astats_over_http/astats_over_http.c
index e39bcc8..8efd242 100644
--- a/traffic_server/plugins/astats_over_http/astats_over_http.c
+++ b/traffic_server/plugins/astats_over_http/astats_over_http.c
@@ -40,6 +40,11 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+typedef enum {
+       JSON_OUTPUT,
+       CSV_OUTPUT
+} output_format;
+
 typedef struct {
        unsigned int recordTypes;
        char *stats_path;
@@ -82,6 +87,7 @@ typedef struct stats_state_t {
        char *interfaceName;
        char *query;
        unsigned int recordTypes;
+       output_format output;
 } stats_state;
 
 int configReloadRequests = 0;
@@ -193,14 +199,25 @@ stats_add_data_to_resp_buffer(const char *s, stats_state 
*my_state) {
        return s_len;
 }
 
-static const char RESP_HEADER[] = "HTTP/1.0 200 Ok\r\nContent-Type: 
text/javascript\r\nCache-Control: no-cache\r\n\r\n";
+static const char RESP_HEADER_JSON[] = "HTTP/1.0 200 Ok\r\nContent-Type: 
text/json\r\nCache-Control: no-cache\r\n\r\n";
+static const char RESP_HEADER_CSV[] = "HTTP/1.0 200 Ok\r\nContent-Type: 
text/csv\r\nCache-Control: no-cache\r\n\r\n";
 
 static void
 stats_process_read(TSCont contp, TSEvent event, stats_state *my_state) {
        TSDebug(PLUGIN_TAG, "stats_process_read(%d)", event);
 
        if (event == TS_EVENT_VCONN_READ_READY) {
-               my_state->output_bytes = 
stats_add_data_to_resp_buffer(RESP_HEADER, my_state);
+               switch (my_state->output) {
+                       case JSON_OUTPUT:
+                               my_state->output_bytes = 
stats_add_data_to_resp_buffer(RESP_HEADER_JSON, my_state);
+                               break;
+                       case CSV_OUTPUT:
+                               my_state->output_bytes = 
stats_add_data_to_resp_buffer(RESP_HEADER_CSV, my_state);
+                               break;
+                       default:
+                               TSError("stats_process_read: Unknown output 
format\n");
+                               break;
+               }
                TSVConnShutdown(my_state->net_vc, 1, 0);
                my_state->write_vio = TSVConnWrite(my_state->net_vc, contp, 
my_state->resp_reader, INT64_MAX);
        }
@@ -218,13 +235,20 @@ stats_process_read(TSCont contp, TSEvent event, 
stats_state *my_state) {
 }
 
 #define APPEND(a) my_state->output_bytes += stats_add_data_to_resp_buffer(a, 
my_state)
-#define APPEND_STAT(a, fmt, v) do { \
+#define APPEND_STAT_JSON(a, fmt, v) do { \
                char b[3048]; \
                int nbytes = snprintf(b, sizeof(b), "   \"%s\": " fmt ",\n", a, 
v); \
                if (0 < nbytes && nbytes < (int)sizeof(b)) \
                        APPEND(b); \
 } while(0)
 
+#define APPEND_STAT_CSV(a, fmt, v) do { \
+               char b[3048]; \
+               int nbytes = snprintf(b, sizeof(b), "%s," fmt "\n", a, v); \
+               if (0 < nbytes && nbytes < (int)sizeof(b)) \
+                       APPEND(b); \
+} while(0)
+
 static void
 json_out_stat(TSRecordType rec_type, void *edata, int registered, const char 
*name, TSRecordDataType data_type, TSRecordData *datum) {
        stats_state *my_state = edata;
@@ -245,13 +269,46 @@ json_out_stat(TSRecordType rec_type, void *edata, int 
registered, const char *na
 
        switch(data_type) {
        case TS_RECORDDATATYPE_COUNTER:
-               APPEND_STAT(name, "%" PRIu64, datum->rec_counter); break;
+               APPEND_STAT_JSON(name, "%" PRIu64, datum->rec_counter); break;
+       case TS_RECORDDATATYPE_INT:
+               APPEND_STAT_JSON(name, "%" PRIu64, datum->rec_int); break;
+       case TS_RECORDDATATYPE_FLOAT:
+               APPEND_STAT_JSON(name, "%f", datum->rec_float); break;
+       case TS_RECORDDATATYPE_STRING:
+               APPEND_STAT_JSON(name, "\"%s\"", datum->rec_string); break;
+       default:
+               TSDebug(PLUGIN_TAG, "unkown type for %s: %d", name, data_type);
+               break;
+       }
+}
+
+static void
+csv_out_stat(TSRecordType rec_type, void *edata, int registered, const char 
*name, TSRecordDataType data_type, TSRecordData *datum) {
+       stats_state *my_state = edata;
+       int found = 0;
+       int i;
+
+       if (my_state->globals_cnt) {
+               for (i = 0; i < my_state->globals_cnt; i++) {
+                       if (strstr(name, my_state->globals[i])) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found)
+                       return; // skip
+       }
+
+       switch(data_type) {
+       case TS_RECORDDATATYPE_COUNTER:
+               APPEND_STAT_CSV(name, "%" PRIu64, datum->rec_counter); break;
        case TS_RECORDDATATYPE_INT:
-               APPEND_STAT(name, "%" PRIu64, datum->rec_int); break;
+               APPEND_STAT_CSV(name, "%" PRIu64, datum->rec_int); break;
        case TS_RECORDDATATYPE_FLOAT:
-               APPEND_STAT(name, "%f", datum->rec_float); break;
+               APPEND_STAT_CSV(name, "%f", datum->rec_float); break;
        case TS_RECORDDATATYPE_STRING:
-               APPEND_STAT(name, "\"%s\"", datum->rec_string); break;
+               APPEND_STAT_CSV(name, "%s", datum->rec_string); break;
        default:
                TSDebug(PLUGIN_TAG, "unkown type for %s: %d", name, data_type);
                break;
@@ -297,18 +354,18 @@ static int getSpeed(char *inf, char *buffer, int 
bufferSize) {
        return speed;
 }
 
-static void appendSystemState(stats_state *my_state) {
+static void appendSystemStateJson(stats_state *my_state) {
        char *interface = my_state->interfaceName;
        char buffer[16384];
        char *str;
        char *end;
        int speed = 0;
 
-       APPEND_STAT("inf.name", "\"%s\"", interface);
+       APPEND_STAT_JSON("inf.name", "\"%s\"", interface);
 
        speed = getSpeed(interface, buffer, sizeof(buffer));
 
-       APPEND_STAT("inf.speed", "%d", speed);
+       APPEND_STAT_JSON("inf.speed", "%d", speed);
 
        str = getFile("/proc/net/dev", buffer, sizeof(buffer));
        if (str && interface) {
@@ -317,7 +374,7 @@ static void appendSystemState(stats_state *my_state) {
                        end = strstr(str, "\n");
                        if (end)
                                *end = 0;
-                       APPEND_STAT("proc.net.dev", "\"%s\"", str);
+                       APPEND_STAT_JSON("proc.net.dev", "\"%s\"", str);
                }
        }
 
@@ -326,7 +383,40 @@ static void appendSystemState(stats_state *my_state) {
                end = strstr(str, "\n");
                if (end)
                        *end = 0;
-               APPEND_STAT("proc.loadavg", "\"%s\"", str);
+               APPEND_STAT_JSON("proc.loadavg", "\"%s\"", str);
+       }
+}
+
+static void appendSystemStateCsv(stats_state *my_state) {
+       char *interface = my_state->interfaceName;
+       char buffer[16384];
+       char *str;
+       char *end;
+       int speed = 0;
+
+       APPEND_STAT_CSV("inf.name", "%s", interface);
+
+       speed = getSpeed(interface, buffer, sizeof(buffer));
+
+       APPEND_STAT_CSV("inf.speed", "%d", speed);
+
+       str = getFile("/proc/net/dev", buffer, sizeof(buffer));
+       if (str && interface) {
+               str = strstr(str, interface);
+               if (str) {
+                       end = strstr(str, "\n");
+                       if (end)
+                               *end = 0;
+                       APPEND_STAT_CSV("proc.net.dev", "%s", str);
+               }
+       }
+
+       str = getFile("/proc/loadavg", buffer, sizeof(buffer));
+       if (str) {
+               end = strstr(str, "\n");
+               if (end)
+                       *end = 0;
+               APPEND_STAT_CSV("proc.loadavg", "%s", str);
        }
 }
 
@@ -343,12 +433,12 @@ static void json_out_stats(stats_state *my_state) {
 
        if (my_state->recordTypes & SYSTEM_RECORD_TYPE) {
                APPEND(",\n \"system\": {\n");
-               appendSystemState(my_state);
-               APPEND_STAT("configReloadRequests", "%d", configReloadRequests);
-               APPEND_STAT("lastReloadRequest", "%" PRIu64, lastReloadRequest);
-               APPEND_STAT("configReloads", "%d", configReloads);
-               APPEND_STAT("lastReload", "%" PRIu64, lastReload);
-               APPEND_STAT("astatsLoad", "%" PRIu64, astatsLoad);
+               appendSystemStateJson(my_state);
+               APPEND_STAT_JSON("configReloadRequests", "%d", 
configReloadRequests);
+               APPEND_STAT_JSON("lastReloadRequest", "%" PRIu64, 
lastReloadRequest);
+               APPEND_STAT_JSON("configReloads", "%d", configReloads);
+               APPEND_STAT_JSON("lastReload", "%" PRIu64, lastReload);
+               APPEND_STAT_JSON("astatsLoad", "%" PRIu64, astatsLoad);
                APPEND("\"something\": \"here\"");
                APPEND("\n  }");
        }
@@ -356,12 +446,41 @@ static void json_out_stats(stats_state *my_state) {
        APPEND("\n}\n");
 }
 
+static void csv_out_stats(stats_state *my_state) {
+       const char *version;
+       TSDebug(PLUGIN_TAG, "recordTypes: '0x%x'", my_state->recordTypes);
+    TSRecordDump(my_state->recordTypes, csv_out_stat, my_state);
+       version = TSTrafficServerVersionGet();
+       //APPEND("version","%s",version);
+       APPEND_STAT_CSV("version","%s", version);
+       if (my_state->recordTypes & SYSTEM_RECORD_TYPE) {
+               //APPEND(",\n \"system\": {\n");
+               appendSystemStateCsv(my_state);
+               APPEND_STAT_CSV("configReloadRequests", "%d", 
configReloadRequests);
+               APPEND_STAT_CSV("lastReloadRequest", "%" PRIu64, 
lastReloadRequest);
+               APPEND_STAT_CSV("configReloads", "%d", configReloads);
+               APPEND_STAT_CSV("lastReload", "%" PRIu64, lastReload);
+               APPEND_STAT_CSV("astatsLoad", "%" PRIu64, astatsLoad);
+               APPEND("something,here\n");
+       }
+}
+
 static void 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(PLUGIN_TAG, "plugin adding response body");
                        my_state->body_written = 1;
-                       json_out_stats(my_state);
+                       switch (my_state->output) {
+                               case JSON_OUTPUT:
+                                       json_out_stats(my_state);
+                                       break;
+                               case CSV_OUTPUT:
+                                       csv_out_stats(my_state);
+                                       break;
+                               default:
+                                       TSError("stats_process_write: Unknown 
output type\n");
+                                       break;
+                       }
                        TSVIONBytesSet(my_state->write_vio, 
my_state->output_bytes);
                }
                TSVIOReenable(my_state->write_vio);
@@ -398,7 +517,7 @@ static int astats_origin(TSCont cont, TSEvent event, void 
*edata) {
        config_t* config;
        TSHttpTxn txnp = (TSHttpTxn) edata;
        TSMBuffer reqp;
-       TSMLoc hdr_loc = NULL, url_loc = NULL;
+       TSMLoc hdr_loc = NULL, url_loc = NULL, accept_field = NULL;
        TSEvent reenable = TS_EVENT_HTTP_CONTINUE;
        config = get_config(cont);
 
@@ -416,7 +535,6 @@ static int astats_origin(TSCont cont, TSEvent event, void 
*edata) {
        TSDebug(PLUGIN_TAG,"Path: %.*s",path_len,path);
 
        if (!(path_len == config->stats_path_len && !memcmp(path, 
config->stats_path, config->stats_path_len))) {
-//             TSDebug(PLUGIN_TAG, "not right path: %.*s",path_len,path);
                goto notforme;
        }
 
@@ -425,7 +543,6 @@ static int astats_origin(TSCont cont, TSEvent event, void 
*edata) {
                TSDebug(PLUGIN_TAG, "not right ip");
                goto notforme;
        }
-//     TSDebug(PLUGIN_TAG,"Path...: %.*s",path_len,path);
 
        int query_len;
        char *query = (char*)TSUrlHttpQueryGet(reqp,url_loc,&query_len);
@@ -440,6 +557,21 @@ static int astats_origin(TSCont cont, TSEvent event, void 
*edata) {
        my_state = (stats_state *) TSmalloc(sizeof(*my_state));
        memset(my_state, 0, sizeof(*my_state));
 
+       accept_field = TSMimeHdrFieldFind(reqp, hdr_loc, TS_MIME_FIELD_ACCEPT, 
TS_MIME_LEN_ACCEPT);
+       my_state->output = JSON_OUTPUT; // default to json output
+       // accept header exists, use it to determine response type
+       if (accept_field != TS_NULL_MLOC) {
+               int len = -1;
+               const char* str = TSMimeHdrFieldValueStringGet(reqp, hdr_loc, 
accept_field, -1, &len);
+
+               // Parse the Accept header, default to JSON output unless its 
another supported format
+               if (!strncasecmp(str, "text/csv", len)) {
+                       my_state->output = CSV_OUTPUT;
+               } else {
+                       my_state->output = JSON_OUTPUT;
+               }
+       }
+
        my_state->recordTypes = config->recordTypes;
        if (query_len) {
                my_state->query = nstrl(query, query_len);
@@ -463,6 +595,8 @@ static int astats_origin(TSCont cont, TSEvent event, void 
*edata) {
                TSHandleMLocRelease(reqp, hdr_loc, url_loc);
        if (hdr_loc)
                TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc);
+       if (accept_field)
+               TSHandleMLocRelease(reqp, TS_NULL_MLOC, accept_field);
 
        TSHttpTxnReenable(txnp, reenable);
 
@@ -677,7 +811,6 @@ static config_t* new_config(TSFile fh) {
         config->allowIps6 = 0;
         config->ip6Count = 0;
         config->recordTypes = DEFAULT_RECORD_TYPES;
-       //      TSmalloc(6);
 
        if(!fh) {
                config->stats_path = nstr("_astats");

Reply via email to