37 std::chrono::system_clock::time_point tp) {
38 const auto time_t_val = std::chrono::system_clock::to_time_t(tp);
42 gmtime_s(&tm_val, &time_t_val);
44 gmtime_r(&time_t_val, &tm_val);
47 std::ostringstream oss;
48 oss << std::put_time(&tm_val,
"%Y-%m-%dT%H:%M:%SZ");
57[[nodiscard]]
inline std::string escape_json_string(std::string_view str) {
59 result.reserve(str.size() + 10);
85 if (
static_cast<unsigned char>(c) < 0x20) {
88 std::snprintf(buf,
sizeof(buf),
"\\u%04x",
89 static_cast<unsigned char>(c));
106[[nodiscard]]
inline std::string to_json(
const database_status& status) {
107 std::ostringstream oss;
109 << R
"("connected":)" << (status.connected ? "true" :
"false");
111 if (
status.last_connected) {
116 oss << R
"(,"active_connections":)" << status.active_connections;
118 if (
status.response_time) {
119 oss << R
"(,"response_time_ms":)" << status.response_time->count();
122 if (
status.error_message) {
123 oss << R
"(,"error":")" << escape_json_string(*status.error_message)
136[[nodiscard]]
inline std::string to_json(
const storage_status& status) {
137 std::ostringstream oss;
139 << R
"("writable":)" << (status.writable ? "true" :
"false")
140 << R
"(,"readable":)" << (status.readable ? "true" :
"false")
141 << R
"(,"total_bytes":)" << status.total_bytes
142 << R"(,"used_bytes":)" << status.used_bytes
143 << R"(,"available_bytes":)" << status.available_bytes
144 << R"(,"usage_percent":)" << std::fixed << std::setprecision(2)
145 << status.usage_percent();
147 if (
status.error_message) {
148 oss << R
"(,"error":")" << escape_json_string(*status.error_message)
161[[nodiscard]]
inline std::string to_json(
const association_metrics& metrics) {
162 std::ostringstream oss;
164 << R
"("active":)" << metrics.active_associations
165 << R"(,"max":)" << metrics.max_associations
166 << R"(,"total":)" << metrics.total_associations
167 << R"(,"failed":)" << metrics.failed_associations << "}";
176[[nodiscard]]
inline std::string to_json(
const storage_metrics& metrics) {
177 std::ostringstream oss;
179 << R
"("total_instances":)" << metrics.total_instances
180 << R"(,"total_studies":)" << metrics.total_studies
181 << R"(,"total_series":)" << metrics.total_series
182 << R"(,"successful_stores":)" << metrics.successful_stores
183 << R"(,"failed_stores":)" << metrics.failed_stores << "}";
192[[nodiscard]]
inline std::string to_json(
const version_info& info) {
193 std::ostringstream oss;
195 << R
"("version":")" << info.version_string() << "\""
196 << R
"(,"major":)" << info.major << R"(,"minor":)" << info.minor
197 << R"(,"patch":)" << info.patch;
199 if (!
info.build_id.empty()) {
200 oss << R
"(,"build_id":")" << escape_json_string(info.build_id) << "\"";
203 oss << R
"(,"startup_time":")" << to_iso8601(info.startup_time) << "\""
204 << R
"(,"uptime_seconds":)" << info.uptime().count() << "}";
229[[nodiscard]]
inline std::string to_json(
const health_status& status) {
230 std::ostringstream oss;
234 << R
"(,"healthy":)" << (status.is_healthy() ? "true" :
"false")
235 << R
"(,"operational":)" << (status.is_operational() ? "true" :
"false");
238 oss << R
"(,"message":")" << escape_json_string(*status.message) << "\"";
241 oss << R
"(,"database":)" << to_json(status.database)
242 << R"(,"storage":)" << to_json(status.storage)
243 << R"(,"associations":)" << to_json(status.associations)
244 << R"(,"metrics":)" << to_json(status.metrics)
245 << R"(,"version":)" << to_json(status.version) << "}";
259[[nodiscard]]
inline std::string to_json_pretty(
const health_status& status,
261 const std::string ind(
static_cast<std::size_t
>(indent),
' ');
262 const std::string ind2(
static_cast<std::size_t
>(indent * 2),
' ');
264 std::ostringstream oss;
268 << ind << R
"("healthy": )" << (status.is_healthy() ? "true" :
"false")
270 << ind << R
"("operational": )"
271 << (status.is_operational() ? "true" :
"false");
275 << ind << R
"("message": ")" << escape_json_string(*status.message)
281 << ind << R
"("database": {)" << "\n"
282 << ind2 << R
"("connected": )"
283 << (status.database.connected ? "true" :
"false");
285 if (
status.database.last_connected) {
287 << ind2 << R
"("last_connected": ")"
293 << R
"("active_connections": )" << status.database.active_connections;
295 if (
status.database.response_time) {
297 << ind2 << R
"("response_time_ms": )"
298 << status.database.response_time->count();
301 if (
status.database.error_message) {
303 << ind2 << R
"("error": ")"
304 << escape_json_string(*status.database.error_message) << "\"";
307 oss <<
"\n" << ind <<
"}";
311 << ind << R
"("storage": {)" << "\n"
312 << ind2 << R
"("writable": )"
313 << (status.storage.writable ? "true" :
"false") <<
",\n"
314 << ind2 << R
"("readable": )"
315 << (status.storage.readable ? "true" :
"false") <<
",\n"
316 << ind2 << R
"("total_bytes": )" << status.storage.total_bytes << ",\n"
317 << ind2 << R
"("used_bytes": )" << status.storage.used_bytes << ",\n"
318 << ind2 << R
"("available_bytes": )" << status.storage.available_bytes
320 << ind2 << R
"("usage_percent": )" << std::fixed << std::setprecision(2)
321 << status.storage.usage_percent();
323 if (
status.storage.error_message) {
325 << ind2 << R
"("error": ")"
326 << escape_json_string(*status.storage.error_message) << "\"";
329 oss <<
"\n" << ind <<
"}";
333 << ind << R
"("associations": {)" << "\n"
334 << ind2 << R
"("active": )" << status.associations.active_associations
336 << ind2 << R
"("max": )" << status.associations.max_associations << ",\n"
337 << ind2 << R
"("total": )" << status.associations.total_associations
339 << ind2 << R
"("failed": )" << status.associations.failed_associations
345 << ind << R
"("metrics": {)" << "\n"
346 << ind2 << R
"("total_instances": )" << status.metrics.total_instances
348 << ind2 << R
"("total_studies": )" << status.metrics.total_studies
350 << ind2 << R
"("total_series": )" << status.metrics.total_series << ",\n"
352 << R
"("successful_stores": )" << status.metrics.successful_stores
354 << ind2 << R
"("failed_stores": )" << status.metrics.failed_stores
360 << ind << R
"("version": {)" << "\n"
361 << ind2 << R
"("version": ")" << status.version.version_string()
363 << ind2 << R
"("major": )" << status.version.major << ",\n"
364 << ind2 << R
"("minor": )" << status.version.minor << ",\n"
365 << ind2 << R
"("patch": )" << status.version.patch;
367 if (!
status.version.build_id.empty()) {
369 << ind2 << R
"("build_id": ")"
370 << escape_json_string(status.version.build_id) << "\"";
374 << ind2 << R
"("startup_time": ")"
376 << ind2 << R
"("uptime_seconds": )" << status.version.uptime().count()
Health status data structures for PACS system monitoring.
std::string to_iso8601(std::chrono::system_clock::time_point tp)
Convert time_point to ISO 8601 string.
constexpr std::string_view to_string(health_level level) noexcept
Convert health level to string representation.