You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by je...@apache.org on 2010/11/04 22:54:21 UTC
svn commit: r1031277 - in /trafficserver/plugins/stats: ./ README stats.c
Author: jesus
Date: Thu Nov 4 21:54:21 2010
New Revision: 1031277
URL: http://svn.apache.org/viewvc?rev=1031277&view=rev
Log:
JSON stats over HTTP
Added:
trafficserver/plugins/stats/
trafficserver/plugins/stats/README
trafficserver/plugins/stats/stats.c
Added: trafficserver/plugins/stats/README
URL: http://svn.apache.org/viewvc/trafficserver/plugins/stats/README?rev=1031277&view=auto
==============================================================================
--- trafficserver/plugins/stats/README (added)
+++ trafficserver/plugins/stats/README Thu Nov 4 21:54:21 2010
@@ -0,0 +1,16 @@
+Compile:
+ tsxs -c stats.c -o stats.so
+Install:
+ sudo tsxs -o stats.so -i
+
+Add to the plugins.conf:
+
+ stats.so
+
+Add to the remap.conf:
+
+ regex_map http://[0-9.]+/_stats http://[stats]/_stats
+
+
+
+start traffic server and visit http://IP:port/_stats
Added: trafficserver/plugins/stats/stats.c
URL: http://svn.apache.org/viewvc/trafficserver/plugins/stats/stats.c?rev=1031277&view=auto
==============================================================================
--- trafficserver/plugins/stats/stats.c (added)
+++ trafficserver/plugins/stats/stats.c Thu Nov 4 21:54:21 2010
@@ -0,0 +1,340 @@
+/** @file
+
+ A brief file description
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+/* stats.c: expose traffic server stats over http
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <ts/ts.h>
+#include <string.h>
+
+typedef struct stats_state_t
+{
+ INKVConn net_vc;
+ INKVIO read_vio;
+ INKVIO write_vio;
+
+ INKIOBuffer req_buffer;
+ INKIOBufferReader req_reader;
+
+ INKIOBuffer resp_buffer;
+ INKIOBufferReader resp_reader;
+
+ INKHttpTxn http_txnp;
+
+ int output_bytes;
+ int body_written;
+} stats_state;
+
+static void
+stats_cleanup(INKCont contp, stats_state * my_state)
+{
+ if (my_state->req_buffer) {
+ if (INKIOBufferDestroy(my_state->req_buffer) == INK_ERROR) {
+ INKDebug("istats", "INKIOBufferDestroy");
+ }
+ my_state->req_buffer = NULL;
+ }
+
+ if (my_state->resp_buffer) {
+ if (INKIOBufferDestroy(my_state->resp_buffer) == INK_ERROR) {
+ INKDebug("istats", "INKIOBufferDestroy");
+ }
+ my_state->resp_buffer = NULL;
+ }
+ if (INKVConnClose(my_state->net_vc) == INK_ERROR) {
+ INKDebug("istats", "INKVConnClose");
+ }
+ INKfree(my_state);
+ INKContDestroy(contp);
+}
+
+static void
+stats_process_accept(INKCont contp, stats_state * my_state)
+{
+
+ my_state->req_buffer = INKIOBufferCreate();
+ if (my_state->req_buffer == INK_ERROR_PTR) {
+ INKDebug("istats", "INKIOBufferCreate");
+ return;
+ }
+ my_state->req_reader = INKIOBufferReaderAlloc(my_state->req_buffer);
+ if (my_state->req_reader == INK_ERROR_PTR) {
+ INKDebug("istats", "INKIOBufferReaderAlloc");
+ return;
+ }
+ my_state->resp_buffer = INKIOBufferCreate();
+ if (my_state->resp_buffer == INK_ERROR_PTR) {
+ INKDebug("istats", "INKIOBufferCreate");
+ return;
+ }
+ my_state->resp_reader = INKIOBufferReaderAlloc(my_state->resp_buffer);
+ if (my_state->resp_reader == INK_ERROR_PTR) {
+ INKDebug("istats", "INKIOBufferReaderAlloc");
+ return;
+ }
+
+ my_state->read_vio = INKVConnRead(my_state->net_vc, contp, my_state->req_buffer, INT_MAX);
+ if (my_state->read_vio == INK_ERROR_PTR) {
+ INKDebug("istats", "INKVConnRead");
+ return;
+ }
+}
+
+static int
+stats_add_data_to_resp_buffer(const char *s, stats_state * my_state)
+{
+ int s_len = strlen(s);
+ char *buf = (char *) INKmalloc(s_len);
+
+ memcpy(buf, s, s_len);
+ INKIOBufferWrite(my_state->resp_buffer, buf, s_len);
+
+ INKfree(buf);
+ buf = NULL;
+ return s_len;
+}
+
+static int
+stats_add_resp_header(stats_state * my_state)
+{
+ char resp[] = "HTTP/1.0 200 Ok\r\n"
+ "Content-Type: text/javascript\r\nCache-Control: no-cache\r\n\r\n";
+ return stats_add_data_to_resp_buffer(resp, my_state);
+}
+
+static void
+stats_process_read(INKCont contp, INKEvent event, stats_state * my_state)
+{
+ INKDebug("istats", "stats_process_read(%d)", event);
+ if (event == INK_EVENT_VCONN_READ_READY) {
+ my_state->output_bytes = stats_add_resp_header(my_state);
+ if (INKVConnShutdown(my_state->net_vc, 1, 0) == INK_ERROR) {
+ INKDebug("istats", "INKVConnShutdown");
+ return;
+ }
+ my_state->write_vio = INKVConnWrite(my_state->net_vc, contp, my_state->resp_reader, INT_MAX);
+ if (my_state->write_vio == INK_ERROR_PTR) {
+ INKDebug("istats", "INKVConnWrite");
+ return;
+ }
+ } else if (event == INK_EVENT_ERROR) {
+ INKError("stats_process_read: Received INK_EVENT_ERROR\n");
+ } else if (event == INK_EVENT_VCONN_EOS) {
+ /* client may end the connection, simply return */
+ return;
+ } else if (event == INK_EVENT_NET_ACCEPT_FAILED) {
+ INKError("stats_process_read: Received INK_EVENT_NET_ACCEPT_FAILED\n");
+ } else {
+ printf("Unexpected Event %d\n", event);
+ INKReleaseAssert(!"Unexpected Event");
+ }
+}
+
+#define APPEND(a) my_state->output_bytes += stats_add_data_to_resp_buffer(a, my_state)
+#define APPEND_STAT(a, fmt, v) do { \
+ char b[128]; \
+ snprintf(b, sizeof(b), "\"%s\": \"" fmt "\",\n", a, v); \
+ APPEND(b); \
+} while(0)
+
+static void
+json_out_stat(INKRecordType rec_type, void *edata, int registered,
+ const char *name, INKRecordDataType data_type,
+ INKRecordData *datum) {
+ stats_state *my_state = edata;
+
+ switch(data_type) {
+ case INK_RECORDDATATYPE_COUNTER:
+ APPEND_STAT(name, "%llu", datum->rec_counter); break;
+ case INK_RECORDDATATYPE_INT:
+ APPEND_STAT(name, "%llu", datum->rec_int); break;
+ case INK_RECORDDATATYPE_FLOAT:
+ APPEND_STAT(name, "%f", datum->rec_float); break;
+ case INK_RECORDDATATYPE_STRING:
+ APPEND_STAT(name, "%s", datum->rec_string); break;
+ default:
+ INKDebug("istats", "unkown type for %s: %d", name, data_type);
+ break;
+ }
+}
+static void
+json_out_stats(stats_state * my_state)
+{
+ const char *version;
+ APPEND("{ \"global\": {\n");
+
+ INKRecordDump(INK_RECORDTYPE_PROCESS, json_out_stat, my_state);
+ version = INKTrafficServerVersionGet();
+ APPEND("\"server\": \"");
+ APPEND(version);
+ APPEND("\"\n");
+ APPEND(" }\n}\n");
+}
+
+static void
+stats_process_write(INKCont contp, INKEvent event, stats_state * my_state)
+{
+ if (event == INK_EVENT_VCONN_WRITE_READY) {
+ if (my_state->body_written == 0) {
+ INKDebug("istats", "plugin adding response body");
+ my_state->body_written = 1;
+ json_out_stats(my_state);
+ if (INKVIONBytesSet(my_state->write_vio, my_state->output_bytes) == INK_ERROR) {
+ INKDebug("istats", "INKVIONBytesSet");
+ return;
+ }
+ }
+ if (INKVIOReenable(my_state->write_vio) == INK_ERROR) {
+ INKDebug("istats", "INKVIOReenable");
+ return;
+ }
+ } else if (INK_EVENT_VCONN_WRITE_COMPLETE) {
+ stats_cleanup(contp, my_state);
+ } else if (event == INK_EVENT_ERROR) {
+ INKError("stats_process_write: Received INK_EVENT_ERROR\n");
+ } else {
+ INKReleaseAssert(!"Unexpected Event");
+ }
+}
+
+static int
+stats_dostuff(INKCont contp, INKEvent event, void *edata)
+{
+ stats_state *my_state = INKContDataGet(contp);
+ if (event == INK_EVENT_NET_ACCEPT) {
+ my_state->net_vc = (INKVConn) edata;
+ stats_process_accept(contp, my_state);
+ } else if (edata == my_state->read_vio) {
+ stats_process_read(contp, event, my_state);
+ } else if (edata == my_state->write_vio) {
+ stats_process_write(contp, event, my_state);
+ } else {
+ INKReleaseAssert(!"Unexpected Event");
+ }
+ return 0;
+}
+static int
+stats_origin(INKCont contp, INKEvent event, void *edata)
+{
+ INKCont icontp;
+ stats_state *my_state;
+ INKHttpTxn txnp = (INKHttpTxn) edata;
+ INKMBuffer reqp;
+ INKMLoc hdr_loc = NULL, field = NULL;
+ int host_header_len;
+ const char *host_header_ptr;
+
+ INKDebug("istats", "in the read stuff");
+
+ INKHttpTxnClientReqGet(txnp, &reqp, &hdr_loc);
+ field = INKMimeHdrFieldFind(reqp, hdr_loc,
+ INK_MIME_FIELD_HOST, INK_MIME_LEN_HOST);
+ if(field) {
+ INKMimeHdrFieldValueStringGet(reqp, hdr_loc, field, 0,
+ &host_header_ptr, &host_header_len);
+ if(host_header_len != 7 || memcmp(host_header_ptr, "[stats]", 7))
+ goto notforme;
+ }
+
+ /* This is us -- register our intercept */
+ INKDebug("istats", "Intercepting request");
+
+ icontp = INKContCreate(stats_dostuff, INKMutexCreate());
+ if (icontp == INK_ERROR_PTR) goto notforme;
+ my_state = (stats_state *) INKmalloc(sizeof(*my_state));
+ memset(my_state, 0, sizeof(*my_state));
+ if (INK_ERROR == INKContDataSet(icontp, my_state)) goto error;
+ if (INK_ERROR == INKHttpTxnIntercept(icontp, txnp)) goto error;
+ goto cleanup;
+
+ error:
+ INKError("[PluginInit] Error while intercepting stuff");
+ INKContDestroy(icontp);
+ INKHttpTxnReenable(txnp, INK_EVENT_HTTP_ERROR);
+ goto cleanup;
+
+ notforme:
+
+ cleanup:
+ INKHttpTxnReenable(txnp, INK_EVENT_HTTP_CONTINUE);
+ if(hdr_loc) INKHandleMLocRelease(reqp, INK_NULL_MLOC, hdr_loc);
+ if(field) INKHandleMLocRelease(reqp, INK_NULL_MLOC, field);
+ return 0;
+}
+
+int
+check_ts_version()
+{
+
+ const char *ts_version = INKTrafficServerVersionGet();
+ int result = 0;
+
+ if (ts_version) {
+ int major_ts_version = 0;
+ int minor_ts_version = 0;
+ int patch_ts_version = 0;
+
+ if (sscanf(ts_version, "%d.%d.%d", &major_ts_version, &minor_ts_version, &patch_ts_version) != 3) {
+ return 0;
+ }
+
+ /* Need at least TS 2.0 */
+ if (major_ts_version >= 2) {
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+void
+INKPluginInit(int argc, const char *argv[])
+{
+ INKPluginRegistrationInfo info;
+
+ info.plugin_name = "stats";
+ info.vendor_name = "Apache Software Foundation";
+ info.support_email = "jesus@omniti.com";
+
+ if (!INKPluginRegister(INK_SDK_VERSION_2_0, &info))
+ INKError("Plugin registration failed. \n");
+
+ if (!check_ts_version()) {
+ INKError("Plugin requires Traffic Server 2.0 or later\n");
+ return;
+ }
+
+ /* Create a continuation with a mutex as there is a shared global structure
+ containing the headers to add */
+ if (INK_ERROR == INKHttpHookAdd(INK_HTTP_READ_REQUEST_HDR_HOOK,
+ INKContCreate(stats_origin, NULL))) {
+ INKError("[PluginInit] Error while registering to hook");
+ return;
+ }
+
+ INKDebug("istats", "stats module registered");
+}