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