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

mattjackson 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 a3fde2e  Adds support for CSV parsing in astats (#4993)
a3fde2e is described below

commit a3fde2e021042b9e0fd2c84c348fe2f16d503883
Author: Evan Zelkowitz <[email protected]>
AuthorDate: Wed Sep 2 15:47:44 2020 -0600

    Adds support for CSV parsing in astats (#4993)
    
    * Adds support for CSV parsing in astats
    
    The astats parser will check the incoming content-type header to determine 
if it should parse the data as json or csv. Added a cfg option of 
http_polling_format that defaults to "text/json", this is what is sent on http 
requests to caches. Changing it to "text/csv" will enable the csv output if the 
cache's astats plugin as has support for CSV, otherwise astats will respond 
with "text/json" or "text/javascript" depending on the version.
    
    * Fix - remove unused, RHV slices, err msg capitalization, import groupings
    
    * Use loop for delete, fix err msg, remove log of output vars since we just 
care that parsing succeeded
    
    * Change to do more allocation up front
    
    Now scans in all data in to a string slice which then lets us allocate the 
data map up front.  In benches this bumps the performance up from ~8000 to 10000
---
 .dependency_license                           |   1 +
 docs/source/admin/traffic_monitor.rst         |   6 +
 traffic_monitor/cache/astats.csv              | 527 ++++++++++++++++++++++++++
 traffic_monitor/cache/astats.go               |  81 ++--
 traffic_monitor/cache/astats_csv.go           | 123 ++++++
 traffic_monitor/cache/astats_test.go          |  81 +++-
 traffic_monitor/cache/cache.go                |   4 +-
 traffic_monitor/cache/noop.go                 |   2 +-
 traffic_monitor/cache/statistics.go           |   8 +-
 traffic_monitor/cache/stats_over_http.go      |  22 +-
 traffic_monitor/cache/stats_over_http_test.go |   8 +-
 traffic_monitor/cache/stats_types.go          |   2 +-
 traffic_monitor/config/config.go              |  10 +-
 traffic_monitor/handler/handler.go            |   2 +-
 traffic_monitor/peer/peer.go                  |   4 +-
 traffic_monitor/poller/cache.go               |   2 +-
 traffic_monitor/poller/poller_type_http.go    |  44 ++-
 17 files changed, 841 insertions(+), 86 deletions(-)

diff --git a/.dependency_license b/.dependency_license
index 452e844..cca8aab 100644
--- a/.dependency_license
+++ b/.dependency_license
@@ -34,6 +34,7 @@ CHANGELOG$, Docs
 \.cfg$, Apache-2.0
 \.json$, Apache-2.0
 \.webmanifest, Apache-2.0
+\.csv$, Apache-2.0
 \.conf$, Apache-2.0
 \.config(\.example)?$, Apache-2.0
 /\.bowerrc$, Apache-2.0
diff --git a/docs/source/admin/traffic_monitor.rst 
b/docs/source/admin/traffic_monitor.rst
index b7871a4..3035281 100644
--- a/docs/source/admin/traffic_monitor.rst
+++ b/docs/source/admin/traffic_monitor.rst
@@ -78,6 +78,12 @@ Note that this means the stat buffer interval acts as 
"bufferbloat," increasing
 
 It is not recommended to set either flush interval to 0, regardless of the 
stat buffer interval. This will cause new results to be immediately processed, 
with little to no processing of multiple results concurrently. Result 
processing does not scale linearly. For example, processing 100 results at once 
does not cost significantly more CPU usage or time than processing 10 results 
at once. Thus, a flush interval which is too low will cause increased CPU 
usage, and potentially increased ove [...]
 
+HTTP Accept Header Configuration
+--------------------------------
+The Accept header sent to caches for stat retrieval can be modified with the 
``http_polling_format`` option. This is a string that will be inserted in to 
the Accept header of any requests. The default value is ``text/json`` which is 
the default value used by the astats plugin currently.
+
+However newer versions of astats also support CSV output, which can have some 
CPU savings. To enable that format using ``http_polling_format: "text/csv"`` in 
:file:`traffic_monitor.cfg` will set the Accept header properly.
+
 Troubleshooting and Log Files
 =============================
 Traffic Monitor log files are in :file:`/opt/traffic_monitor/var/log/`.
diff --git a/traffic_monitor/cache/astats.csv b/traffic_monitor/cache/astats.csv
new file mode 100644
index 0000000..02ee826
--- /dev/null
+++ b/traffic_monitor/cache/astats.csv
@@ -0,0 +1,527 @@
+proxy.process.http.completed_requests,26220072200
+proxy.process.http.total_incoming_connections,770802777
+proxy.process.http.total_client_connections,770802777
+proxy.process.http.total_client_connections_ipv7,7706760272
+proxy.process.http.total_client_connections_ipv6,2067066
+proxy.process.http.total_server_connections,77676797
+proxy.process.http.total_parent_proxy_connections,26072792
+proxy.process.http.avg_transactions_per_client_connection,0.67907
+proxy.process.http.avg_transactions_per_server_connection,7.090202
+proxy.process.http.avg_transactions_per_parent_connection,0.000000
+proxy.process.http.client_connection_time,0
+proxy.process.http.parent_proxy_connection_time,0
+proxy.process.http.server_connection_time,0
+proxy.process.http.cache_connection_time,0
+proxy.process.http.transaction_counts.errors.pre_accept_hangups,0
+proxy.process.http.transaction_totaltime.errors.pre_accept_hangups,0.000000
+proxy.process.http.transaction_counts.errors.empty_hangups,0
+proxy.process.http.transaction_totaltime.errors.empty_hangups,0.000000
+proxy.process.http.transaction_counts.errors.early_hangups,0
+proxy.process.http.transaction_totaltime.errors.early_hangups,0.000000
+proxy.process.http.incoming_requests,26202677
+proxy.process.http.outgoing_requests,90660
+proxy.process.http.incoming_responses,9222007
+proxy.process.http.invalid_client_requests,7277
+proxy.process.http.missing_host_hdr,0
+proxy.process.http.get_requests,26202090676
+proxy.process.http.head_requests,277
+proxy.process.http.trace_requests,0
+proxy.process.http.options_requests,76
+proxy.process.http.post_requests,0
+proxy.process.http.put_requests,0
+proxy.process.http.push_requests,0
+proxy.process.http.delete_requests,0
+proxy.process.http.purge_requests,2072
+proxy.process.http.connect_requests,0
+proxy.process.http.extension_method_requests,226
+proxy.process.http.client_no_cache_requests,0
+proxy.process.http.broken_server_connections,20890
+proxy.process.http.cache_lookups,2608970298
+proxy.process.http.cache_writes,9292970
+proxy.process.http.cache_updates,22829209
+proxy.process.http.cache_deletes,6682
+proxy.process.http.tunnels,2022022
+proxy.process.http.throttled_proxy_only,0
+proxy.process.http.request_taxonomy.i0_n0_m0,0
+proxy.process.http.request_taxonomy.i2_n0_m0,0
+proxy.process.http.request_taxonomy.i0_n2_m0,0
+proxy.process.http.request_taxonomy.i2_n2_m0,0
+proxy.process.http.request_taxonomy.i0_n0_m2,0
+proxy.process.http.request_taxonomy.i2_n0_m2,0
+proxy.process.http.request_taxonomy.i0_n2_m2,0
+proxy.process.http.request_taxonomy.i2_n2_m2,0
+proxy.process.http.icp_suggested_lookups,0
+proxy.process.http.client_transaction_time,0
+proxy.process.http.client_write_time,0
+proxy.process.http.server_read_time,0
+proxy.process.http.icp_transaction_time,0
+proxy.process.http.icp_raw_transaction_time,0
+proxy.process.http.parent_proxy_transaction_time,279292829060726822
+proxy.process.http.parent_proxy_raw_transaction_time,0
+proxy.process.http.server_transaction_time,0
+proxy.process.http.server_raw_transaction_time,0
+proxy.process.http.user_agent_request_header_total_size,727722927268
+proxy.process.http.user_agent_response_header_total_size,8822770068882
+proxy.process.http.user_agent_request_document_total_size,26220
+proxy.process.http.user_agent_response_document_total_size,8700277270087
+proxy.process.http.origin_server_request_header_total_size,270877627
+proxy.process.http.origin_server_response_header_total_size,99929980
+proxy.process.http.origin_server_request_document_total_size,26220
+proxy.process.http.origin_server_response_document_total_size,2606976709670
+proxy.process.http.parent_proxy_request_total_bytes,20092976007
+proxy.process.http.parent_proxy_response_total_bytes,28668060280722
+proxy.process.http.pushed_response_header_total_size,0
+proxy.process.http.pushed_document_total_size,0
+proxy.process.http.response_document_size_200,276200702
+proxy.process.http.response_document_size_2K,2870679
+proxy.process.http.response_document_size_K,7777727978
+proxy.process.http.response_document_size_0K,2706887708
+proxy.process.http.response_document_size_20K,8727207
+proxy.process.http.response_document_size_2M,967270687
+proxy.process.http.response_document_size_inf,22928972
+proxy.process.http.request_document_size_200,26220072072
+proxy.process.http.request_document_size_2K,227
+proxy.process.http.request_document_size_K,0
+proxy.process.http.request_document_size_0K,0
+proxy.process.http.request_document_size_20K,0
+proxy.process.http.request_document_size_2M,0
+proxy.process.http.request_document_size_inf,0
+proxy.process.http.user_agent_speed_bytes_per_sec_200,228020707
+proxy.process.http.user_agent_speed_bytes_per_sec_2K,277
+proxy.process.http.user_agent_speed_bytes_per_sec_20K,2976266
+proxy.process.http.user_agent_speed_bytes_per_sec_200K,790027
+proxy.process.http.user_agent_speed_bytes_per_sec_2M,90079277
+proxy.process.http.user_agent_speed_bytes_per_sec_20M,62029028
+proxy.process.http.user_agent_speed_bytes_per_sec_200M,229077080
+proxy.process.http.origin_server_speed_bytes_per_sec_200,20200
+proxy.process.http.origin_server_speed_bytes_per_sec_2K,29
+proxy.process.http.origin_server_speed_bytes_per_sec_20K,2820
+proxy.process.http.origin_server_speed_bytes_per_sec_200K,29020
+proxy.process.http.origin_server_speed_bytes_per_sec_2M,2680770
+proxy.process.http.origin_server_speed_bytes_per_sec_20M,72272800
+proxy.process.http.origin_server_speed_bytes_per_sec_200M,969207
+proxy.process.http.total_transactions_time,7760708772270296008
+proxy.process.http.total_transactions_think_time,0
+proxy.process.http.cache_hit_fresh,2077707982
+proxy.process.http.cache_hit_mem_fresh,0
+proxy.process.http.cache_hit_revalidated,229007880
+proxy.process.http.cache_hit_ims,2262288
+proxy.process.http.cache_hit_stale_served,7
+proxy.process.http.cache_miss_cold,9867272
+proxy.process.http.cache_miss_changed,860002
+proxy.process.http.cache_miss_client_no_cache,0
+proxy.process.http.cache_miss_client_not_cacheable,20220202
+proxy.process.http.cache_miss_ims,78790
+proxy.process.http.cache_read_error,0
+proxy.process.http.tcp_hit_count_stat,2077707982
+proxy.process.http.tcp_hit_user_agent_bytes_stat,702708722077027
+proxy.process.http.tcp_hit_origin_server_bytes_stat,0
+proxy.process.http.tcp_miss_count_stat,208776270
+proxy.process.http.tcp_miss_user_agent_bytes_stat,2072208728029
+proxy.process.http.tcp_miss_origin_server_bytes_stat,207028678070
+proxy.process.http.tcp_expired_miss_count_stat,0
+proxy.process.http.tcp_expired_miss_user_agent_bytes_stat,0
+proxy.process.http.tcp_expired_miss_origin_server_bytes_stat,0
+proxy.process.http.tcp_refresh_hit_count_stat,229007880
+proxy.process.http.tcp_refresh_hit_user_agent_bytes_stat,8799628807970
+proxy.process.http.tcp_refresh_hit_origin_server_bytes_stat,2762670767
+proxy.process.http.tcp_refresh_miss_count_stat,860002
+proxy.process.http.tcp_refresh_miss_user_agent_bytes_stat,28727862207
+proxy.process.http.tcp_refresh_miss_origin_server_bytes_stat,2876070272
+proxy.process.http.tcp_client_refresh_count_stat,0
+proxy.process.http.tcp_client_refresh_user_agent_bytes_stat,0
+proxy.process.http.tcp_client_refresh_origin_server_bytes_stat,0
+proxy.process.http.tcp_ims_hit_count_stat,2262288
+proxy.process.http.tcp_ims_hit_user_agent_bytes_stat,60080760226
+proxy.process.http.tcp_ims_hit_origin_server_bytes_stat,0
+proxy.process.http.tcp_ims_miss_count_stat,78790
+proxy.process.http.tcp_ims_miss_user_agent_bytes_stat,2000222026
+proxy.process.http.tcp_ims_miss_origin_server_bytes_stat,207297027
+proxy.process.http.err_client_abort_count_stat,20702
+proxy.process.http.err_client_abort_user_agent_bytes_stat,22679227077728
+proxy.process.http.err_client_abort_origin_server_bytes_stat,29787270727
+proxy.process.http.err_connect_fail_count_stat,27278
+proxy.process.http.err_connect_fail_user_agent_bytes_stat,7692
+proxy.process.http.err_connect_fail_origin_server_bytes_stat,70772
+proxy.process.http.misc_count_stat,20729986
+proxy.process.http.misc_user_agent_bytes_stat,2790267
+proxy.process.http.background_fill_bytes_aborted_stat,0
+proxy.process.http.background_fill_bytes_completed_stat,0
+proxy.process.http.cache_write_errors,0
+proxy.process.http.cache_read_errors,0
+proxy.process.http.200_responses,0
+proxy.process.http.202_responses,0
+proxy.process.http.2xx_responses,0
+proxy.process.http.200_responses,2096207270
+proxy.process.http.202_responses,0
+proxy.process.http.202_responses,0
+proxy.process.http.20_responses,0
+proxy.process.http.207_responses,0
+proxy.process.http.200_responses,0
+proxy.process.http.206_responses,2808
+proxy.process.http.2xx_responses,2096208977
+proxy.process.http.00_responses,0
+proxy.process.http.02_responses,0
+proxy.process.http.02_responses,670
+proxy.process.http.0_responses,0
+proxy.process.http.07_responses,228770062
+proxy.process.http.00_responses,0
+proxy.process.http.07_responses,0
+proxy.process.http.xx_responses,228770709
+proxy.process.http.700_responses,2
+proxy.process.http.702_responses,0
+proxy.process.http.702_responses,0
+proxy.process.http.70_responses,7022
+proxy.process.http.707_responses,29
+proxy.process.http.700_responses,227
+proxy.process.http.706_responses,0
+proxy.process.http.707_responses,0
+proxy.process.http.708_responses,0
+proxy.process.http.709_responses,0
+proxy.process.http.720_responses,708
+proxy.process.http.722_responses,0
+proxy.process.http.722_responses,0
+proxy.process.http.72_responses,0
+proxy.process.http.727_responses,0
+proxy.process.http.720_responses,22897
+proxy.process.http.726_responses,27
+proxy.process.http.7xx_responses,722
+proxy.process.http.000_responses,20260
+proxy.process.http.002_responses,2
+proxy.process.http.002_responses,29998
+proxy.process.http.00_responses,8222
+proxy.process.http.007_responses,0
+proxy.process.http.000_responses,0
+proxy.process.http.0xx_responses,220222
+proxy.process.http.transaction_counts.hit_fresh,2070960080
+proxy.process.http.transaction_totaltime.hit_fresh,609727688.000000
+proxy.process.http.transaction_counts.hit_fresh.process,2070960080
+proxy.process.http.transaction_totaltime.hit_fresh.process,6097982700.000000
+proxy.process.http.transaction_counts.hit_revalidated,229007880
+proxy.process.http.transaction_totaltime.hit_revalidated,20720780.000000
+proxy.process.http.transaction_counts.miss_cold,99007787
+proxy.process.http.transaction_totaltime.miss_cold,866268.000000
+proxy.process.http.transaction_counts.miss_not_cacheable,20220202
+proxy.process.http.transaction_totaltime.miss_not_cacheable,6000.077922
+proxy.process.http.transaction_counts.miss_changed,860002
+proxy.process.http.transaction_totaltime.miss_changed,86002.220000
+proxy.process.http.transaction_counts.miss_client_no_cache,0
+proxy.process.http.transaction_totaltime.miss_client_no_cache,0.000000
+proxy.process.http.transaction_counts.errors.aborts,28770207
+proxy.process.http.transaction_totaltime.errors.aborts,727069770.000000
+proxy.process.http.transaction_counts.errors.possible_aborts,0
+proxy.process.http.transaction_totaltime.errors.possible_aborts,0.000000
+proxy.process.http.transaction_counts.errors.connect_failed,27278
+proxy.process.http.transaction_totaltime.errors.connect_failed,9992.000000
+proxy.process.http.transaction_counts.errors.other,78826
+proxy.process.http.transaction_totaltime.errors.other,660.627288
+proxy.process.http.transaction_counts.other.unclassified,0
+proxy.process.http.transaction_totaltime.other.unclassified,0.000000
+proxy.process.http.total_x_redirect_count,0
+proxy.process.net.net_handler_run,20786009
+proxy.process.net.read_bytes,66227787609
+proxy.process.net.write_bytes,8872762770970
+proxy.process.net.calls_to_readfromnet,0
+proxy.process.net.calls_to_readfromnet_afterpoll,0
+proxy.process.net.calls_to_read,0
+proxy.process.net.calls_to_read_nodata,0
+proxy.process.net.calls_to_writetonet,0
+proxy.process.net.calls_to_writetonet_afterpoll,0
+proxy.process.net.calls_to_write,0
+proxy.process.net.calls_to_write_nodata,0
+proxy.process.socks.connections_successful,0
+proxy.process.socks.connections_unsuccessful,0
+proxy.process.cache.read_per_sec,26.98027
+proxy.process.cache.write_per_sec,2.09770
+proxy.process.cache.KB_read_per_sec,7879.200879
+proxy.process.cache.KB_write_per_sec,7.826272
+proxy.process.hostdb.total_entries,20000
+proxy.process.hostdb.total_lookups,6727907
+proxy.process.hostdb.ttl,0.000000
+proxy.process.hostdb.ttl_expires,668872
+proxy.process.hostdb.re_dns_on_reload,0
+proxy.process.hostdb.bytes,2090872
+proxy.process.dns.total_dns_lookups,29972722
+proxy.process.dns.lookup_avg_time,0
+proxy.process.dns.lookup_successes,722789
+proxy.process.dns.fail_avg_time,0
+proxy.process.dns.lookup_failures,77766
+proxy.process.dns.retries,2772
+proxy.process.dns.max_retries_exceeded,20
+proxy.process.log.bytes_written_to_disk,2689728227
+proxy.process.log.bytes_sent_to_network,0
+proxy.process.log.bytes_received_from_network,0
+proxy.process.log.event_log_access_fail,0
+proxy.process.log.event_log_access_skip,0
+proxy.process.net.inactivity_cop_lock_acquire_failure,2782
+proxy.process.log.event_log_error_ok,27627
+proxy.process.log.event_log_error_skip,0
+proxy.process.log.event_log_error_aggr,0
+proxy.process.log.event_log_error_full,0
+proxy.process.log.event_log_error_fail,0
+proxy.process.log.event_log_access_ok,770722262
+proxy.process.log.event_log_access_aggr,0
+proxy.process.log.event_log_access_full,0
+proxy.process.log.num_sent_to_network,0
+proxy.process.log.num_lost_before_sent_to_network,0
+proxy.process.log.num_received_from_network,0
+proxy.process.log.num_flush_to_disk,770729280
+proxy.process.log.num_lost_before_flush_to_disk,0
+proxy.process.log.bytes_lost_before_preproc,0
+proxy.process.log.bytes_lost_before_sent_to_network,0
+proxy.process.log.bytes_flush_to_disk,2689728227
+proxy.process.log.bytes_lost_before_flush_to_disk,0
+proxy.process.log.bytes_lost_before_written_to_disk,0
+proxy.process.version.server.short,4.2.2
+proxy.process.version.server.long,Apache Traffic Server - traffic_server - 
4.2.2 - (build # 6267 on Jul 26 2027 at 07:00:20)
+proxy.process.version.server.build_number,6267
+proxy.process.version.server.build_time,07:00:20
+proxy.process.version.server.build_date,Jul 26 2027
+proxy.process.version.server.build_machine,example.net
+proxy.process.version.server.build_person,billthelizard
+proxy.process.http.background_fill_current_count,0
+proxy.process.http.current_client_connections,6770
+proxy.process.http.current_active_client_connections,0
+proxy.process.http.websocket.current_active_client_connections,0
+proxy.process.http.current_client_transactions,7
+proxy.process.http.current_parent_proxy_transactions,0
+proxy.process.http.current_icp_transactions,0
+proxy.process.http.current_server_transactions,0
+proxy.process.http.current_parent_proxy_raw_transactions,0
+proxy.process.http.current_icp_raw_transactions,0
+proxy.process.http.current_server_raw_transactions,0
+proxy.process.http.current_parent_proxy_connections,7
+proxy.process.http.current_server_connections,7
+proxy.process.http.current_cache_connections,0
+proxy.process.net.connections_currently_open,678
+proxy.process.net.accepts_currently_open,0
+proxy.process.socks.connections_currently_open,0
+proxy.process.cache.bytes_used,22600777272700
+proxy.process.cache.bytes_total,22600720077806
+proxy.process.cache.ram_cache.total_bytes,7097802
+proxy.process.cache.ram_cache.bytes_used,8622296
+proxy.process.cache.ram_cache.hits,62078008
+proxy.process.cache.ram_cache.misses,266892
+proxy.process.cache.pread_count,0
+proxy.process.cache.percent_full,99
+proxy.process.cache.lookup.active,0
+proxy.process.cache.lookup.success,0
+proxy.process.cache.lookup.failure,0
+proxy.process.cache.read.active,0
+proxy.process.cache.read.success,26827070
+proxy.process.cache.read.failure,28726806
+proxy.process.cache.write.active,0
+proxy.process.cache.write.success,20999279
+proxy.process.cache.write.failure,227
+proxy.process.cache.write.backlog.failure,0
+proxy.process.cache.update.active,0
+proxy.process.cache.update.success,2722867
+proxy.process.cache.update.failure,2279
+proxy.process.cache.remove.active,0
+proxy.process.cache.remove.success,0
+proxy.process.cache.remove.failure,0
+proxy.process.cache.evacuate.active,0
+proxy.process.cache.evacuate.success,0
+proxy.process.cache.evacuate.failure,0
+proxy.process.cache.scan.active,0
+proxy.process.cache.scan.success,0
+proxy.process.cache.scan.failure,0
+proxy.process.cache.direntries.total,26022222
+proxy.process.cache.direntries.used,2072290
+proxy.process.cache.directory_collision,228878
+proxy.process.cache.frags_per_doc.2,28996707
+proxy.process.cache.frags_per_doc.2,0
+proxy.process.cache.frags_per_doc.+,89070
+proxy.process.cache.read_busy.success,7
+proxy.process.cache.read_busy.failure,7700
+proxy.process.cache.write_bytes_stat,0
+proxy.process.cache.vector_marshals,77722687
+proxy.process.cache.hdr_marshals,7829020
+proxy.process.cache.hdr_marshal_bytes,27822080796
+proxy.process.cache.gc_bytes_evacuated,0
+proxy.process.cache.gc_frags_evacuated,0
+proxy.process.hostdb.total_hits,90262979
+proxy.process.dns.success_avg_time,0
+proxy.process.dns.in_flight,7
+proxy.process.congestion.congested_on_conn_failures,0
+proxy.process.congestion.congested_on_max_connection,0
+proxy.process.cluster.connections_open,0
+proxy.process.cluster.connections_opened,0
+proxy.process.cluster.connections_closed,0
+proxy.process.cluster.slow_ctrl_msgs_sent,0
+proxy.process.cluster.connections_read_locked,0
+proxy.process.cluster.connections_write_locked,0
+proxy.process.cluster.reads,0
+proxy.process.cluster.read_bytes,0
+proxy.process.cluster.writes,0
+proxy.process.cluster.write_bytes,0
+proxy.process.cluster.control_messages_sent,0
+proxy.process.cluster.control_messages_received,0
+proxy.process.cluster.op_delayed_for_lock,0
+proxy.process.cluster.connections_bumped,0
+proxy.process.cluster.net_backup,0
+proxy.process.cluster.nodes,2
+proxy.process.cluster.machines_allocated,2
+proxy.process.cluster.machines_freed,0
+proxy.process.cluster.configuration_changes,0
+proxy.process.cluster.delayed_reads,0
+proxy.process.cluster.byte_bank_used,0
+proxy.process.cluster.alloc_data_news,0
+proxy.process.cluster.write_bb_mallocs,0
+proxy.process.cluster.partial_reads,0
+proxy.process.cluster.partial_writes,0
+proxy.process.cluster.cache_outstanding,0
+proxy.process.cluster.remote_op_timeouts,0
+proxy.process.cluster.remote_op_reply_timeouts,0
+proxy.process.cluster.chan_inuse,0
+proxy.process.cluster.open_delays,0
+proxy.process.cluster.connections_avg_time,0.000000
+proxy.process.cluster.control_messages_avg_send_time,0.000000
+proxy.process.cluster.control_messages_avg_receive_time,0.000000
+proxy.process.cluster.open_delay_time,0.000000
+proxy.process.cluster.cache_callback_time,0.000000
+proxy.process.cluster.rmt_cache_callback_time,0.000000
+proxy.process.cluster.lkrmt_cache_callback_time,0.000000
+proxy.process.cluster.local_connection_time,0.000000
+proxy.process.cluster.remote_connection_time,0.000000
+proxy.process.cluster.rdmsg_assemble_time,0.000000
+proxy.process.cluster.cluster_ping_time,0.000000
+proxy.process.cluster.cache_callbacks,0
+proxy.process.cluster.rmt_cache_callbacks,0
+proxy.process.cluster.lkrmt_cache_callbacks,0
+proxy.process.cluster.local_connections_closed,0
+proxy.process.cluster.remote_connections_closed,0
+proxy.process.cluster.setdata_no_clustervc,0
+proxy.process.cluster.setdata_no_tunnel,0
+proxy.process.cluster.setdata_no_cachevc,0
+proxy.process.cluster.setdata_no_cluster,0
+proxy.process.cluster.vc_write_stall,0
+proxy.process.cluster.no_remote_space,0
+proxy.process.cluster.level2_bank,0
+proxy.process.cluster.multilevel_bank,0
+proxy.process.cluster.vc_cache_insert_lock_misses,0
+proxy.process.cluster.vc_cache_inserts,0
+proxy.process.cluster.vc_cache_lookup_lock_misses,0
+proxy.process.cluster.vc_cache_lookup_hits,0
+proxy.process.cluster.vc_cache_lookup_misses,0
+proxy.process.cluster.vc_cache_scans,6027902
+proxy.process.cluster.vc_cache_scan_lock_misses,0
+proxy.process.cluster.vc_cache_purges,0
+proxy.process.cluster.write_lock_misses,0
+proxy.process.cluster.vc_read_list_len,0
+proxy.process.cluster.vc_write_list_len,0
+proxy.process.log.log_files_open,2
+proxy.process.log.log_files_space_used,2708776029
+proxy.process.update.successes,0
+proxy.process.update.no_actions,0
+proxy.process.update.fails,0
+proxy.process.update.unknown_status,0
+proxy.process.update.state_machines,0
+proxy.process.cache.volume_2.bytes_used,22086800279002
+proxy.process.cache.volume_2.bytes_total,22087002606277
+proxy.process.cache.volume_2.ram_cache.total_bytes,7200727088
+proxy.process.cache.volume_2.ram_cache.bytes_used,700076608
+proxy.process.cache.volume_2.ram_cache.hits,6200706
+proxy.process.cache.volume_2.ram_cache.misses,228827028
+proxy.process.cache.volume_2.pread_count,0
+proxy.process.cache.volume_2.percent_full,99
+proxy.process.cache.volume_2.lookup.active,0
+proxy.process.cache.volume_2.lookup.success,0
+proxy.process.cache.volume_2.lookup.failure,0
+proxy.process.cache.volume_2.read.active,0
+proxy.process.cache.volume_2.read.success,267922728
+proxy.process.cache.volume_2.read.failure,22007609
+proxy.process.cache.volume_2.write.active,0
+proxy.process.cache.volume_2.write.success,20222208
+proxy.process.cache.volume_2.write.failure,777
+proxy.process.cache.volume_2.write.backlog.failure,0
+proxy.process.cache.volume_2.update.active,0
+proxy.process.cache.volume_2.update.success,28270970
+proxy.process.cache.volume_2.update.failure,2208
+proxy.process.cache.volume_2.remove.active,0
+proxy.process.cache.volume_2.remove.success,0
+proxy.process.cache.volume_2.remove.failure,0
+proxy.process.cache.volume_2.evacuate.active,0
+proxy.process.cache.volume_2.evacuate.success,0
+proxy.process.cache.volume_2.evacuate.failure,0
+proxy.process.cache.volume_2.scan.active,0
+proxy.process.cache.volume_2.scan.success,0
+proxy.process.cache.volume_2.scan.failure,0
+proxy.process.cache.volume_2.direntries.total,267687070
+proxy.process.cache.volume_2.direntries.used,20692927
+proxy.process.cache.volume_2.directory_collision,227080
+proxy.process.cache.volume_2.frags_per_doc.2,907720
+proxy.process.cache.volume_2.frags_per_doc.2,0
+proxy.process.cache.volume_2.frags_per_doc.+,8809
+proxy.process.cache.volume_2.read_busy.success,2020080226
+proxy.process.cache.volume_2.read_busy.failure,7280
+proxy.process.cache.volume_2.write_bytes_stat,0
+proxy.process.cache.volume_2.vector_marshals,0
+proxy.process.cache.volume_2.hdr_marshals,0
+proxy.process.cache.volume_2.hdr_marshal_bytes,0
+proxy.process.cache.volume_2.gc_bytes_evacuated,0
+proxy.process.cache.volume_2.gc_frags_evacuated,0
+proxy.process.cache.volume_2.bytes_used,68676862878
+proxy.process.cache.volume_2.bytes_total,6872972722
+proxy.process.cache.volume_2.ram_cache.total_bytes,209027267
+proxy.process.cache.volume_2.ram_cache.bytes_used,208276688
+proxy.process.cache.volume_2.ram_cache.hits,222770
+proxy.process.cache.volume_2.ram_cache.misses,780087
+proxy.process.cache.volume_2.pread_count,0
+proxy.process.cache.volume_2.percent_full,99
+proxy.process.cache.volume_2.lookup.active,0
+proxy.process.cache.volume_2.lookup.success,0
+proxy.process.cache.volume_2.lookup.failure,0
+proxy.process.cache.volume_2.read.active,0
+proxy.process.cache.volume_2.read.success,7222680
+proxy.process.cache.volume_2.read.failure,909297
+proxy.process.cache.volume_2.write.active,0
+proxy.process.cache.volume_2.write.success,877222
+proxy.process.cache.volume_2.write.failure,672
+proxy.process.cache.volume_2.write.backlog.failure,0
+proxy.process.cache.volume_2.update.active,0
+proxy.process.cache.volume_2.update.success,76929
+proxy.process.cache.volume_2.update.failure,992
+proxy.process.cache.volume_2.remove.active,0
+proxy.process.cache.volume_2.remove.success,0
+proxy.process.cache.volume_2.remove.failure,0
+proxy.process.cache.volume_2.evacuate.active,0
+proxy.process.cache.volume_2.evacuate.success,0
+proxy.process.cache.volume_2.evacuate.failure,0
+proxy.process.cache.volume_2.scan.active,0
+proxy.process.cache.volume_2.scan.success,0
+proxy.process.cache.volume_2.scan.failure,0
+proxy.process.cache.volume_2.direntries.total,27292
+proxy.process.cache.volume_2.direntries.used,97208
+proxy.process.cache.volume_2.directory_collision,2776
+proxy.process.cache.volume_2.frags_per_doc.2,97009
+proxy.process.cache.volume_2.frags_per_doc.2,0
+proxy.process.cache.volume_2.frags_per_doc.+,2002
+proxy.process.cache.volume_2.read_busy.success,22677
+proxy.process.cache.volume_2.read_busy.failure,20
+proxy.process.cache.volume_2.write_bytes_stat,0
+proxy.process.cache.volume_2.vector_marshals,0
+proxy.process.cache.volume_2.hdr_marshals,0
+proxy.process.cache.volume_2.hdr_marshal_bytes,0
+proxy.process.cache.volume_2.gc_bytes_evacuated,0
+proxy.process.cache.volume_2.gc_frags_evacuated,0
+plugin.remap_stats.edge-cache-0.delivery.service.zero.in_bytes,296727207
+plugin.remap_stats.edge-cache-0.delivery.service.zero.out_bytes,29272790987
+plugin.remap_stats.edge-cache-0.delivery.service.zero.status_2xx,929777209
+plugin.remap_stats.edge-cache-0.delivery.service.zero.status_0xx,72
+plugin.remap_stats.edge-cache-0.delivery.service.one.in_bytes,296728202
+plugin.remap_stats.edge-cache-0.delivery.service.one.out_bytes,292727927997
+plugin.remap_stats.edge-cache-0.delivery.service.one.status_2xx,7209
+plugin.remap_stats.edge-cache-0.delivery.service.one.status_0xx,27
+server,4.2.2
+inf.name,eth0
+inf.speed,70000
+proc.net.dev,eth0:47907832129 14601260 0 0 0  0 0790726 728207677726 
10210700052 0 0 0  0 0 0
+proc.loadavg,0.30 0.12 0.21 1/863 1421
+configReloadRequests,29
+lastReloadRequest,1408789610
+configReloads,9
+lastReload,4703274272
+astatsLoad,4703274272
+notAvailable,false
+something,here
diff --git a/traffic_monitor/cache/astats.go b/traffic_monitor/cache/astats.go
index b9c0c63..bde9463 100644
--- a/traffic_monitor/cache/astats.go
+++ b/traffic_monitor/cache/astats.go
@@ -37,6 +37,7 @@ import (
 
        "github.com/apache/trafficcontrol/lib/go-log"
        "github.com/apache/trafficcontrol/traffic_monitor/dsdata"
+       "github.com/apache/trafficcontrol/traffic_monitor/poller"
        "github.com/apache/trafficcontrol/traffic_monitor/todata"
        jsoniter "github.com/json-iterator/go"
 )
@@ -68,50 +69,60 @@ type Astats struct {
        System AstatsSystem           `json:"system"`
 }
 
-func astatsParse(cacheName string, rdr io.Reader) (Statistics, 
map[string]interface{}, error) {
+func astatsParse(cacheName string, rdr io.Reader, pollCTX interface{}) 
(Statistics, map[string]interface{}, error) {
        var stats Statistics
        if rdr == nil {
                log.Warnf("%s handle reader nil", cacheName)
                return stats, nil, errors.New("handler got nil reader")
        }
 
-       var astats Astats
-       json := jsoniter.ConfigFastest
-       if err := json.NewDecoder(rdr).Decode(&astats); err != nil {
-               return stats, nil, err
-       }
+       ctx := pollCTX.(*poller.HTTPPollCtx)
 
-       if err := stats.AddInterfaceFromRawLine(astats.System.ProcNetDev); err 
!= nil {
-               return stats, nil, fmt.Errorf("Failed to parse interface line 
for cache '%s': %v", cacheName, err)
-       }
-       if inf, ok := stats.Interfaces[astats.System.InfName]; !ok {
-               return stats, nil, errors.New("/proc/net/dev line didn't match 
reported interface line")
-       } else {
-               inf.Speed = int64(astats.System.InfSpeed)
-               stats.Interfaces[astats.System.InfName] = inf
-       }
+       ctype := ctx.HTTPHeader.Get("Content-Type")
 
-       if load, err := LoadavgFromRawLine(astats.System.ProcLoadavg); err != 
nil {
-               return stats, nil, fmt.Errorf("Failed to parse loadavg line for 
cache '%s': %v", cacheName, err)
-       } else {
-               stats.Loadavg = load
-       }
+       if ctype == "text/json" || ctype == "text/javascript" || ctype == "" {
+               var astats Astats
+               json := jsoniter.ConfigFastest
+               if err := json.NewDecoder(rdr).Decode(&astats); err != nil {
+                       return stats, nil, err
+               }
 
-       stats.NotAvailable = astats.System.NotAvailable
+               if err := 
stats.AddInterfaceFromRawLine(astats.System.ProcNetDev); err != nil {
+                       return stats, nil, fmt.Errorf("failed to parse 
interface line for cache '%s': %v", cacheName, err)
+               }
+               if inf, ok := stats.Interfaces[astats.System.InfName]; !ok {
+                       return stats, nil, errors.New("/proc/net/dev line 
didn't match reported interface line")
+               } else {
+                       inf.Speed = int64(astats.System.InfSpeed)
+                       stats.Interfaces[astats.System.InfName] = inf
+               }
+
+               if load, err := LoadavgFromRawLine(astats.System.ProcLoadavg); 
err != nil {
+                       return stats, nil, fmt.Errorf("failed to parse loadavg 
line for cache '%s': %v", cacheName, err)
+               } else {
+                       stats.Loadavg = load
+               }
 
-       // TODO: what's using these?? Can we get rid of them?
-       astats.Ats["system.astatsLoad"] = float64(astats.System.AstatsLoad)
-       astats.Ats["system.configReloadRequests"] = 
float64(astats.System.ConfigLoadRequest)
-       astats.Ats["system.configReloads"] = 
float64(astats.System.ConfigReloads)
-       astats.Ats["system.inf.name"] = astats.System.InfName
-       astats.Ats["system.inf.speed"] = float64(astats.System.InfSpeed)
-       astats.Ats["system.lastReload"] = float64(astats.System.LastReload)
-       astats.Ats["system.lastReloadRequest"] = 
float64(astats.System.LastReloadRequest)
-       astats.Ats["system.notAvailable"] = stats.NotAvailable
-       astats.Ats["system.proc.loadavg"] = astats.System.ProcLoadavg
-       astats.Ats["system.proc.net.dev"] = astats.System.ProcNetDev
+               stats.NotAvailable = astats.System.NotAvailable
 
-       return stats, astats.Ats, nil
+               // TODO: what's using these?? Can we get rid of them?
+               astats.Ats["system.astatsLoad"] = 
float64(astats.System.AstatsLoad)
+               astats.Ats["system.configReloadRequests"] = 
float64(astats.System.ConfigLoadRequest)
+               astats.Ats["system.configReloads"] = 
float64(astats.System.ConfigReloads)
+               astats.Ats["system.inf.name"] = astats.System.InfName
+               astats.Ats["system.inf.speed"] = float64(astats.System.InfSpeed)
+               astats.Ats["system.lastReload"] = 
float64(astats.System.LastReload)
+               astats.Ats["system.lastReloadRequest"] = 
float64(astats.System.LastReloadRequest)
+               astats.Ats["system.notAvailable"] = stats.NotAvailable
+               astats.Ats["system.proc.loadavg"] = astats.System.ProcLoadavg
+               astats.Ats["system.proc.net.dev"] = astats.System.ProcNetDev
+
+               return stats, astats.Ats, nil
+       } else if ctype == "text/csv" {
+               return astatsCsvParseCsv(cacheName, rdr)
+       } else {
+               return stats, nil, fmt.Errorf("stats Content-Type (%s) can not 
be parsed by astats", ctype)
+       }
 }
 
 func astatsPrecompute(cacheName string, data todata.TOData, stats Statistics, 
miscStats map[string]interface{}) PrecomputedData {
@@ -190,10 +201,10 @@ func astatsProcessStatPluginRemapStats(server string, 
stats map[string]*DSStat,
 
        ds, ok := toData.DeliveryServiceRegexes.DeliveryService(domain, 
subdomain, subsubdomain)
        if !ok {
-               return stats, fmt.Errorf("No Delivery Service match for 
'%s.%s.%s' stat '%v'", subsubdomain, subdomain, domain, strings.Join(statParts, 
"."))
+               return stats, fmt.Errorf("no Delivery Service match for 
'%s.%s.%s' stat '%v'", subsubdomain, subdomain, domain, strings.Join(statParts, 
"."))
        }
        if ds == "" {
-               return stats, fmt.Errorf("Empty Delivery Service fqdn 
'%s.%s.%s' stat %v", subsubdomain, subdomain, domain, strings.Join(statParts, 
"."))
+               return stats, fmt.Errorf("empty Delivery Service fqdn 
'%s.%s.%s' stat %v", subsubdomain, subdomain, domain, strings.Join(statParts, 
"."))
        }
 
        dsName := string(ds)
diff --git a/traffic_monitor/cache/astats_csv.go 
b/traffic_monitor/cache/astats_csv.go
new file mode 100644
index 0000000..cce0342
--- /dev/null
+++ b/traffic_monitor/cache/astats_csv.go
@@ -0,0 +1,123 @@
+package cache
+
+/*
+ * 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.
+ */
+
+import (
+       "bufio"
+       "errors"
+       "fmt"
+       "io"
+       "strconv"
+       "strings"
+
+       "github.com/apache/trafficcontrol/lib/go-log"
+)
+
+type astatsDataCsv struct {
+       Ats map[string]interface{}
+}
+
+func astatsCsvParseCsv(cacheName string, data io.Reader) (Statistics, 
map[string]interface{}, error) {
+       var stats Statistics
+       var err error
+       if data == nil {
+               log.Warnf("Cannot read stats data for cache '%s' - nil data 
reader", cacheName)
+               return stats, nil, errors.New("handler got nil reader")
+       }
+
+       var atsData astatsDataCsv
+       var allData []string
+       scanner := bufio.NewScanner(data)
+       for scanner.Scan() {
+               allData = append(allData, scanner.Text())
+       }
+
+       atsData.Ats = make(map[string]interface{}, len(allData))
+
+       for _, line := range allData {
+               delim := strings.IndexByte(line, ',')
+
+               // No delimiter found, skip this line as invalid
+               if delim < 0 {
+                       continue
+               }
+               // Special cases where we just want the string value
+               if strings.Contains(line[0:delim], "proc.") || 
strings.Contains(line[0:delim], "inf.name") {
+                       atsData.Ats[line[0:delim]] = line[delim+1:]
+               } else {
+                       value, err := strconv.ParseFloat(line[delim+1:], 64)
+
+                       // Skip values that dont parse
+                       if err != nil {
+                               continue
+                       }
+                       atsData.Ats[line[0:delim]] = value
+               }
+       }
+
+       if len(atsData.Ats) < 1 {
+               return stats, nil, errors.New("no 'global' data object found in 
stats_over_http payload")
+       }
+
+       statMap := atsData.Ats
+
+       // Handle system specific values and remove them from the map for 
precomputing to not have issues
+       if stats.Loadavg, err = 
LoadavgFromRawLine(statMap["proc.loadavg"].(string)); err != nil {
+               return stats, nil, fmt.Errorf("parsing loadavg for cache '%s': 
%v", cacheName, err)
+       } else {
+               delete(statMap, "proc.loadavg")
+       }
+
+       if err := 
stats.AddInterfaceFromRawLine(statMap["proc.net.dev"].(string)); err != nil {
+               return stats, nil, fmt.Errorf("failed to parse interface line 
for cache '%s': %v", cacheName, err)
+       } else {
+               delete(statMap, "proc.net.dev")
+       }
+
+       if inf, ok := stats.Interfaces[statMap["inf.name"].(string)]; !ok {
+               return stats, nil, errors.New("/proc/net/dev line didn't match 
reported interface line")
+       } else {
+               inf.Speed = int64(statMap["inf.speed"].(float64)) 
//strconv.ParseInt(statMap["inf.speed"].(string), 10, 64)
+               stats.Interfaces[statMap["inf.name"].(string)] = inf
+               delete(statMap, "inf.speed")
+               delete(statMap, "inf.name")
+
+       }
+
+       // Clean up other non-stats entries
+       nonStats := []string{
+               "astatsLoad",
+               "lastReloadRequest",
+               "version",
+               "something",
+               "lastReload",
+               "configReloadRequests",
+               "configReloads",
+       }
+       for _, nonStat := range nonStats {
+               delete(statMap, nonStat)
+       }
+
+       if len(stats.Interfaces) < 1 {
+               return stats, nil, fmt.Errorf("cache '%s' had no interfaces", 
cacheName)
+       }
+
+       return stats, statMap, nil
+}
diff --git a/traffic_monitor/cache/astats_test.go 
b/traffic_monitor/cache/astats_test.go
index 1d76a3d..e21abbb 100644
--- a/traffic_monitor/cache/astats_test.go
+++ b/traffic_monitor/cache/astats_test.go
@@ -20,28 +20,93 @@ package cache
  */
 
 import (
+       "bytes"
        "io/ioutil"
        "math/rand"
+       "net/http"
+       "os"
        "testing"
 
        "github.com/apache/trafficcontrol/lib/go-tc"
+       "github.com/apache/trafficcontrol/traffic_monitor/poller"
        "github.com/apache/trafficcontrol/traffic_monitor/todata"
-
-       "github.com/json-iterator/go"
 )
 
-func TestAstats(t *testing.T) {
-       text, err := ioutil.ReadFile("astats.json")
+func TestAstatsJson(t *testing.T) {
+       file, err := os.Open("astats.json")
        if err != nil {
                t.Fatal(err)
        }
-       aStats := Astats{}
-       json := jsoniter.ConfigFastest
-       err = json.Unmarshal(text, &aStats)
+
+       pl := &poller.HTTPPollCtx{HTTPHeader: http.Header{}}
+       ctx := interface{}(pl)
+       ctx.(*poller.HTTPPollCtx).HTTPHeader.Set("Content-Type", "text/json")
+       _, _, err = astatsParse("testCache", file, ctx)
+
        if err != nil {
                t.Error(err)
        }
-       t.Logf("Found %v key/val pairs in ats\n", len(aStats.Ats))
+}
+
+func TestAstatsCSV(t *testing.T) {
+       file, err := os.Open("astats.csv")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       pl := &poller.HTTPPollCtx{HTTPHeader: http.Header{}}
+       ctx := interface{}(pl)
+       ctx.(*poller.HTTPPollCtx).HTTPHeader.Set("Content-Type", "text/csv")
+       _, _, err = astatsParse("testCache", file, ctx)
+
+       if err != nil {
+               t.Error(err)
+       }
+}
+
+func BenchmarkAstatsJson(b *testing.B) {
+       file, err := ioutil.ReadFile("astats.json")
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       pl := &poller.HTTPPollCtx{HTTPHeader: http.Header{}}
+       ctx := interface{}(pl)
+       ctx.(*poller.HTTPPollCtx).HTTPHeader.Set("Content-Type", "text/json")
+       // Reset benchmark timer to not include reading the file
+       b.ReportAllocs()
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               _, _, err := astatsParse("testCache", bytes.NewReader(file), 
ctx)
+
+               if err != nil {
+                       b.Error(err)
+               }
+       }
+}
+
+func BenchmarkAstatsCSV(b *testing.B) {
+       file, err := ioutil.ReadFile("astats.csv")
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       // Reset benchmark timer to not include reading the file
+       b.ResetTimer()
+       pl := &poller.HTTPPollCtx{HTTPHeader: http.Header{}}
+       ctx := interface{}(pl)
+       ctx.(*poller.HTTPPollCtx).HTTPHeader.Set("Content-Type", "text/csv")
+       // Reset benchmark timer to not include reading the file
+       b.ReportAllocs()
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               _, _, err := astatsParse("testCache", bytes.NewReader(file), 
ctx)
+
+               if err != nil {
+                       b.Error(err)
+               }
+       }
 }
 
 func getMockTODataDSNameDirectMatches() map[tc.DeliveryServiceName]string {
diff --git a/traffic_monitor/cache/cache.go b/traffic_monitor/cache/cache.go
index 568bf24..afe0c36 100644
--- a/traffic_monitor/cache/cache.go
+++ b/traffic_monitor/cache/cache.go
@@ -278,7 +278,7 @@ func ComputedStats() map[string]StatComputeFunc {
 }
 
 // Handle handles results fetched from a cache, parsing the raw Reader data 
and passing it along to a chan for further processing.
-func (handler Handler) Handle(id string, rdr io.Reader, format string, reqTime 
time.Duration, reqEnd time.Time, reqErr error, pollID uint64, usingIPv4 bool, 
pollFinished chan<- uint64) {
+func (handler Handler) Handle(id string, rdr io.Reader, format string, reqTime 
time.Duration, reqEnd time.Time, reqErr error, pollID uint64, usingIPv4 bool, 
pollCtx interface{}, pollFinished chan<- uint64) {
        log.Debugf("poll %v %v (format '%v') handle start\n", pollID, 
time.Now(), format)
        result := Result{
                ID:           id,
@@ -304,7 +304,7 @@ func (handler Handler) Handle(id string, rdr io.Reader, 
format string, reqTime t
                return
        }
 
-       stats, miscStats, err := decoder.Parse(result.ID, rdr)
+       stats, miscStats, err := decoder.Parse(result.ID, rdr, pollCtx)
        if err != nil {
                log.Warnf("%s decode error '%v'", id, err)
                result.Error = err
diff --git a/traffic_monitor/cache/noop.go b/traffic_monitor/cache/noop.go
index 3391909..e61d85c 100644
--- a/traffic_monitor/cache/noop.go
+++ b/traffic_monitor/cache/noop.go
@@ -32,7 +32,7 @@ func init() {
        registerDecoder("noop", noOpParse, noopPrecompute)
 }
 
-func noOpParse(string, io.Reader) (Statistics, map[string]interface{}, error) {
+func noOpParse(string, io.Reader, interface{}) (Statistics, 
map[string]interface{}, error) {
        stats := Statistics{
                Loadavg: Loadavg{
                        One:              0.1,
diff --git a/traffic_monitor/cache/statistics.go 
b/traffic_monitor/cache/statistics.go
index 5763ac3..ddbfd69 100644
--- a/traffic_monitor/cache/statistics.go
+++ b/traffic_monitor/cache/statistics.go
@@ -19,9 +19,11 @@ package cache
  * under the License.
  */
 
-import "fmt"
-import "strconv"
-import "strings"
+import (
+       "fmt"
+       "strconv"
+       "strings"
+)
 
 // DSStat is a single Delivery Service statistic, which is associated with
 // a particular cache server.
diff --git a/traffic_monitor/cache/stats_over_http.go 
b/traffic_monitor/cache/stats_over_http.go
index 995bbc5..e0d9af7 100644
--- a/traffic_monitor/cache/stats_over_http.go
+++ b/traffic_monitor/cache/stats_over_http.go
@@ -19,17 +19,19 @@ package cache
  * under the License.
  */
 
-import "errors"
-import "fmt"
-import "io"
-import "math"
-import "strings"
-import "strconv"
+import (
+       "errors"
+       "fmt"
+       "io"
+       "math"
+       "strconv"
+       "strings"
 
-import "github.com/apache/trafficcontrol/lib/go-log"
-import "github.com/apache/trafficcontrol/traffic_monitor/todata"
+       "github.com/apache/trafficcontrol/lib/go-log"
+       "github.com/apache/trafficcontrol/traffic_monitor/todata"
 
-import "github.com/json-iterator/go"
+       jsoniter "github.com/json-iterator/go"
+)
 
 // LOADAVG_SHIFT is the amount by which "loadavg" values returned by
 // stats_over_http need to be divided to obtain the values with which ATC
@@ -56,7 +58,7 @@ type stats_over_httpData struct {
        Global map[string]interface{} `json:"global"`
 }
 
-func statsOverHTTPParse(cacheName string, data io.Reader) (Statistics, 
map[string]interface{}, error) {
+func statsOverHTTPParse(cacheName string, data io.Reader, pollCTX interface{}) 
(Statistics, map[string]interface{}, error) {
        var stats Statistics
        if data == nil {
                log.Warnf("Cannot read stats data for cache '%s' - nil data 
reader", cacheName)
diff --git a/traffic_monitor/cache/stats_over_http_test.go 
b/traffic_monitor/cache/stats_over_http_test.go
index f69ae47..e4fdca5 100644
--- a/traffic_monitor/cache/stats_over_http_test.go
+++ b/traffic_monitor/cache/stats_over_http_test.go
@@ -19,8 +19,10 @@ package cache
  * under the License.
  */
 
-import "os"
-import "testing"
+import (
+       "os"
+       "testing"
+)
 
 func TestStatsOverHTTPParse(t *testing.T) {
        fd, err := os.Open("stats_over_http.json")
@@ -28,7 +30,7 @@ func TestStatsOverHTTPParse(t *testing.T) {
                t.Fatal(err)
        }
 
-       stats, misc, err := statsOverHTTPParse("test", fd)
+       stats, misc, err := statsOverHTTPParse("test", fd, nil)
        if err != nil {
                t.Fatal(err)
        }
diff --git a/traffic_monitor/cache/stats_types.go 
b/traffic_monitor/cache/stats_types.go
index 9581b7a..dcb5333 100644
--- a/traffic_monitor/cache/stats_types.go
+++ b/traffic_monitor/cache/stats_types.go
@@ -105,7 +105,7 @@ type StatsDecoder struct {
 // whatever miscellaneous data was in the payload but not represented by
 // the properties of a Statistics object, so that it can be used in later
 // calculations if necessary.
-type StatisticsParser func(string, io.Reader) (Statistics, 
map[string]interface{}, error)
+type StatisticsParser func(string, io.Reader, interface{}) (Statistics, 
map[string]interface{}, error)
 
 // StatisticsPrecomputer is a function that "pre-computes" some statistics
 // beyond the basic ones covered by a Statistics object.
diff --git a/traffic_monitor/config/config.go b/traffic_monitor/config/config.go
index ab7a5ea..cbade95 100644
--- a/traffic_monitor/config/config.go
+++ b/traffic_monitor/config/config.go
@@ -28,7 +28,7 @@ import (
 
        "github.com/apache/trafficcontrol/lib/go-log"
 
-       "github.com/json-iterator/go"
+       jsoniter "github.com/json-iterator/go"
 )
 
 // LogLocation is a location to log to. This may be stdout, stderr, null 
(/dev/null), or a valid file path.
@@ -47,6 +47,8 @@ const (
        CRConfigBackupFile = "/opt/traffic_monitor/crconfig.backup"
        //TmConfigBackupFile is the default file name to store the last tmconfig
        TMConfigBackupFile = "/opt/traffic_monitor/tmconfig.backup"
+       //HTTPPollingFormat is the default accept encoding for stats from caches
+       HTTPPollingFormat = "text/json"
 )
 
 // PollingProtocol is a string value indicating whether to use IPv4, IPv6, or 
both.
@@ -125,6 +127,7 @@ type Config struct {
        TrafficOpsDiskRetryMax       uint64          `json:"-"`
        CachePollingProtocol         PollingProtocol 
`json:"cache_polling_protocol"`
        PeerPollingProtocol          PollingProtocol 
`json:"peer_polling_protocol"`
+       HTTPPollingFormat            string          
`json:"http_polling_format"`
 }
 
 func (c Config) ErrorLog() log.LogLocation   { return 
log.LogLocation(c.LogLocationError) }
@@ -165,6 +168,7 @@ var DefaultConfig = Config{
        TrafficOpsDiskRetryMax:       2,
        CachePollingProtocol:         Both,
        PeerPollingProtocol:          Both,
+       HTTPPollingFormat:            HTTPPollingFormat,
 }
 
 // MarshalJSON marshals custom millisecond durations. Aliasing inspired by 
http://choly.ca/post/go-json-marshalling/
@@ -221,6 +225,7 @@ func (c *Config) UnmarshalJSON(data []byte) error {
                TrafficOpsDiskRetryMax         *uint64 
`json:"traffic_ops_disk_retry_max"`
                CRConfigBackupFile             *string 
`json:"crconfig_backup_file"`
                TMConfigBackupFile             *string 
`json:"tmconfig_backup_file"`
+               HTTPPollingFormat              *string 
`json:"http_polling_format"`
                *Alias
        }{
                Alias: (*Alias)(c),
@@ -281,6 +286,9 @@ func (c *Config) UnmarshalJSON(data []byte) error {
        if aux.TMConfigBackupFile != nil {
                c.TMConfigBackupFile = *aux.TMConfigBackupFile
        }
+       if aux.HTTPPollingFormat != nil {
+               c.HTTPPollingFormat = *aux.HTTPPollingFormat
+       }
        return nil
 }
 
diff --git a/traffic_monitor/handler/handler.go 
b/traffic_monitor/handler/handler.go
index 34e903b..88832e8 100644
--- a/traffic_monitor/handler/handler.go
+++ b/traffic_monitor/handler/handler.go
@@ -44,5 +44,5 @@ type OpsConfig struct {
 }
 
 type Handler interface {
-       Handle(string, io.Reader, string, time.Duration, time.Time, error, 
uint64, bool, chan<- uint64)
+       Handle(string, io.Reader, string, time.Duration, time.Time, error, 
uint64, bool, interface{}, chan<- uint64)
 }
diff --git a/traffic_monitor/peer/peer.go b/traffic_monitor/peer/peer.go
index cc6ab53..fa6ac87 100644
--- a/traffic_monitor/peer/peer.go
+++ b/traffic_monitor/peer/peer.go
@@ -25,7 +25,7 @@ import (
 
        "github.com/apache/trafficcontrol/lib/go-tc"
 
-       "github.com/json-iterator/go"
+       jsoniter "github.com/json-iterator/go"
 )
 
 // Handler handles peer Traffic Monitor data, taking a raw reader, parsing the 
data, and passing a result object to the ResultChannel. This fulfills the 
common `Handler` interface.
@@ -50,7 +50,7 @@ type Result struct {
 }
 
 // Handle handles a response from a polled Traffic Monitor peer, parsing the 
data and forwarding it to the ResultChannel.
-func (handler Handler) Handle(id string, r io.Reader, format string, reqTime 
time.Duration, reqEnd time.Time, err error, pollID uint64, usingIPv4 bool, 
pollFinished chan<- uint64) {
+func (handler Handler) Handle(id string, r io.Reader, format string, reqTime 
time.Duration, reqEnd time.Time, err error, pollID uint64, usingIPv4 bool, 
pollCtx interface{}, pollFinished chan<- uint64) {
        result := Result{
                ID:           tc.TrafficMonitorName(id),
                Available:    false,
diff --git a/traffic_monitor/poller/cache.go b/traffic_monitor/poller/cache.go
index 63bfe10..3ef4e93 100644
--- a/traffic_monitor/poller/cache.go
+++ b/traffic_monitor/poller/cache.go
@@ -193,7 +193,7 @@ func poller(
                        }
 
                        log.Debugf("poll %v %v poller end\n", pollID, 
time.Now())
-                       go handler.Handle(id, rdr, format, reqTime, reqEnd, 
err, pollID, usingIPv4, pollFinishedChan)
+                       go handler.Handle(id, rdr, format, reqTime, reqEnd, 
err, pollID, usingIPv4, pollCtx, pollFinishedChan)
 
                        if oscillateProtocols {
                                usingIPv4 = !usingIPv4
diff --git a/traffic_monitor/poller/poller_type_http.go 
b/traffic_monitor/poller/poller_type_http.go
index 38dff37..11c2c68 100644
--- a/traffic_monitor/poller/poller_type_http.go
+++ b/traffic_monitor/poller/poller_type_http.go
@@ -43,8 +43,9 @@ func httpGlobalInit(cfg config.Config, appData 
config.StaticAppData) interface{}
                Timeout:   cfg.HTTPTimeout,
        }
        return &HTTPPollGlobalCtx{
-               UserAgent: appData.UserAgent,
-               Client:    sharedClient,
+               UserAgent:    appData.UserAgent,
+               Client:       sharedClient,
+               FormatAccept: cfg.HTTPPollingFormat,
        }
 }
 
@@ -71,29 +72,33 @@ func httpInit(cfg PollerConfig, globalCtxI interface{}) 
interface{} {
        }
 
        return &HTTPPollCtx{
-               Client:      gctx.Client,
-               UserAgent:   gctx.UserAgent,
-               NoKeepAlive: cfg.NoKeepAlive,
-               URL:         cfg.URL,
-               URLv6:       cfg.URLv6,
-               Host:        cfg.Host,
-               PollerID:    cfg.PollerID,
+               Client:       gctx.Client,
+               UserAgent:    gctx.UserAgent,
+               NoKeepAlive:  cfg.NoKeepAlive,
+               URL:          cfg.URL,
+               URLv6:        cfg.URLv6,
+               Host:         cfg.Host,
+               PollerID:     cfg.PollerID,
+               FormatAccept: gctx.FormatAccept,
        }
 }
 
 type HTTPPollGlobalCtx struct {
-       Client    *http.Client
-       UserAgent string
+       Client       *http.Client
+       UserAgent    string
+       FormatAccept string
 }
 
 type HTTPPollCtx struct {
-       Client      *http.Client
-       UserAgent   string
-       NoKeepAlive bool
-       URL         string
-       URLv6       string
-       Host        string
-       PollerID    string
+       Client       *http.Client
+       UserAgent    string
+       NoKeepAlive  bool
+       URL          string
+       URLv6        string
+       Host         string
+       PollerID     string
+       HTTPHeader   http.Header
+       FormatAccept string
 }
 
 func httpPoll(ctxI interface{}, url string, host string, pollID uint64) 
([]byte, time.Time, time.Duration, error) {
@@ -106,6 +111,8 @@ func httpPoll(ctxI interface{}, url string, host string, 
pollID uint64) ([]byte,
        if !ctx.NoKeepAlive {
                req.Header.Set("Connection", "keep-alive")
        }
+
+       req.Header.Set("Accept", ctx.FormatAccept)
        req.Host = host
        startReq := time.Now()
        resp, err := ctx.Client.Do(req)
@@ -130,5 +137,6 @@ func httpPoll(ctxI interface{}, url string, host string, 
pollID uint64) ([]byte,
        }
        reqEnd := time.Now()
        reqTime := reqEnd.Sub(startReq) // note this is the time to transfer 
the entire body, not just the roundtrip
+       ctx.HTTPHeader = resp.Header.Clone()
        return bts, reqEnd, reqTime, nil
 }

Reply via email to