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 : : #include "wirestead/diagnostics/error_handler.hpp"
18 : :
19 : : #include <algorithm>
20 : : #include <iomanip>
21 : : #include <iostream>
22 : : #include <sstream>
23 : : #include <string_view>
24 : :
25 : : #include "wirestead/diagnostics/logger.hpp"
26 : :
27 : : namespace wirestead {
28 : : namespace diagnostics {
29 : :
30 : 37 : ErrorHandler::ErrorHandler() = default;
31 : 37 : ErrorHandler::~ErrorHandler() = default;
32 : :
33 : 1129 : ErrorHandler& ErrorHandler::instance() {
34 : 1129 : static ErrorHandler inst;
35 : 1129 : return inst;
36 : : }
37 : :
38 : 1 : ErrorHandler& ErrorHandler::default_handler() { return instance(); }
39 : :
40 : 1084 : void ErrorHandler::report_error(const ErrorInfo& error) {
41 [ + + ]: 1084 : if (!enabled_.load()) {
42 : 5 : return;
43 : : }
44 : :
45 [ + + ]: 1082 : if (error.level < min_level_.load()) {
46 : 3 : return;
47 : : }
48 : :
49 : 1079 : std::vector<ErrorCallback> callbacks_copy;
50 : : {
51 : 1079 : std::lock_guard<std::mutex> lock(mutex_);
52 : 1079 : update_stats(error);
53 : 1079 : add_to_recent_errors(error);
54 : 1079 : add_to_component_errors(error);
55 : 1079 : callbacks_copy = callbacks_;
56 : 1079 : }
57 : 1079 : notify_callbacks(callbacks_copy, error);
58 : 1079 : }
59 : :
60 : 9 : void ErrorHandler::register_callback(ErrorCallback callback) {
61 : 9 : std::lock_guard<std::mutex> lock(mutex_);
62 : 9 : callbacks_.push_back(std::move(callback));
63 : 9 : }
64 : :
65 : 27 : void ErrorHandler::clear_callbacks() {
66 : 27 : std::lock_guard<std::mutex> lock(mutex_);
67 : 27 : callbacks_.clear();
68 : 27 : }
69 : :
70 : 24 : void ErrorHandler::set_min_error_level(ErrorLevel level) { min_level_.store(level); }
71 : :
72 : 1 : ErrorLevel ErrorHandler::min_error_level() const { return min_level_.load(); }
73 : :
74 : 26 : void ErrorHandler::set_enabled(bool enabled) { enabled_.store(enabled); }
75 : :
76 : 2 : bool ErrorHandler::enabled() const { return enabled_.load(); }
77 : :
78 : 15 : ErrorStats ErrorHandler::error_stats() const {
79 : 15 : std::lock_guard<std::mutex> lock(stats_mutex_);
80 : 15 : return stats_;
81 : 15 : }
82 : :
83 : 23 : void ErrorHandler::reset_stats() {
84 : 23 : std::lock_guard<std::mutex> lock(stats_mutex_);
85 : 23 : stats_.reset();
86 : 23 : }
87 : :
88 : 3 : std::vector<ErrorInfo> ErrorHandler::errors_by_component(std::string_view component) const {
89 : 3 : std::lock_guard<std::mutex> lock(mutex_);
90 : 6 : auto it = errors_by_component_.find(std::string(component));
91 [ + + ]: 3 : if (it != errors_by_component_.end()) {
92 : 2 : return it->second;
93 : : }
94 : 1 : return {};
95 : 3 : }
96 : :
97 : 2 : std::vector<ErrorInfo> ErrorHandler::recent_errors(size_t count) const {
98 : 2 : std::lock_guard<std::mutex> lock(mutex_);
99 : :
100 : 2 : size_t start_index = 0;
101 [ + + ]: 2 : if (recent_errors_.size() > count) {
102 : 1 : start_index = recent_errors_.size() - count;
103 : : }
104 : :
105 : 2 : return std::vector<ErrorInfo>(recent_errors_.begin() + static_cast<std::ptrdiff_t>(start_index),
106 : 4 : recent_errors_.end());
107 : 2 : }
108 : :
109 : 2 : bool ErrorHandler::has_errors(std::string_view component) const {
110 : 2 : std::lock_guard<std::mutex> lock(mutex_);
111 : 4 : auto it = errors_by_component_.find(std::string(component));
112 [ + + + - ]: 4 : return it != errors_by_component_.end() && !it->second.empty();
113 : 2 : }
114 : :
115 : 5 : size_t ErrorHandler::error_count(std::string_view component, ErrorLevel level) const {
116 : 5 : std::lock_guard<std::mutex> lock(mutex_);
117 : 10 : auto it = errors_by_component_.find(std::string(component));
118 [ + + ]: 5 : if (it == errors_by_component_.end()) {
119 : 1 : return 0;
120 : : }
121 : :
122 [ + - ]: 4 : return static_cast<size_t>(std::count_if(it->second.begin(), it->second.end(),
123 : 16 : [level](const ErrorInfo& error) { return error.level == level; }));
124 : 5 : }
125 : :
126 : 1079 : void ErrorHandler::update_stats(const ErrorInfo& error) {
127 : 1079 : std::lock_guard<std::mutex> lock(stats_mutex_);
128 : :
129 : 1079 : stats_.total_errors++;
130 : 1079 : stats_.errors_by_level[static_cast<int>(error.level)]++;
131 : 1079 : stats_.errors_by_category[static_cast<int>(error.category)]++;
132 : :
133 [ + + ]: 1079 : if (error.retryable) {
134 : 29 : stats_.retryable_errors++;
135 : : }
136 : :
137 [ + - + + ]: 1079 : if (stats_.first_error == std::chrono::system_clock::time_point{}) {
138 : 35 : stats_.first_error = error.timestamp;
139 : : }
140 : 1079 : stats_.last_error = error.timestamp;
141 : 1079 : }
142 : :
143 : 1079 : void ErrorHandler::notify_callbacks(const std::vector<ErrorCallback>& callbacks, const ErrorInfo& error) {
144 [ + + ]: 1090 : for (const auto& callback : callbacks) {
145 : : try {
146 : 11 : callback(error);
147 : 2 : } catch (const std::exception& e) {
148 : : // Avoid infinite recursion - use Logger directly instead of ErrorHandler
149 : 3 : WIRESTEAD_LOG_ERROR("error_handler", "callback", "Error in error callback: " + std::string(e.what()));
150 : 2 : } catch (...) {
151 : 1 : WIRESTEAD_LOG_ERROR("error_handler", "callback", "Unknown error in error callback");
152 : 1 : }
153 : : }
154 : 1079 : }
155 : :
156 : 1079 : void ErrorHandler::add_to_recent_errors(const ErrorInfo& error) {
157 : 1079 : recent_errors_.push_back(error);
158 : :
159 : : // Keep only the most recent errors
160 [ + + ]: 1079 : if (recent_errors_.size() > MAX_RECENT_ERRORS) {
161 : 10 : recent_errors_.erase(
162 : 5 : recent_errors_.begin(),
163 : 10 : recent_errors_.begin() + static_cast<std::ptrdiff_t>(recent_errors_.size() - MAX_RECENT_ERRORS));
164 : : }
165 : 1079 : }
166 : :
167 : 1079 : void ErrorHandler::add_to_component_errors(const ErrorInfo& error) {
168 : 1079 : errors_by_component_[error.component].push_back(error);
169 : :
170 : : // Limit component error history to prevent memory growth
171 : 1079 : constexpr size_t MAX_COMPONENT_ERRORS = 100;
172 : 1079 : auto& component_errors = errors_by_component_[error.component];
173 [ + + ]: 1079 : if (component_errors.size() > MAX_COMPONENT_ERRORS) {
174 : 1810 : component_errors.erase(
175 : 905 : component_errors.begin(),
176 : 1810 : component_errors.begin() + static_cast<std::ptrdiff_t>(component_errors.size() - MAX_COMPONENT_ERRORS));
177 : : }
178 : 1079 : }
179 : :
180 : : // Convenience functions implementation
181 : : namespace error_reporting {
182 : :
183 : 54 : void report_connection_error(std::string_view component, std::string_view operation,
184 : : const boost::system::error_code& ec, bool retryable) {
185 : 54 : ErrorInfo error(ErrorLevel::ERROR, ErrorCategory::CONNECTION, component, operation, ec.message(), ec, retryable);
186 : 54 : ErrorHandler::instance().report_error(error);
187 : 54 : }
188 : :
189 : 1 : void report_communication_error(std::string_view component, std::string_view operation, std::string_view message,
190 : : bool retryable) {
191 : 1 : ErrorInfo error(ErrorLevel::ERROR, ErrorCategory::COMMUNICATION, component, operation, message);
192 : 1 : error.retryable = retryable;
193 : 1 : ErrorHandler::instance().report_error(error);
194 : 1 : }
195 : :
196 : 6 : void report_configuration_error(std::string_view component, std::string_view operation, std::string_view message) {
197 : 6 : ErrorInfo error(ErrorLevel::ERROR, ErrorCategory::CONFIGURATION, component, operation, message);
198 : 6 : ErrorHandler::instance().report_error(error);
199 : 6 : }
200 : :
201 : 6 : void report_memory_error(std::string_view component, std::string_view operation, std::string_view message) {
202 : 6 : ErrorInfo error(ErrorLevel::CRITICAL, ErrorCategory::MEMORY, component, operation, message);
203 : 6 : ErrorHandler::instance().report_error(error);
204 : 6 : }
205 : :
206 : 2 : void report_system_error(std::string_view component, std::string_view operation, std::string_view message,
207 : : const boost::system::error_code& ec) {
208 : 2 : ErrorInfo error(ErrorLevel::ERROR, ErrorCategory::SYSTEM, component, operation, message, ec);
209 : 2 : ErrorHandler::instance().report_error(error);
210 : 2 : }
211 : :
212 : 3 : void report_warning(std::string_view component, std::string_view operation, std::string_view message) {
213 : 3 : ErrorInfo error(ErrorLevel::WARNING, ErrorCategory::UNKNOWN, component, operation, message);
214 : 3 : ErrorHandler::instance().report_error(error);
215 : 3 : }
216 : :
217 : 1012 : void report_info(std::string_view component, std::string_view operation, std::string_view message) {
218 : 1012 : ErrorInfo error(ErrorLevel::INFO, ErrorCategory::UNKNOWN, component, operation, message);
219 : 1012 : ErrorHandler::instance().report_error(error);
220 : 1012 : }
221 : :
222 : : } // namespace error_reporting
223 : :
224 : : } // namespace diagnostics
225 : : } // namespace wirestead
|