This is an automated email from the ASF dual-hosted git repository.
masaori pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 0d2ad23 Add slow logs for HTTP/2 Connection & Stream
0d2ad23 is described below
commit 0d2ad23921061c4117104b64d15bc17cae79c9fe
Author: Masaori Koshiba <[email protected]>
AuthorDate: Wed Jul 24 14:48:09 2019 +0900
Add slow logs for HTTP/2 Connection & Stream
---
doc/admin-guide/files/records.config.en.rst | 20 ++++++++++
mgmt/RecordsConfig.cc | 4 ++
proxy/Milestones.h | 59 +++++++++++++++++++----------
proxy/http2/HTTP2.cc | 4 ++
proxy/http2/HTTP2.h | 2 +
proxy/http2/Http2ClientSession.cc | 11 ++++++
proxy/http2/Http2ClientSession.h | 8 ++++
proxy/http2/Http2ConnectionState.cc | 6 +++
proxy/http2/Http2Stream.cc | 42 ++++++++++++++++++--
proxy/http2/Http2Stream.h | 27 ++++++++++---
10 files changed, 154 insertions(+), 29 deletions(-)
diff --git a/doc/admin-guide/files/records.config.en.rst
b/doc/admin-guide/files/records.config.en.rst
index 243d03a..65f27f0 100644
--- a/doc/admin-guide/files/records.config.en.rst
+++ b/doc/admin-guide/files/records.config.en.rst
@@ -2852,6 +2852,26 @@ Logging Configuration
completion will cause its timing stats to be written to the
:ts:cv:`debugging log file
<proxy.config.output.logfile>`. This is identifying data about the
transaction and all of the :c:type:`transaction milestones <TSMilestonesType>`.
+.. ts:cv:: CONFIG proxy.config.http2.connection.slow.log.threshold INT 0
+ :reloadable:
+ :units: milliseconds
+
+ If set to a non-zero value :arg:`N` then any HTTP/2 connection
+ that takes longer than :arg:`N` milliseconds from open to close will cause
+ its timing stats to be written to the :ts:cv:`debugging log file
+ <proxy.config.output.logfile>`. This is identifying data about the
+ transaction and all of the :c:type:`transaction milestones
<TSMilestonesType>`.
+
+.. ts:cv:: CONFIG proxy.config.http2.stream.slow.log.threshold INT 0
+ :reloadable:
+ :units: milliseconds
+
+ If set to a non-zero value :arg:`N` then any HTTP/2 stream
+ that takes longer than :arg:`N` milliseconds from open to close will cause
+ its timing stats to be written to the :ts:cv:`debugging log file
+ <proxy.config.output.logfile>`. This is identifying data about the
+ transaction and all of the :c:type:`transaction milestones
<TSMilestonesType>`.
+
.. ts:cv:: CONFIG proxy.config.log.config.filename STRING logging.yaml
:reloadable:
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index 3058a2a..a0891df 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -645,6 +645,10 @@ static const RecordElement RecordsConfig[] =
,
{RECT_CONFIG, "proxy.config.http.slow.log.threshold", RECD_INT, "0",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
,
+ {RECT_CONFIG, "proxy.config.http2.connection.slow.log.threshold", RECD_INT,
"0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ ,
+ {RECT_CONFIG, "proxy.config.http2.stream.slow.log.threshold", RECD_INT, "0",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ ,
//##############################################################################
//#
diff --git a/proxy/Milestones.h b/proxy/Milestones.h
index d429e28..3170c0f 100644
--- a/proxy/Milestones.h
+++ b/proxy/Milestones.h
@@ -1,6 +1,6 @@
/** @file
- A brief file description
+ Milestones
@section license License
@@ -23,22 +23,32 @@
#pragma once
-#include "tscore/ink_platform.h"
-#include "tscore/ink_hrtime.h"
#include "ts/apidefs.h"
+
+#include "tscore/ink_platform.h"
#include "tscore/ink_hrtime.h"
-/////////////////////////////////////////////////////////////
-//
-// class TransactionMilestones
-//
-/////////////////////////////////////////////////////////////
-class TransactionMilestones
+#include "I_EventSystem.h"
+
+template <class T, size_t entries> class Milestones
{
public:
- TransactionMilestones() { ink_zero(milestones); }
- ink_hrtime &operator[](TSMilestonesType ms) { return milestones[ms]; }
- ink_hrtime operator[](TSMilestonesType ms) const { return milestones[ms]; }
+ ink_hrtime &operator[](T ms) { return
this->_milestones[static_cast<size_t>(ms)]; }
+ ink_hrtime operator[](T ms) const { return
this->_milestones[static_cast<size_t>(ms)]; }
+
+ /**
+ * Mark given milestone with timestamp if it's not marked yet
+ * @param ms The milestone to mark
+ * @return N/A
+ */
+ void
+ mark(T ms)
+ {
+ if (this->_milestones[static_cast<size_t>(ms)] == 0) {
+ this->_milestones[static_cast<size_t>(ms)] = Thread::get_hrtime();
+ }
+ }
+
/**
* Takes two milestones and returns the difference.
* @param start The start time
@@ -46,12 +56,12 @@ public:
* @return The difference time in milliseconds
*/
int64_t
- difference_msec(TSMilestonesType ms_start, TSMilestonesType ms_end) const
+ difference_msec(T ms_start, T ms_end) const
{
- if (milestones[ms_end] == 0) {
+ if (this->_milestones[static_cast<size_t>(ms_end)] == 0) {
return -1;
}
- return ink_hrtime_to_msec(milestones[ms_end] - milestones[ms_start]);
+ return ink_hrtime_to_msec(this->_milestones[static_cast<size_t>(ms_end)] -
this->_milestones[static_cast<size_t>(ms_start)]);
}
/**
@@ -61,17 +71,26 @@ public:
* @return A double that is the difference time in seconds
*/
double
- difference_sec(TSMilestonesType ms_start, TSMilestonesType ms_end) const
+ difference_sec(T ms_start, T ms_end) const
{
- return (double)difference_msec(ms_start, ms_end) / 1000.0;
+ return static_cast<double>(difference_msec(ms_start, ms_end) / 1000.0);
}
+ /**
+ * Takes two milestones and returns the difference.
+ * @param start The start time
+ * @param end The end time
+ * @return The difference time in high-resolution time
+ */
ink_hrtime
- elapsed(TSMilestonesType ms_start, TSMilestonesType ms_end) const
+ elapsed(T ms_start, T ms_end) const
{
- return milestones[ms_end] - milestones[ms_start];
+ return this->_milestones[static_cast<size_t>(ms_end)] -
this->_milestones[static_cast<size_t>(ms_start)];
}
private:
- ink_hrtime milestones[TS_MILESTONE_LAST_ENTRY];
+ std::array<ink_hrtime, entries> _milestones = {0};
};
+
+// For compatibility with HttpSM.h and HttpTransact.h
+using TransactionMilestones = Milestones<TSMilestonesType,
TS_MILESTONE_LAST_ENTRY>;
diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc
index a53769b..535f3a3 100644
--- a/proxy/http2/HTTP2.cc
+++ b/proxy/http2/HTTP2.cc
@@ -745,6 +745,8 @@ uint32_t Http2::zombie_timeout_in = 0;
float Http2::stream_error_rate_threshold = 0.1;
uint32_t Http2::max_settings_per_frame = 7;
uint32_t Http2::max_settings_per_minute = 14;
+uint32_t Http2::con_slow_log_threshold = 0;
+uint32_t Http2::stream_slow_log_threshold = 0;
void
Http2::init()
@@ -765,6 +767,8 @@ Http2::init()
REC_EstablishStaticConfigFloat(stream_error_rate_threshold,
"proxy.config.http2.stream_error_rate_threshold");
REC_EstablishStaticConfigInt32U(max_settings_per_frame,
"proxy.config.http2.max_settings_per_frame");
REC_EstablishStaticConfigInt32U(max_settings_per_minute,
"proxy.config.http2.max_settings_per_minute");
+ REC_EstablishStaticConfigInt32U(con_slow_log_threshold,
"proxy.config.http2.connection.slow.log.threshold");
+ REC_EstablishStaticConfigInt32U(stream_slow_log_threshold,
"proxy.config.http2.stream.slow.log.threshold");
// If any settings is broken, ATS should not start
ink_release_assert(http2_settings_parameter_is_valid({HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
max_concurrent_streams_in}));
diff --git a/proxy/http2/HTTP2.h b/proxy/http2/HTTP2.h
index 63fe9aa..325ebf3 100644
--- a/proxy/http2/HTTP2.h
+++ b/proxy/http2/HTTP2.h
@@ -378,6 +378,8 @@ public:
static float stream_error_rate_threshold;
static uint32_t max_settings_per_frame;
static uint32_t max_settings_per_minute;
+ static uint32_t con_slow_log_threshold;
+ static uint32_t stream_slow_log_threshold;
static void init();
};
diff --git a/proxy/http2/Http2ClientSession.cc
b/proxy/http2/Http2ClientSession.cc
index e033ef5..3e2a8bc 100644
--- a/proxy/http2/Http2ClientSession.cc
+++ b/proxy/http2/Http2ClientSession.cc
@@ -98,6 +98,16 @@ Http2ClientSession::free()
REMEMBER(NO_EVENT, this->recursion)
Http2SsnDebug("session free");
+ this->_milestones.mark(Http2SsnMilestone::CLOSE);
+ ink_hrtime total_time = this->_milestones.elapsed(Http2SsnMilestone::OPEN,
Http2SsnMilestone::CLOSE);
+
+ // Slow Log
+ if (Http2::con_slow_log_threshold != 0 &&
ink_hrtime_from_msec(Http2::con_slow_log_threshold) < total_time) {
+ Error("[%" PRIu64 "] Slow H2 Connection: open: %" PRIu64 " close: %.3f",
this->con_id,
+ ink_hrtime_to_msec(this->_milestones[Http2SsnMilestone::OPEN]),
+ this->_milestones.difference_sec(Http2SsnMilestone::OPEN,
Http2SsnMilestone::CLOSE));
+ }
+
HTTP2_DECREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT,
this->mutex->thread_holding);
// Update stats on how we died. May want to eliminate this. Was useful for
@@ -177,6 +187,7 @@ Http2ClientSession::new_connection(NetVConnection *new_vc,
MIOBuffer *iobuf, IOB
ink_assert(new_vc->mutex->thread_holding == this_ethread());
HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT,
new_vc->mutex->thread_holding);
HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_CONNECTION_COUNT,
new_vc->mutex->thread_holding);
+ this->_milestones.mark(Http2SsnMilestone::OPEN);
// Unique client session identifier.
this->con_id = ProxySession::next_connection_id();
diff --git a/proxy/http2/Http2ClientSession.h b/proxy/http2/Http2ClientSession.h
index 257f418..8884022 100644
--- a/proxy/http2/Http2ClientSession.h
+++ b/proxy/http2/Http2ClientSession.h
@@ -30,6 +30,7 @@
#include <string_view>
#include "tscore/ink_inet.h"
#include "tscore/History.h"
+#include "Milestones.h"
// Name Edata Description
// HTTP2_SESSION_EVENT_INIT Http2ClientSession * HTTP/2 session is born
@@ -49,6 +50,12 @@ enum class Http2SessionCod : int {
HIGH_ERROR_RATE,
};
+enum class Http2SsnMilestone {
+ OPEN = 0,
+ CLOSE,
+ LAST_ENTRY,
+};
+
size_t const HTTP2_HEADER_BUFFER_SIZE_INDEX =
CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX;
// To support Upgrade: h2c
@@ -334,6 +341,7 @@ private:
IpEndpoint cached_local_addr;
History<HISTORY_DEFAULT_SIZE> _history;
+ Milestones<Http2SsnMilestone,
static_cast<size_t>(Http2SsnMilestone::LAST_ENTRY)> _milestones;
// For Upgrade: h2c
Http2UpgradeContext upgrade_context;
diff --git a/proxy/http2/Http2ConnectionState.cc
b/proxy/http2/Http2ConnectionState.cc
index 9d73d8c..7ed5186 100644
--- a/proxy/http2/Http2ConnectionState.cc
+++ b/proxy/http2/Http2ConnectionState.cc
@@ -336,6 +336,7 @@ rcv_headers_frame(Http2ConnectionState &cstate, const
Http2Frame &frame)
empty_request = true;
}
+ stream->mark_milestone(Http2StreamMilestone::START_DECODE_HEADERS);
Http2ErrorCode result =
stream->decode_header_blocks(*cstate.local_hpack_handle,
cstate.server_settings.get(HTTP2_SETTINGS_HEADER_TABLE_SIZE));
@@ -355,6 +356,7 @@ rcv_headers_frame(Http2ConnectionState &cstate, const
Http2Frame &frame)
// Set up the State Machine
if (!empty_request) {
SCOPED_MUTEX_LOCK(stream_lock, stream->mutex, this_ethread());
+ stream->mark_milestone(Http2StreamMilestone::START_TXN);
stream->new_transaction();
// Send request header to SM
stream->send_request(cstate);
@@ -864,6 +866,7 @@ rcv_continuation_frame(Http2ConnectionState &cstate, const
Http2Frame &frame)
// Set up the State Machine
SCOPED_MUTEX_LOCK(stream_lock, stream->mutex, this_ethread());
+ stream->mark_milestone(Http2StreamMilestone::START_TXN);
stream->new_transaction();
// Send request header to SM
stream->send_request(cstate);
@@ -1520,6 +1523,8 @@ Http2ConnectionState::send_headers_frame(Http2Stream
*stream)
h2_hdr.destroy();
return;
}
+
+ stream->mark_milestone(Http2StreamMilestone::START_ENCODE_HEADERS);
Http2ErrorCode result = http2_encode_header_blocks(&h2_hdr, buf, buf_len,
&header_blocks_size, *(this->remote_hpack_handle),
client_settings.get(HTTP2_SETTINGS_HEADER_TABLE_SIZE));
if (result != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) {
@@ -1536,6 +1541,7 @@ Http2ConnectionState::send_headers_frame(Http2Stream
*stream)
flags |= HTTP2_FLAGS_HEADERS_END_STREAM;
stream->send_end_stream = true;
}
+ stream->mark_milestone(Http2StreamMilestone::START_TX_HEADERS_FRAMES);
} else {
payload_length =
BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_HEADERS]);
}
diff --git a/proxy/http2/Http2Stream.cc b/proxy/http2/Http2Stream.cc
index 09877f2..3bcbfe7 100644
--- a/proxy/http2/Http2Stream.cc
+++ b/proxy/http2/Http2Stream.cc
@@ -172,6 +172,7 @@ Http2Stream::send_request(Http2ConnectionState &cstate)
// Is there a read_vio request waiting?
this->update_read_request(INT64_MAX, true);
+ this->_http_sm_id = this->current_reader->sm_id;
}
bool
@@ -627,7 +628,7 @@ Http2Stream::update_write_request(IOBufferReader
*buf_reader, int64_t write_len,
if (is_done) {
this->mark_body_done();
}
-
+ this->_milestones.mark(Http2StreamMilestone::START_TX_DATA_FRAMES);
this->send_response_body(call_update);
}
break;
@@ -642,7 +643,7 @@ Http2Stream::update_write_request(IOBufferReader
*buf_reader, int64_t write_len,
if (write_vio.ntodo() == bytes_avail || is_done) {
this->mark_body_done();
}
-
+ this->_milestones.mark(Http2StreamMilestone::START_TX_DATA_FRAMES);
this->send_response_body(call_update);
}
@@ -725,6 +726,8 @@ Http2Stream::destroy()
ink_release_assert(this->closed);
ink_release_assert(reentrancy_count == 0);
+ uint64_t cid = 0;
+
// Safe to initiate SSN_CLOSE if this is the last stream
if (proxy_ssn) {
Http2ClientSession *h2_proxy_ssn = static_cast<Http2ClientSession
*>(proxy_ssn);
@@ -735,13 +738,38 @@ Http2Stream::destroy()
// Update session's stream counts, so it accurately goes into keep-alive
state
h2_proxy_ssn->connection_state.release_stream(this);
+
+ cid = proxy_ssn->connection_id();
}
// Clean up the write VIO in case of inactivity timeout
this->do_io_write(nullptr, 0, nullptr);
- ink_hrtime end_time = Thread::get_hrtime();
- HTTP2_SUM_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_TRANSACTIONS_TIME, _thread,
end_time - _start_time);
+ this->_milestones.mark(Http2StreamMilestone::CLOSE);
+
+ ink_hrtime total_time =
this->_milestones.elapsed(Http2StreamMilestone::OPEN,
Http2StreamMilestone::CLOSE);
+ HTTP2_SUM_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_TRANSACTIONS_TIME, this->_thread,
total_time);
+
+ // Slow Log
+ if (Http2::stream_slow_log_threshold != 0 &&
ink_hrtime_from_msec(Http2::stream_slow_log_threshold) < total_time) {
+ Error("[%" PRIu64 "] [%" PRIu32 "] [%" PRId64 "] Slow H2 Stream: "
+ "open: %" PRIu64 " "
+ "dec_hdrs: %.3f "
+ "txn: %.3f "
+ "enc_hdrs: %.3f "
+ "tx_hdrs: %.3f "
+ "tx_data: %.3f "
+ "close: %.3f",
+ cid, static_cast<uint32_t>(this->_id), this->_http_sm_id,
+ ink_hrtime_to_msec(this->_milestones[Http2StreamMilestone::OPEN]),
+ this->_milestones.difference_sec(Http2StreamMilestone::OPEN,
Http2StreamMilestone::START_DECODE_HEADERS),
+ this->_milestones.difference_sec(Http2StreamMilestone::OPEN,
Http2StreamMilestone::START_TXN),
+ this->_milestones.difference_sec(Http2StreamMilestone::OPEN,
Http2StreamMilestone::START_ENCODE_HEADERS),
+ this->_milestones.difference_sec(Http2StreamMilestone::OPEN,
Http2StreamMilestone::START_TX_HEADERS_FRAMES),
+ this->_milestones.difference_sec(Http2StreamMilestone::OPEN,
Http2StreamMilestone::START_TX_DATA_FRAMES),
+ this->_milestones.difference_sec(Http2StreamMilestone::OPEN,
Http2StreamMilestone::CLOSE));
+ }
+
_req_header.destroy();
response_header.destroy();
@@ -907,6 +935,12 @@ Http2Stream::decrement_client_transactions_stat()
HTTP2_DECREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT,
_thread);
}
+void
+Http2Stream::mark_milestone(Http2StreamMilestone type)
+{
+ this->_milestones.mark(type);
+}
+
bool
Http2Stream::_switch_thread_if_not_on_right_thread(int event, void *edata)
{
diff --git a/proxy/http2/Http2Stream.h b/proxy/http2/Http2Stream.h
index 1e159da..8d1c8e7 100644
--- a/proxy/http2/Http2Stream.h
+++ b/proxy/http2/Http2Stream.h
@@ -29,12 +29,24 @@
#include "../http/HttpTunnel.h" // To get ChunkedHandler
#include "Http2DependencyTree.h"
#include "tscore/History.h"
+#include "Milestones.h"
class Http2Stream;
class Http2ConnectionState;
typedef Http2DependencyTree::Tree<Http2Stream *> DependencyTree;
+enum class Http2StreamMilestone {
+ OPEN = 0,
+ START_DECODE_HEADERS,
+ START_TXN,
+ START_ENCODE_HEADERS,
+ START_TX_HEADERS_FRAMES,
+ START_TX_DATA_FRAMES,
+ CLOSE,
+ LAST_ENTRY,
+};
+
class Http2Stream : public ProxyTransaction
{
public:
@@ -47,10 +59,12 @@ public:
void
init(Http2StreamId sid, ssize_t initial_rwnd)
{
- _id = sid;
- _start_time = Thread::get_hrtime();
- _thread = this_ethread();
+ this->mark_milestone(Http2StreamMilestone::OPEN);
+
+ this->_id = sid;
+ this->_thread = this_ethread();
this->client_rwnd = initial_rwnd;
+
sm_reader = request_reader = request_buffer.alloc_reader();
// FIXME: Are you sure? every "stream" needs request_header?
_req_header.create(HTTP_TYPE_REQUEST);
@@ -223,6 +237,8 @@ public:
void increment_client_transactions_stat() override;
void decrement_client_transactions_stat() override;
+ void mark_milestone(Http2StreamMilestone type);
+
private:
void response_initialize_data_handling(bool &is_done);
void response_process_data(bool &is_done);
@@ -238,16 +254,17 @@ private:
bool _switch_thread_if_not_on_right_thread(int event, void *edata);
HTTPParser http_parser;
- ink_hrtime _start_time = 0;
- EThread *_thread = nullptr;
+ EThread *_thread = nullptr;
Http2StreamId _id;
Http2StreamState _state = Http2StreamState::HTTP2_STREAM_STATE_IDLE;
+ int64_t _http_sm_id = -1;
HTTPHdr _req_header;
VIO read_vio;
VIO write_vio;
History<HISTORY_DEFAULT_SIZE> _history;
+ Milestones<Http2StreamMilestone,
static_cast<size_t>(Http2StreamMilestone::LAST_ENTRY)> _milestones;
bool trailing_header = false;
bool body_done = false;