Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #pragma once
18 : :
19 : : #include <algorithm>
20 : : #include <boost/system/error_code.hpp>
21 : : #include <chrono>
22 : : #include <cstdint>
23 : : #include <ctime>
24 : : #include <iomanip>
25 : : #include <sstream>
26 : : #include <string>
27 : : #include <string_view>
28 : :
29 : : namespace wirestead {
30 : : namespace diagnostics {
31 : :
32 : : #ifdef ERROR
33 : : #undef ERROR
34 : : #endif
35 : : #ifdef WARNING
36 : : #undef WARNING
37 : : #endif
38 : :
39 : : /**
40 : : * @brief Error severity levels
41 : : */
42 : : enum class ErrorLevel {
43 : : INFO = 0, // Informational message (normal operation info)
44 : : WARNING = 1, // Warning (recoverable issue)
45 : : ERROR = 2, // Error (retry required)
46 : : CRITICAL = 3 // Critical error (unrecoverable)
47 : : };
48 : :
49 : : /**
50 : : * @brief Error categories for classification
51 : : */
52 : : enum class ErrorCategory {
53 : : CONNECTION = 0, // Connection related (TCP, Serial connect/disconnect)
54 : : COMMUNICATION = 1, // Communication related (data send/receive)
55 : : CONFIGURATION = 2, // Configuration related (invalid config values)
56 : : MEMORY = 3, // Memory related (allocation/deallocation errors)
57 : : SYSTEM = 4, // System related (OS level errors)
58 : : UNKNOWN = 5 // Unknown error
59 : : };
60 : :
61 : : /**
62 : : * @brief Comprehensive error information structure
63 : : */
64 : : struct ErrorInfo {
65 : : ErrorLevel level; // Error severity
66 : : ErrorCategory category; // Error category
67 : : std::string component; // Component name (serial, tcp_server, tcp_client)
68 : : std::string operation; // Operation being performed (read, write, connect, bind)
69 : : std::string message; // Error message
70 : : boost::system::error_code boost_error; // Boost error code
71 : : std::chrono::system_clock::time_point timestamp; // Error occurrence time
72 : : bool retryable; // Retry possibility
73 : : uint32_t retry_count; // Current retry count
74 : : std::string context; // Additional context information
75 : :
76 : : /**
77 : : * @brief Constructor for basic error info
78 : : */
79 : 1045 : ErrorInfo(ErrorLevel l, ErrorCategory c, std::string_view comp, std::string_view op, std::string_view msg)
80 : 1045 : : level(l),
81 : 1045 : category(c),
82 : 2090 : component(comp),
83 : 2090 : operation(op),
84 : 2090 : message(msg),
85 : 1045 : timestamp(std::chrono::system_clock::now()),
86 : 1045 : retryable(false),
87 : 2090 : retry_count(0) {}
88 : :
89 : : /**
90 : : * @brief Constructor with Boost error code
91 : : */
92 : 281 : ErrorInfo(ErrorLevel l, ErrorCategory c, std::string_view comp, std::string_view op, std::string_view msg,
93 : : const boost::system::error_code& ec, bool retry = false)
94 : 281 : : level(l),
95 : 281 : category(c),
96 : 562 : component(comp),
97 : 562 : operation(op),
98 : 562 : message(msg),
99 : 281 : boost_error(ec),
100 : 281 : timestamp(std::chrono::system_clock::now()),
101 : 281 : retryable(retry),
102 : 281 : retry_count(0) {}
103 : :
104 : : /**
105 : : * @brief Get formatted timestamp string
106 : : */
107 : 1 : std::string timestamp_string() const {
108 : 1 : auto time_t = std::chrono::system_clock::to_time_t(timestamp);
109 : 1 : auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp.time_since_epoch()) % 1000;
110 : :
111 : 1 : std::tm tm_buf{};
112 : : #if defined(_MSC_VER)
113 : : localtime_s(&tm_buf, &time_t);
114 : : #else
115 : 1 : localtime_r(&time_t, &tm_buf);
116 : : #endif
117 : :
118 : 1 : std::ostringstream oss;
119 : 1 : oss << std::put_time(&tm_buf, "%Y-%m-%d %H:%M:%S");
120 : 1 : oss << '.' << std::setfill('0') << std::setw(3) << ms.count();
121 : 2 : return oss.str();
122 : 1 : }
123 : :
124 : : /**
125 : : * @brief Get error level as string
126 : : */
127 : 9 : std::string level_string() const {
128 [ + + + + : 9 : switch (level) {
- ]
129 : 1 : case ErrorLevel::INFO:
130 : 2 : return "INFO";
131 : 1 : case ErrorLevel::WARNING:
132 : 2 : return "WARNING";
133 : 3 : case ErrorLevel::ERROR:
134 : 6 : return "ERROR";
135 : 4 : case ErrorLevel::CRITICAL:
136 : 8 : return "CRITICAL";
137 : : }
138 : 0 : return "UNKNOWN";
139 : : }
140 : :
141 : : /**
142 : : * @brief Get error category as string
143 : : */
144 : 7 : std::string category_string() const {
145 [ + + + + : 7 : switch (category) {
+ + - ]
146 : 1 : case ErrorCategory::CONNECTION:
147 : 2 : return "CONNECTION";
148 : 2 : case ErrorCategory::COMMUNICATION:
149 : 4 : return "COMMUNICATION";
150 : 1 : case ErrorCategory::CONFIGURATION:
151 : 2 : return "CONFIGURATION";
152 : 1 : case ErrorCategory::MEMORY:
153 : 2 : return "MEMORY";
154 : 1 : case ErrorCategory::SYSTEM:
155 : 2 : return "SYSTEM";
156 : 1 : case ErrorCategory::UNKNOWN:
157 : 2 : return "UNKNOWN";
158 : : }
159 : 0 : return "UNKNOWN";
160 : : }
161 : :
162 : : /**
163 : : * @brief Get formatted error summary
164 : : */
165 : 3 : std::string summary() const {
166 : 3 : std::ostringstream oss;
167 : 3 : oss << "[" << level_string() << "] " << "[" << component << "] " << "[" << operation << "] " << message;
168 : :
169 [ + + ]: 3 : if (boost_error) {
170 : 2 : oss << " (boost: " << boost_error.message() << ", code: " << boost_error.value() << ")";
171 : : }
172 : :
173 [ + + ]: 3 : if (retryable) {
174 : 2 : oss << " [RETRYABLE, count: " << retry_count << "]";
175 : : }
176 : :
177 : 6 : return oss.str();
178 : 3 : }
179 : : };
180 : :
181 : : /**
182 : : * @brief Error statistics for monitoring
183 : : */
184 : : struct ErrorStats {
185 : : size_t total_errors = 0;
186 : : size_t errors_by_level[4] = {0, 0, 0, 0}; // INFO, WARNING, ERROR, CRITICAL
187 : : size_t errors_by_category[6] = {0, 0, 0, 0, 0, 0}; // CONNECTION, COMMUNICATION, etc.
188 : : size_t retryable_errors = 0;
189 : : size_t successful_retries = 0;
190 : : size_t failed_retries = 0;
191 : :
192 : : std::chrono::system_clock::time_point first_error;
193 : : std::chrono::system_clock::time_point last_error;
194 : :
195 : : /**
196 : : * @brief Reset all statistics
197 : : */
198 : 24 : void reset() {
199 : 24 : total_errors = 0;
200 : 72 : std::fill(std::begin(errors_by_level), std::end(errors_by_level), 0);
201 : 72 : std::fill(std::begin(errors_by_category), std::end(errors_by_category), 0);
202 : 24 : retryable_errors = 0;
203 : 24 : successful_retries = 0;
204 : 24 : failed_retries = 0;
205 : 24 : first_error = std::chrono::system_clock::time_point{};
206 : 24 : last_error = std::chrono::system_clock::time_point{};
207 : 24 : }
208 : :
209 : : /**
210 : : * @brief Get error rate (errors per minute)
211 : : */
212 : 2 : double error_rate() const {
213 [ + + ]: 2 : if (total_errors == 0) return 0.0;
214 : :
215 : 1 : auto duration = std::chrono::duration_cast<std::chrono::minutes>(last_error - first_error).count();
216 [ + - ]: 1 : return duration > 0 ? static_cast<double>(total_errors) / static_cast<double>(duration) : 0.0;
217 : : }
218 : : };
219 : :
220 : : } // namespace diagnostics
221 : : } // namespace wirestead
|