Line data Source code
1 : /*
2 : * If not stated otherwise in this file or this component's LICENSE file the
3 : * following copyright and licenses apply:
4 : *
5 : * Copyright 2026 Sky UK
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : #include "LogMetricsReporter.h"
21 : #include "RialtoServerLogging.h"
22 : #include <algorithm>
23 : #include <cinttypes>
24 : #include <cmath>
25 :
26 : namespace
27 : {
28 : constexpr std::uint64_t kActiveReportIntervalMs{10 * 60 * 1000};
29 : constexpr double kRelativeChangeTolerance{0.10};
30 : constexpr double kCpuChangeThresholdPercentagePoints{10.0};
31 : constexpr double kMemoryAbsoluteFloorKb{1024.0};
32 : } // namespace
33 :
34 : namespace firebolt::rialto::server
35 : {
36 26 : void LogMetricsReporter::reportPeriodicSample(const PeriodicMetricsReport &report)
37 : {
38 26 : if (!shouldReportPeriodicSample(report))
39 : {
40 9 : return;
41 : }
42 :
43 17 : RIALTO_SERVER_LOG_MIL("Metrics sample=%" PRIu64 ", reason=%s, app='%s', client_pid=%u, client_cpu=%.2f%%, "
44 : "server_cpu=%.2f%%, combined_cpu=%.2f%%, client_cpu_ms=%" PRIu64 ", "
45 : "server_cpu_ms=%" PRIu64 ", client_mem_kb=%" PRIu64 ", server_mem_kb=%" PRIu64 ", "
46 : "shm_mem_kb=%" PRIu64 ", cgroup_mem_kb=%" PRIu64 "/%" PRIu64,
47 : report.sampleId, report.reason.c_str(), report.appName.c_str(), report.clientPid,
48 : report.clientCpuPercent, report.serverCpuPercent, report.combinedCpuPercent,
49 : report.clientCpuTimeMs, report.serverCpuTimeMs, report.clientMemoryKb, report.serverMemoryKb,
50 : report.shmMemoryKb, report.cgroupMemoryUsageKb, report.cgroupMemoryLimitKb);
51 : }
52 :
53 26 : bool LogMetricsReporter::shouldReportPeriodicSample(const PeriodicMetricsReport &report)
54 : {
55 26 : std::lock_guard<std::mutex> lock{m_mutex};
56 :
57 26 : if (report.reason != "PERIODIC")
58 : {
59 1 : return false;
60 : }
61 :
62 25 : if (!m_lastReportedSample)
63 : {
64 7 : m_lastReportedSample = report;
65 7 : return true;
66 : }
67 :
68 18 : const auto &previous{*m_lastReportedSample};
69 18 : const bool stateChanged{report.applicationState != previous.applicationState};
70 : const bool metricsChanged{
71 34 : std::abs(report.clientCpuPercent - previous.clientCpuPercent) >= kCpuChangeThresholdPercentagePoints ||
72 30 : std::abs(report.serverCpuPercent - previous.serverCpuPercent) >= kCpuChangeThresholdPercentagePoints ||
73 14 : std::abs(report.combinedCpuPercent - previous.combinedCpuPercent) >= kCpuChangeThresholdPercentagePoints ||
74 12 : changedSignificantly(static_cast<double>(report.clientMemoryKb), static_cast<double>(previous.clientMemoryKb),
75 11 : kMemoryAbsoluteFloorKb) ||
76 11 : changedSignificantly(static_cast<double>(report.serverMemoryKb), static_cast<double>(previous.serverMemoryKb),
77 10 : kMemoryAbsoluteFloorKb) ||
78 10 : changedSignificantly(static_cast<double>(report.cgroupMemoryUsageKb),
79 10 : static_cast<double>(previous.cgroupMemoryUsageKb), kMemoryAbsoluteFloorKb) ||
80 10 : changedSignificantly(static_cast<double>(report.cgroupMemoryLimitKb),
81 44 : static_cast<double>(previous.cgroupMemoryLimitKb), kMemoryAbsoluteFloorKb) ||
82 10 : changedSignificantly(static_cast<double>(report.shmMemoryKb), static_cast<double>(previous.shmMemoryKb),
83 18 : kMemoryAbsoluteFloorKb)};
84 40 : const bool activeIntervalElapsed{report.applicationState == ApplicationState::RUNNING &&
85 22 : report.monotonicTimeMs >= previous.monotonicTimeMs &&
86 4 : report.monotonicTimeMs - previous.monotonicTimeMs >= kActiveReportIntervalMs};
87 :
88 18 : if (stateChanged || metricsChanged || activeIntervalElapsed)
89 : {
90 10 : m_lastReportedSample = report;
91 10 : return true;
92 : }
93 :
94 8 : return false;
95 26 : }
96 :
97 53 : bool LogMetricsReporter::changedSignificantly(double current, double previous, double absoluteFloor)
98 : {
99 53 : const double threshold{std::max(std::abs(previous) * kRelativeChangeTolerance, absoluteFloor)};
100 53 : return std::abs(current - previous) >= threshold;
101 : }
102 :
103 4 : void LogMetricsReporter::reportStateTransition(const StateTransitionReport &report)
104 : {
105 4 : const auto &r{report.metrics};
106 4 : RIALTO_SERVER_LOG_MIL("Metrics state report [%s] state='%s', duration_ms=%" PRIu64 ", samples=%" PRIu64 ", "
107 : "client_cpu={min=%.2f, max=%.2f, mean=%.2f, stddev=%.2f}%%, "
108 : "server_cpu={min=%.2f, max=%.2f, mean=%.2f, stddev=%.2f}%%, "
109 : "combined_cpu={min=%.2f, max=%.2f, mean=%.2f, stddev=%.2f}%%, "
110 : "client_mem_kb={min=%.0f, max=%.0f, mean=%.0f}, "
111 : "server_mem_kb={min=%.0f, max=%.0f, mean=%.0f}, "
112 : "cgroup_mem_kb={min=%.0f, max=%.0f, mean=%.0f}",
113 : report.context.c_str(), r.stateName.c_str(), r.durationMs, r.clientCpu.count, r.clientCpu.min,
114 : r.clientCpu.max, r.clientCpu.mean, r.clientCpu.stddev, r.serverCpu.min, r.serverCpu.max,
115 : r.serverCpu.mean, r.serverCpu.stddev, r.combinedCpu.min, r.combinedCpu.max,
116 : r.combinedCpu.mean, r.combinedCpu.stddev, r.clientMemoryKb.min, r.clientMemoryKb.max,
117 : r.clientMemoryKb.mean, r.serverMemoryKb.min, r.serverMemoryKb.max, r.serverMemoryKb.mean,
118 : r.cgroupMemoryUsageKb.min, r.cgroupMemoryUsageKb.max, r.cgroupMemoryUsageKb.mean);
119 : }
120 :
121 1 : void LogMetricsReporter::reportThresholdExceeded(const ThresholdAlert &alert)
122 : {
123 1 : const char *severity = (alert.severity == ThresholdSeverity::CRITICAL) ? "CRITICAL" : "WARNING";
124 1 : RIALTO_SERVER_LOG_WARN("Metrics threshold %s: %s=%.2f exceeds %.2f", severity, alert.metricName.c_str(),
125 : alert.currentValue, alert.thresholdValue);
126 : }
127 : } // namespace firebolt::rialto::server
|