Skip to content

Commit 57df92f

Browse files
authored
Merge pull request #10141 from Icinga/log-slow-http-rpc-processing
Log HTTP/RPC message processing stats
2 parents 4b884ea + 4564c06 commit 57df92f

File tree

3 files changed

+71
-15
lines changed

3 files changed

+71
-15
lines changed

lib/remote/httpserverconnection.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,13 +433,17 @@ bool ProcessRequest(
433433
boost::beast::http::response<boost::beast::http::string_body>& response,
434434
HttpServerConnection& server,
435435
bool& hasStartedStreaming,
436+
std::chrono::steady_clock::duration& cpuBoundWorkTime,
436437
boost::asio::yield_context& yc
437438
)
438439
{
439440
namespace http = boost::beast::http;
440441

441442
try {
443+
// Cache the elapsed time to acquire a CPU semaphore used to detect extremely heavy workloads.
444+
auto start (std::chrono::steady_clock::now());
442445
CpuBoundWork handlingRequest (yc);
446+
cpuBoundWorkTime = std::chrono::steady_clock::now() - start;
443447

444448
HttpHandler::ProcessRequest(stream, authenticatedUser, request, response, yc, server);
445449
} catch (const std::exception& ex) {
@@ -530,9 +534,14 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
530534
<< ", user: " << (authenticatedUser ? authenticatedUser->GetName() : "<unauthenticated>")
531535
<< ", agent: " << request[http::field::user_agent]; //operator[] - Returns the value for a field, or "" if it does not exist.
532536

533-
Defer addRespCode ([&response, start, &logMsg]() {
534-
logMsg << ", status: " << response.result() << ") took "
535-
<< ch::duration_cast<ch::milliseconds>(ch::steady_clock::now() - start).count() << "ms.";
537+
ch::steady_clock::duration cpuBoundWorkTime(0);
538+
Defer addRespCode ([&response, start, &logMsg, &cpuBoundWorkTime]() {
539+
logMsg << ", status: " << response.result() << ")";
540+
if (cpuBoundWorkTime >= ch::seconds(1)) {
541+
logMsg << " waited " << ch::duration_cast<ch::milliseconds>(cpuBoundWorkTime).count() << "ms on semaphore and";
542+
}
543+
544+
logMsg << " took total " << ch::duration_cast<ch::milliseconds>(ch::steady_clock::now() - start).count() << "ms.";
536545
});
537546

538547
if (!HandleAccessControl(*m_Stream, request, response, yc)) {
@@ -553,7 +562,7 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
553562

554563
m_Seen = std::numeric_limits<decltype(m_Seen)>::max();
555564

556-
if (!ProcessRequest(*m_Stream, request, authenticatedUser, response, *this, m_HasStartedStreaming, yc)) {
565+
if (!ProcessRequest(*m_Stream, request, authenticatedUser, response, *this, m_HasStartedStreaming, cpuBoundWorkTime, yc)) {
557566
break;
558567
}
559568

lib/remote/jsonrpcconnection.cpp

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,19 @@ void JsonRpcConnection::Start()
6060

6161
void JsonRpcConnection::HandleIncomingMessages(boost::asio::yield_context yc)
6262
{
63+
namespace ch = std::chrono;
64+
65+
auto toMilliseconds ([](ch::steady_clock::duration d) {
66+
return ch::duration_cast<ch::milliseconds>(d).count();
67+
});
68+
6369
m_Stream->next_layer().SetSeen(&m_Seen);
6470

6571
while (!m_ShuttingDown) {
66-
String message;
72+
String jsonString;
6773

6874
try {
69-
message = JsonRpc::ReadMessage(m_Stream, yc, m_Endpoint ? -1 : 1024 * 1024);
75+
jsonString = JsonRpc::ReadMessage(m_Stream, yc, m_Endpoint ? -1 : 1024 * 1024);
7076
} catch (const std::exception& ex) {
7177
Log(m_ShuttingDown ? LogDebug : LogNotice, "JsonRpcConnection")
7278
<< "Error while reading JSON-RPC message for identity '" << m_Identity
@@ -76,17 +82,50 @@ void JsonRpcConnection::HandleIncomingMessages(boost::asio::yield_context yc)
7682
}
7783

7884
m_Seen = Utility::GetTime();
85+
if (m_Endpoint) {
86+
m_Endpoint->AddMessageReceived(jsonString.GetLength());
87+
}
88+
89+
String rpcMethod("UNKNOWN");
90+
ch::steady_clock::duration cpuBoundDuration(0);
91+
auto start (ch::steady_clock::now());
7992

8093
try {
8194
CpuBoundWork handleMessage (yc);
8295

96+
// Cache the elapsed time to acquire a CPU semaphore used to detect extremely heavy workloads.
97+
cpuBoundDuration = ch::steady_clock::now() - start;
98+
99+
Dictionary::Ptr message = JsonRpc::DecodeMessage(jsonString);
100+
if (String method = message->Get("method"); !method.IsEmpty()) {
101+
rpcMethod = std::move(method);
102+
}
103+
83104
MessageHandler(message);
84105

85106
l_TaskStats.InsertValue(Utility::GetTime(), 1);
107+
108+
auto total = ch::steady_clock::now() - start;
109+
110+
Log msg(total >= ch::seconds(5) ? LogWarning : LogDebug, "JsonRpcConnection");
111+
msg << "Processed JSON-RPC '" << rpcMethod << "' message for identity '" << m_Identity
112+
<< "' (took total " << toMilliseconds(total) << "ms";
113+
114+
if (cpuBoundDuration >= ch::seconds(1)) {
115+
msg << ", waited " << toMilliseconds(cpuBoundDuration) << "ms on semaphore";
116+
}
117+
msg << ").";
86118
} catch (const std::exception& ex) {
87-
Log(m_ShuttingDown ? LogDebug : LogWarning, "JsonRpcConnection")
88-
<< "Error while processing JSON-RPC message for identity '" << m_Identity
89-
<< "': " << DiagnosticInformation(ex);
119+
auto total = ch::steady_clock::now() - start;
120+
121+
Log msg(m_ShuttingDown ? LogDebug : LogWarning, "JsonRpcConnection");
122+
msg << "Error while processing JSON-RPC '" << rpcMethod << "' message for identity '"
123+
<< m_Identity << "' (took total " << toMilliseconds(total) << "ms";
124+
125+
if (cpuBoundDuration >= ch::seconds(1)) {
126+
msg << ", waited " << toMilliseconds(cpuBoundDuration) << "ms on semaphore";
127+
}
128+
msg << "): " << DiagnosticInformation(ex);
90129

91130
break;
92131
}
@@ -259,10 +298,19 @@ void JsonRpcConnection::Disconnect()
259298
}
260299
}
261300

262-
void JsonRpcConnection::MessageHandler(const String& jsonString)
301+
/**
302+
* Route the provided message to its corresponding handler (if any).
303+
*
304+
* This will first verify the timestamp of that RPC message (if any) and subsequently, rejects any message whose
305+
* timestamp is less than the remote log position of the client Endpoint; otherwise, the endpoint's remote log
306+
* position is updated to that timestamp. It is not expected to happen, but any message lacking an RPC method or
307+
* referring to a non-existent one is also discarded. Afterward, the RPC handler is then called for that message
308+
* and sends it's result back to the sender if the message contains an ID.
309+
*
310+
* @param message The RPC message you want to process.
311+
*/
312+
void JsonRpcConnection::MessageHandler(const Dictionary::Ptr& message)
263313
{
264-
Dictionary::Ptr message = JsonRpc::DecodeMessage(jsonString);
265-
266314
if (m_Endpoint && message->Contains("ts")) {
267315
double ts = message->Get("ts");
268316

@@ -281,8 +329,6 @@ void JsonRpcConnection::MessageHandler(const String& jsonString)
281329
origin->FromZone = m_Endpoint->GetZone();
282330
else
283331
origin->FromZone = Zone::GetByName(message->Get("originZone"));
284-
285-
m_Endpoint->AddMessageReceived(jsonString.GetLength());
286332
}
287333

288334
Value vmethod;

lib/remote/jsonrpcconnection.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ class JsonRpcConnection final : public Object
8888
void CheckLiveness(boost::asio::yield_context yc);
8989

9090
bool ProcessMessage();
91-
void MessageHandler(const String& jsonString);
91+
92+
void MessageHandler(const Dictionary::Ptr& message);
9293

9394
void CertificateRequestResponseHandler(const Dictionary::Ptr& message);
9495

0 commit comments

Comments
 (0)