Repository: trafficserver Updated Branches: refs/heads/master ff45432a8 -> bde10b778
TS-2974: new epic metrics plugin Add a new plugin to publich metrics into the Epic monitoring system, https://code.google.com/p/epicnms/. Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/bde10b77 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/bde10b77 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/bde10b77 Branch: refs/heads/master Commit: bde10b7785bddd43a56ab1ef0a5f33463ed3e712 Parents: ff45432 Author: James Peach <[email protected]> Authored: Mon Jul 28 09:34:36 2014 -0700 Committer: James Peach <[email protected]> Committed: Wed Jul 30 10:48:27 2014 -0700 ---------------------------------------------------------------------- CHANGES | 2 + configure.ac | 1 + doc/reference/plugins/epic.en.rst | 46 +++ doc/reference/plugins/index.en.rst | 1 + plugins/experimental/Makefile.am | 1 + plugins/experimental/epic/Makefile.am | 22 ++ plugins/experimental/epic/epic.cc | 517 +++++++++++++++++++++++++++++ 7 files changed, 590 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bde10b77/CHANGES ---------------------------------------------------------------------- diff --git a/CHANGES b/CHANGES index 405d2a7..69df453 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -*- coding: utf-8 -*- Changes with Apache Traffic Server 5.1.0 + *) [TS-2974] Add a new metrics plugin to support the Epic monitoring system. + *) [TS-2973] Add milestones support to the xdebug plugin. *) [TS-2957] Add new sslheaders plugin. http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bde10b77/configure.ac ---------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index 124fafd..b1b4cef 100644 --- a/configure.ac +++ b/configure.ac @@ -1938,6 +1938,7 @@ AS_IF([test "x$enable_experimental_plugins" = xyes], [ plugins/experimental/channel_stats/Makefile plugins/experimental/collapsed_connection/Makefile plugins/experimental/custom_redirect/Makefile + plugins/experimental/epic/Makefile plugins/experimental/escalate/Makefile plugins/experimental/esi/Makefile plugins/experimental/geoip_acl/Makefile http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bde10b77/doc/reference/plugins/epic.en.rst ---------------------------------------------------------------------- diff --git a/doc/reference/plugins/epic.en.rst b/doc/reference/plugins/epic.en.rst new file mode 100644 index 0000000..b0cafa4 --- /dev/null +++ b/doc/reference/plugins/epic.en.rst @@ -0,0 +1,46 @@ +.. _epic-plugin: + +Epic Plugin +*********** + +.. 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. + +The `Epic` plugin emits Traffic Server metrics in a format that is +consumed tby the `Epic Network Monitoring System +<https://code.google.com/p/epicnms/>`. It is a global plugin that +is installed by adding it to the :file:`plugin.config` file. + +Plugin Options +-------------- + +--directory=DIR + Specify the directory the plugin will write sample files to. The + default is ``/usr/local/epic/cache/eapi``. + +--period=SECS + Specify the sample period in seconds. The default is to write samples every + 30 seconds. + +Caveats +------- + +The Traffic Server metrics system does not store the semantics of +metrics, so it is not possible to programmatically determine whether +a metrics can be rate converted. The plugin contains a static list +of metrics that should not be rate converted (gauges in Epic +terminilogy). http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bde10b77/doc/reference/plugins/index.en.rst ---------------------------------------------------------------------- diff --git a/doc/reference/plugins/index.en.rst b/doc/reference/plugins/index.en.rst index d056326..3687025 100644 --- a/doc/reference/plugins/index.en.rst +++ b/doc/reference/plugins/index.en.rst @@ -68,6 +68,7 @@ directory of the Apache Traffic Server source tree. Exmperimental plugins can be balancer.en buffer_upload.en combo_handler.en + epic.en esi.en geoip_acl.en hipes.en http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bde10b77/plugins/experimental/Makefile.am ---------------------------------------------------------------------- diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am index 947273b..57fec8c 100644 --- a/plugins/experimental/Makefile.am +++ b/plugins/experimental/Makefile.am @@ -22,6 +22,7 @@ SUBDIRS = \ channel_stats \ collapsed_connection \ custom_redirect \ + epic \ escalate \ esi \ geoip_acl \ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bde10b77/plugins/experimental/epic/Makefile.am ---------------------------------------------------------------------- diff --git a/plugins/experimental/epic/Makefile.am b/plugins/experimental/epic/Makefile.am new file mode 100644 index 0000000..9fd33ed --- /dev/null +++ b/plugins/experimental/epic/Makefile.am @@ -0,0 +1,22 @@ +# 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. + +include $(top_srcdir)/build/plugins.mk + +pkglib_LTLIBRARIES = epic.la +epic_la_SOURCES = epic.cc +epic_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS) + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bde10b77/plugins/experimental/epic/epic.cc ---------------------------------------------------------------------- diff --git a/plugins/experimental/epic/epic.cc b/plugins/experimental/epic/epic.cc new file mode 100644 index 0000000..f6e7020 --- /dev/null +++ b/plugins/experimental/epic/epic.cc @@ -0,0 +1,517 @@ +/* + * 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. + */ + +#define __STDC_LIMIT_MACROS 1 +#define __STDC_FORMAT_MACROS 1 + +#include <ts/ts.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <time.h> +#include <getopt.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <inttypes.h> +#include <set> +#include <string> + +#undef DEBUG + +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define debug_tag(tag, fmt, ...) do { \ + if (unlikely(TSIsDebugTagSet(tag))) { \ + TSDebug(tag, fmt, ##__VA_ARGS__); \ + } \ +} while(0) + +#define debug(fmt, ...) \ + debug_tag("epic", "%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__) + +#if defined(DEBUG) +#define error(fmt, ...) debug(fmt, ##__VA_ARGS__) +#else +#define error(fmt, ...) \ + TSError("epic:%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__) +#endif + +template < typename T, unsigned N > static unsigned +countof(const T(&)[N]) +{ + return N; +} + +static TSHRTime epic_period = 30ll; /* 30sec default */ +static const char *epic_prefix = "/usr/local/epic/cache/eapi"; + +#define GAUGE_METRIC_NAMES \ + "proxy.node.bandwidth_hit_ratio_avg_10s", \ + "proxy.node.bandwidth_hit_ratio_avg_10s_int_pct", \ + "proxy.node.bandwidth_hit_ratio_int_pct", \ + "proxy.node.cache.bytes_free", \ + "proxy.node.cache.bytes_free_mb", \ + "proxy.node.cache.bytes_total", \ + "proxy.node.cache.bytes_total_mb", \ + "proxy.node.cache.bytes_used", \ + "proxy.node.cache.bytes_used_mb", \ + "proxy.node.cache.percent_free", \ + "proxy.node.cache.percent_free_int_pct", \ + "proxy.node.cache_hit_mem_ratio_avg_10s", \ + "proxy.node.cache_hit_mem_ratio_avg_10s_int_pct", \ + "proxy.node.cache_hit_mem_ratio_int_pct", \ + "proxy.node.cache_hit_ratio_avg_10s", \ + "proxy.node.cache_hit_ratio_avg_10s_int_pct", \ + "proxy.node.cache_hit_ratio_int_pct", \ + "proxy.node.cache_total_hits", \ + "proxy.node.cache_total_hits_avg_10s", \ + "proxy.node.cache_total_hits_mem", \ + "proxy.node.cache_total_hits_mem_avg_10s", \ + "proxy.node.cache_total_misses", \ + "proxy.node.cache_total_misses_avg_10s", \ + "proxy.node.client_throughput_out", \ + "proxy.node.client_throughput_out_kbit", \ + "proxy.node.cluster.nodes", \ + "proxy.node.current_cache_connections", \ + "proxy.node.current_client_connections", \ + "proxy.node.current_server_connections", \ + "proxy.node.dns.lookup_avg_time_ms", \ + "proxy.node.dns.lookups_per_second", \ + "proxy.node.dns.total_dns_lookups", \ + "proxy.node.hostdb.hit_ratio_avg_10s", \ + "proxy.node.hostdb.hit_ratio_int_pct", \ + "proxy.node.hostdb.total_hits", \ + "proxy.node.hostdb.total_hits_avg_10s", \ + "proxy.node.hostdb.total_lookups", \ + "proxy.node.hostdb.total_lookups_avg_10s", \ + "proxy.node.http.cache_current_connections_count", \ + "proxy.node.http.cache_hit_fresh_avg_10s", \ + "proxy.node.http.cache_hit_ims_avg_10s", \ + "proxy.node.http.cache_hit_mem_fresh_avg_10s", \ + "proxy.node.http.cache_hit_revalidated_avg_10s", \ + "proxy.node.http.cache_hit_stale_served_avg_10s", \ + "proxy.node.http.cache_miss_changed_avg_10s", \ + "proxy.node.http.cache_miss_client_no_cache_avg_10s", \ + "proxy.node.http.cache_miss_cold_avg_10s", \ + "proxy.node.http.cache_miss_ims_avg_10s", \ + "proxy.node.http.cache_miss_not_cacheable_avg_10s", \ + "proxy.node.http.cache_read_error_avg_10s", \ + "proxy.node.http.current_parent_proxy_connections", \ + "proxy.node.http.origin_server_current_connections_count", \ + "proxy.node.http.origin_server_total_request_bytes", \ + "proxy.node.http.origin_server_total_response_bytes", \ + "proxy.node.http.origin_server_total_transactions_count", \ + "proxy.node.http.parent_proxy_total_request_bytes", \ + "proxy.node.http.parent_proxy_total_response_bytes", \ + "proxy.node.http.transaction_counts_avg_10s.errors.aborts", \ + "proxy.node.http.transaction_counts_avg_10s.errors.connect_failed", \ + "proxy.node.http.transaction_counts_avg_10s.errors.early_hangups", \ + "proxy.node.http.transaction_counts_avg_10s.errors.empty_hangups", \ + "proxy.node.http.transaction_counts_avg_10s.errors.other", \ + "proxy.node.http.transaction_counts_avg_10s.errors.possible_aborts", \ + "proxy.node.http.transaction_counts_avg_10s.errors.pre_accept_hangups", \ + "proxy.node.http.transaction_counts_avg_10s.hit_fresh", \ + "proxy.node.http.transaction_counts_avg_10s.hit_revalidated", \ + "proxy.node.http.transaction_counts_avg_10s.miss_changed", \ + "proxy.node.http.transaction_counts_avg_10s.miss_client_no_cache", \ + "proxy.node.http.transaction_counts_avg_10s.miss_cold", \ + "proxy.node.http.transaction_counts_avg_10s.miss_not_cacheable", \ + "proxy.node.http.transaction_counts_avg_10s.other.unclassified", \ + "proxy.node.http.transaction_frac_avg_10s.errors.aborts", \ + "proxy.node.http.transaction_frac_avg_10s.errors.aborts_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.errors.connect_failed", \ + "proxy.node.http.transaction_frac_avg_10s.errors.connect_failed_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.errors.early_hangups", \ + "proxy.node.http.transaction_frac_avg_10s.errors.early_hangups_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.errors.empty_hangups", \ + "proxy.node.http.transaction_frac_avg_10s.errors.empty_hangups_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.errors.other", \ + "proxy.node.http.transaction_frac_avg_10s.errors.other_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.errors.possible_aborts", \ + "proxy.node.http.transaction_frac_avg_10s.errors.possible_aborts_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.errors.pre_accept_hangups", \ + "proxy.node.http.transaction_frac_avg_10s.errors.pre_accept_hangups_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.hit_fresh", \ + "proxy.node.http.transaction_frac_avg_10s.hit_fresh_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.hit_revalidated", \ + "proxy.node.http.transaction_frac_avg_10s.hit_revalidated_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.miss_changed", \ + "proxy.node.http.transaction_frac_avg_10s.miss_changed_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.miss_client_no_cache", \ + "proxy.node.http.transaction_frac_avg_10s.miss_client_no_cache_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.miss_cold", \ + "proxy.node.http.transaction_frac_avg_10s.miss_cold_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.miss_not_cacheable", \ + "proxy.node.http.transaction_frac_avg_10s.miss_not_cacheable_int_pct", \ + "proxy.node.http.transaction_frac_avg_10s.other.unclassified", \ + "proxy.node.http.transaction_frac_avg_10s.other.unclassified_int_pct", \ + "proxy.node.http.transaction_msec_avg_10s.errors.aborts", \ + "proxy.node.http.transaction_msec_avg_10s.errors.connect_failed", \ + "proxy.node.http.transaction_msec_avg_10s.errors.early_hangups", \ + "proxy.node.http.transaction_msec_avg_10s.errors.empty_hangups", \ + "proxy.node.http.transaction_msec_avg_10s.errors.other", \ + "proxy.node.http.transaction_msec_avg_10s.errors.possible_aborts", \ + "proxy.node.http.transaction_msec_avg_10s.errors.pre_accept_hangups", \ + "proxy.node.http.transaction_msec_avg_10s.hit_fresh", \ + "proxy.node.http.transaction_msec_avg_10s.hit_revalidated", \ + "proxy.node.http.transaction_msec_avg_10s.miss_changed", \ + "proxy.node.http.transaction_msec_avg_10s.miss_client_no_cache", \ + "proxy.node.http.transaction_msec_avg_10s.miss_cold", \ + "proxy.node.http.transaction_msec_avg_10s.miss_not_cacheable", \ + "proxy.node.http.transaction_msec_avg_10s.other.unclassified", \ + "proxy.node.http.user_agent_current_connections_count", \ + "proxy.node.http.user_agent_total_request_bytes", \ + "proxy.node.http.user_agent_total_response_bytes", \ + "proxy.node.http.user_agent_xacts_per_second", \ + "proxy.node.http.user_agents_total_documents_served", \ + "proxy.node.http.user_agents_total_transactions_count", \ + "proxy.node.log.bytes_received_from_network_avg_10s", \ + "proxy.node.log.bytes_sent_to_network_avg_10s", \ + "proxy.node.num_processes", \ + "proxy.node.origin_server_total_bytes", \ + "proxy.node.origin_server_total_bytes_avg_10s", \ + "proxy.node.proxy_running", \ + "proxy.node.restarts.manager.start_time", \ + "proxy.node.restarts.proxy.cache_ready_time", \ + "proxy.node.restarts.proxy.start_time", \ + "proxy.node.restarts.proxy.stop_time", \ + "proxy.node.user_agent_total_bytes", \ + "proxy.node.user_agent_total_bytes_avg_10s", \ + "proxy.node.user_agent_xacts_per_second", \ + "proxy.node.user_agents_total_documents_served", \ + "proxy.node.version.manager.build_time", \ + "proxy.process.cache.KB_read_per_sec", \ + "proxy.process.cache.KB_write_per_sec", \ + "proxy.process.cache.bytes_total", \ + "proxy.process.cache.bytes_used", \ + "proxy.process.cache.direntries.total", \ + "proxy.process.cache.direntries.used", \ + "proxy.process.cache.evacuate.active", \ + "proxy.process.cache.lookup.active", \ + "proxy.process.cache.percent_full", \ + "proxy.process.cache.ram_cache.bytes_total", \ + "proxy.process.cache.ram_cache.bytes_used", \ + "proxy.process.cache.ram_cache.total_bytes", \ + "proxy.process.cache.read.active", \ + "proxy.process.cache.read_per_sec", \ + "proxy.process.cache.remove.active", \ + "proxy.process.cache.scan.active", \ + "proxy.process.cache.update.active", \ + "proxy.process.cache.write.active", \ + "proxy.process.cache.write_per_sec", \ + "proxy.process.cluster.cache_callback_time", \ + "proxy.process.cluster.cache_outstanding", \ + "proxy.process.cluster.cluster_ping_time", \ + "proxy.process.cluster.connections_avg_time", \ + "proxy.process.cluster.connections_open", \ + "proxy.process.cluster.control_messages_avg_receive_time", \ + "proxy.process.cluster.control_messages_avg_send_time", \ + "proxy.process.cluster.lkrmt_cache_callback_time", \ + "proxy.process.cluster.local_connection_time", \ + "proxy.process.cluster.open_delay_time", \ + "proxy.process.cluster.rdmsg_assemble_time", \ + "proxy.process.cluster.remote_connection_time", \ + "proxy.process.cluster.remote_op_reply_timeouts", \ + "proxy.process.cluster.remote_op_timeouts", \ + "proxy.process.cluster.rmt_cache_callback_time", \ + "proxy.process.dns.fail_avg_time", \ + "proxy.process.dns.in_flight", \ + "proxy.process.dns.lookup_avg_time", \ + "proxy.process.dns.success_avg_time", \ + "proxy.process.dns.total_dns_lookups", \ + "proxy.process.hostdb.total_entries", \ + "proxy.process.hostdb.total_hits", \ + "proxy.process.hostdb.total_lookups", \ + "proxy.process.http.avg_transactions_per_client_connection", \ + "proxy.process.http.avg_transactions_per_parent_connection", \ + "proxy.process.http.avg_transactions_per_server_connection", \ + "proxy.process.http.background_fill_current_count", \ + "proxy.process.http.cache_connection_time", \ + "proxy.process.http.client_connection_time", \ + "proxy.process.http.client_transaction_time", \ + "proxy.process.http.client_write_time", \ + "proxy.process.http.current_active_client_connections", \ + "proxy.process.http.current_cache_connections", \ + "proxy.process.http.current_client_connections", \ + "proxy.process.http.current_client_transactions", \ + "proxy.process.http.current_icp_raw_transactions", \ + "proxy.process.http.current_icp_transactions", \ + "proxy.process.http.current_parent_proxy_connections", \ + "proxy.process.http.current_parent_proxy_raw_transactions", \ + "proxy.process.http.current_parent_proxy_transactions", \ + "proxy.process.http.current_server_connections", \ + "proxy.process.http.current_server_raw_transactions", \ + "proxy.process.http.current_server_transactions", \ + "proxy.process.http.icp_raw_transaction_time", \ + "proxy.process.http.icp_transaction_time", \ + "proxy.process.http.origin_server_request_document_total_size", \ + "proxy.process.http.origin_server_request_header_total_size", \ + "proxy.process.http.origin_server_response_document_total_size", \ + "proxy.process.http.origin_server_response_header_total_size", \ + "proxy.process.http.origin_server_speed_bytes_per_sec_100", \ + "proxy.process.http.origin_server_speed_bytes_per_sec_100K", \ + "proxy.process.http.origin_server_speed_bytes_per_sec_100M", \ + "proxy.process.http.origin_server_speed_bytes_per_sec_10K", \ + "proxy.process.http.origin_server_speed_bytes_per_sec_10M", \ + "proxy.process.http.origin_server_speed_bytes_per_sec_1K", \ + "proxy.process.http.origin_server_speed_bytes_per_sec_1M", \ + "proxy.process.http.parent_proxy_connection_time", \ + "proxy.process.http.parent_proxy_raw_transaction_time", \ + "proxy.process.http.parent_proxy_request_total_bytes", \ + "proxy.process.http.parent_proxy_response_total_bytes", \ + "proxy.process.http.parent_proxy_transaction_time", \ + "proxy.process.http.pushed_document_total_size", \ + "proxy.process.http.pushed_response_header_total_size", \ + "proxy.process.http.server_connection_time", \ + "proxy.process.http.server_raw_transaction_time", \ + "proxy.process.http.server_read_time", \ + "proxy.process.http.server_transaction_time", \ + "proxy.process.http.total_client_connections", \ + "proxy.process.http.total_client_connections_ipv4", \ + "proxy.process.http.total_client_connections_ipv6", \ + "proxy.process.http.total_incoming_connections", \ + "proxy.process.http.total_parent_proxy_connections", \ + "proxy.process.http.total_server_connections", \ + "proxy.process.http.total_transactions_think_time", \ + "proxy.process.http.total_transactions_time", \ + "proxy.process.http.total_x_redirect_count", \ + "proxy.process.http.transaction_totaltime.errors.aborts", \ + "proxy.process.http.transaction_totaltime.errors.connect_failed", \ + "proxy.process.http.transaction_totaltime.errors.early_hangups", \ + "proxy.process.http.transaction_totaltime.errors.empty_hangups", \ + "proxy.process.http.transaction_totaltime.errors.other", \ + "proxy.process.http.transaction_totaltime.errors.possible_aborts", \ + "proxy.process.http.transaction_totaltime.errors.pre_accept_hangups", \ + "proxy.process.http.transaction_totaltime.hit_fresh", \ + "proxy.process.http.transaction_totaltime.hit_fresh.process", \ + "proxy.process.http.transaction_totaltime.hit_revalidated", \ + "proxy.process.http.transaction_totaltime.miss_changed", \ + "proxy.process.http.transaction_totaltime.miss_client_no_cache", \ + "proxy.process.http.transaction_totaltime.miss_cold", \ + "proxy.process.http.transaction_totaltime.miss_not_cacheable", \ + "proxy.process.http.transaction_totaltime.other.unclassified", \ + "proxy.process.http.user_agent_request_document_total_size", \ + "proxy.process.http.user_agent_request_header_total_size", \ + "proxy.process.http.user_agent_response_document_total_size", \ + "proxy.process.http.user_agent_response_header_total_size", \ + "proxy.process.http.user_agent_speed_bytes_per_sec_100", \ + "proxy.process.http.user_agent_speed_bytes_per_sec_100K", \ + "proxy.process.http.user_agent_speed_bytes_per_sec_100M", \ + "proxy.process.http.user_agent_speed_bytes_per_sec_10K", \ + "proxy.process.http.user_agent_speed_bytes_per_sec_10M", \ + "proxy.process.http.user_agent_speed_bytes_per_sec_1K", \ + "proxy.process.http.user_agent_speed_bytes_per_sec_1M", \ + "proxy.process.https.total_client_connections", \ + "proxy.process.log.log_files_open", \ + "proxy.process.log.log_files_space_used", \ + "proxy.process.net.accepts_currently_open", \ + "proxy.process.net.connections_currently_open", \ + "proxy.process.socks.connections_currently_open", \ + "proxy.process.ssl.total_handshake_time", \ + "proxy.process.ssl.total_success_handshake_count", \ + "proxy.process.ssl.user_agent_session_timeout", \ + "proxy.process.update.state_machines", \ + "proxy.process.version.server.build_time", \ + "proxy.process.websocket.current_active_client_connections" + +// XXX SSL stats (TS-2169) are going to land soon; we will need to update this list ... + +// NOTE: the status list of gauge metric names does not capture dynamically constructed +// names like proxy.process.cache.volume_XX.*.active + +#if __cplusplus >= 201102L + +static const std::set<std::string> epic_gauges = { + GAUGE_METRIC_NAMES +}; + +#else + +static std::set<std::string> init_gauges() +{ + static const char *gauges[] = { + GAUGE_METRIC_NAMES + }; + + std::set<std::string> s; + + for (unsigned i = 0; i < countof(gauges); ++i) { + s.insert(gauges[i]); + } + + return s; +} + +static const std::set<std::string> epic_gauges(init_gauges()); + +#endif + +struct epic_sample_context +{ + time_t sample_time; + FILE *sample_fp; + char sample_host[MAXHOSTNAMELEN]; /* sysconf(_SC_HOST_NAME_MAX) */ +}; + +static void +epic_write_stats( + TSRecordType /* rtype */, + void * edata, + int /* registered */, + const char * name, + TSRecordDataType dtype, + TSRecordData * dvalue) +{ + epic_sample_context *sample = (epic_sample_context *) edata; + const char *etype; + + TSReleaseAssert(sample != NULL); + TSReleaseAssert(sample->sample_fp != NULL); + + /* + * O:varName:itime:value:node:type:step + * + * varName: the name of the variable being stored in 'NODE' + * node : name space for variables, buckets of data, hostname, node, etc. + * itime : the time in unix seconds which the datapoint is to be stored + * value : numeric value to be stored in the ITIME time slot. Counter and + * Derive must be integers, not floats. + * type: the datasource type: + * GAUGE: for things like temperature, or current number of processes + * COUNTER: for continuous incrementing numbers, inception based stats + * (will do counter-wrap addition at 32bit or 64bit) + * DERIVE: like COUNTER, except no counter-wrap detection (note: use + * this for Epic API data publishing) + * ABSOLUTE: for counters that reset upon reading + * step: (optional) default step is 60 seconds, used here if required and + * not sending + */ + + /* Traffic server metrics don't tell is their semantics, only their data + * type. Mostly, metrics are counters, though a few are really gauges. This + * sucks, but there's no workaround right now ... + */ + etype = (epic_gauges.find(name) != epic_gauges.end())? "GAUGE" : "DERIVE"; + + switch (dtype) { + case TS_RECORDDATATYPE_INT: + fprintf(sample->sample_fp, "O:%s:%lld:%" PRId64 ":%s:%s:%lld\n", + name, (long long) sample->sample_time, dvalue->rec_int, sample->sample_host, etype, + (long long) epic_period); + break; + case TS_RECORDDATATYPE_FLOAT: + fprintf(sample->sample_fp, "O:%s:%lld:%f:%s:%s:%lld\n", + name, (long long) sample->sample_time, dvalue->rec_float, sample->sample_host, etype, + (long long) epic_period); + break; + case TS_RECORDDATATYPE_COUNTER: + fprintf(sample->sample_fp, "O:%s:%lld:%" PRId64 ":%s:%s:%lld\n", + name, (long long) sample->sample_time, dvalue->rec_counter, sample->sample_host, etype, + (long long) epic_period); + break; + case TS_RECORDDATATYPE_STRING: + case TS_RECORDDATATYPE_STAT_CONST: /* float */ + case TS_RECORDDATATYPE_STAT_FX: /* int */ + /* fallthru */ + default: +#if defined(DEBUG) + debug("skipping unsupported metric %s (type %d)", name, (int) dtype); +#endif + return; + } +} + +static int +epic_flush_stats(TSCont /* contp */, TSEvent /* event */ , void * /* edata */ ) +{ + char path[MAXPATHLEN]; + epic_sample_context sample; + + TSReleaseAssert(epic_prefix != NULL); + TSReleaseAssert(*epic_prefix != '\0'); + + sample.sample_time = time(NULL); + debug("%s/trafficserver.%lld.%llu", epic_prefix, (long long) sample.sample_time, (unsigned long long) getpid()); + if (gethostname(sample.sample_host, sizeof(sample.sample_host)) == -1) { + error("gethostname() failed: %s", strerror(errno)); + strncpy(sample.sample_host, "unknown", sizeof(sample.sample_host)); + } + + snprintf(path, sizeof(path), "%s/trafficserver.%lld.%llu", + epic_prefix, (long long) sample.sample_time, (unsigned long long) getpid()); + + // XXX track the file size and preallocate ... + + sample.sample_fp = fopen(path, "w"); + if (sample.sample_fp == NULL) { + error("failed to create %s: %s", path, strerror(errno)); + return 0; + } + + TSRecordDump(TS_RECORDTYPE_PLUGIN | TS_RECORDTYPE_NODE | TS_RECORDTYPE_PROCESS, epic_write_stats, &sample); + + if (fclose(sample.sample_fp) == -1) { + error("fclose() failed: %s", strerror(errno)); + } + + return 0; +} + +void +TSPluginInit(int argc, const char *argv[]) +{ + static const struct option longopts[] = { + {"directory", required_argument, NULL, 'd'}, + {"period", required_argument, NULL, 'p'}, + {NULL, 0, NULL, 0} + }; + + TSPluginRegistrationInfo info; + + info.plugin_name = (char *) "epic"; + info.vendor_name = (char *) "Apache Software Foundation"; + info.support_email = (char *) "[email protected]"; + + if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) { + error("plugin registration failed"); + } + + for (;;) { + int opt = getopt_long(argc, (char *const *) argv, "p:d:", longopts, NULL); + + if (opt == -1) { + break; /* done */ + } + + switch (opt) { + case 'd': + epic_prefix = strdup(optarg); + break; + case 'p': + epic_period = atoi(optarg); + break; + default: + error("usage: epic.so [--directory PATH] [--period SECS]"); + } + } + + debug("initialized plugin with directory %s and period %d sec", epic_prefix, (int) epic_period); + TSContScheduleEvery(TSContCreate(epic_flush_stats, NULL), epic_period * 1000ll, TS_THREAD_POOL_TASK); +}
