@@ -160,10 +160,36 @@ Vector<SentryEvent::StackFrame> _extract_error_stack_frames_from_backtraces(
160160 return frames;
161161}
162162
163+ template <class T >
164+ inline size_t _hash (const T &p_value) {
165+ std::hash<T> hasher;
166+ return hasher (p_value);
167+ }
168+
169+ template <class T >
170+ inline void _hash_combine (std::size_t &p_hash, const T &p_value) {
171+ // NOTE: Hash combining technique, originally from boost.
172+ std::hash<T> hasher;
173+ p_hash ^= hasher (p_value) + 0x9e3779b9 + (p_hash << 6 ) + (p_hash >> 2 );
174+ }
175+
163176} // unnamed namespace
164177
165178namespace sentry {
166179
180+ std::size_t SentryLogger::ErrorKeyHash::operator ()(const ErrorKey &p_key) const {
181+ CharString message_cstr = p_key.message .utf8 ();
182+ CharString filename_cstr = p_key.file .utf8 ();
183+
184+ std::string_view message_sv{ message_cstr.get_data () };
185+ std::string_view filename_sv{ filename_cstr.get_data () };
186+
187+ size_t hash_value = _hash (message_sv);
188+ _hash_combine (hash_value, filename_sv);
189+ _hash_combine (hash_value, p_key.line );
190+ return hash_value;
191+ }
192+
167193void SentryLogger::_connect_process_frame () {
168194 SceneTree *scene_tree = Object::cast_to<SceneTree>(Engine::get_singleton ()->get_main_loop ());
169195 if (scene_tree) {
@@ -198,8 +224,8 @@ void SentryLogger::_process_frame() {
198224 }
199225
200226 // Clear source_line_times if it's too big. Cheap and efficient.
201- if (unlikely (source_line_times .size () > 100 )) {
202- source_line_times .clear ();
227+ if (unlikely (error_timepoints .size () > 100 )) {
228+ error_timepoints .clear ();
203229 }
204230}
205231
@@ -214,7 +240,12 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in
214240 return ;
215241 }
216242
217- SourceLine source_line{ p_file.utf8 (), p_line };
243+ String error_message = p_rationale.is_empty () ? p_code : p_rationale;
244+
245+ ErrorKey error_key;
246+ error_key.message = error_message;
247+ error_key.file = p_file;
248+ error_key.line = p_line;
218249
219250 TimePoint now = std::chrono::high_resolution_clock::now ();
220251
@@ -227,8 +258,8 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in
227258 // Reject errors based on per-source-line throttling window to prevent
228259 // repetitive logging caused by loops or errors recurring in each frame.
229260 // The timestamps are tracked for each source line that produced an error.
230- auto it = source_line_times .find (source_line );
231- bool is_spammy_error = it != source_line_times .end () && now - it->second < limits.repeated_error_window ;
261+ auto it = error_timepoints .find (error_key );
262+ bool is_spammy_error = it != error_timepoints .end () && now - it->second < limits.repeated_error_window ;
232263
233264 bool within_frame_limit = frame_events < limits.events_per_frame ;
234265 bool within_throttling_limit = event_times.size () < limits.throttle_events ;
@@ -248,7 +279,7 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in
248279
249280 if (as_event || as_breadcrumb) {
250281 // Store timestamp to prevent repetitive logging from the same line of code.
251- source_line_times[source_line ] = now;
282+ error_timepoints[error_key ] = now;
252283 }
253284 }
254285
@@ -257,10 +288,8 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in
257288 return ;
258289 }
259290
260- String error_value = p_rationale.is_empty () ? p_code : p_rationale;
261-
262291 sentry::util::print_debug (
263- " Capturing error: " , error_value ,
292+ " Capturing error: " , error_message ,
264293 " \n at: " , p_function, " (" , p_file, " :" , p_line, " )" ,
265294 " \n event: " , as_event, " breadcrumb: " , as_breadcrumb);
266295
@@ -282,7 +311,7 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in
282311 ev->set_level (sentry::get_sentry_level_for_godot_error_type ((GodotErrorType)p_error_type));
283312 SentryEvent::Exception exception = {
284313 error_type_as_string[int (p_error_type)],
285- error_value ,
314+ error_message ,
286315 frames
287316 };
288317 ev->add_exception (exception);
@@ -300,7 +329,7 @@ void SentryLogger::_log_error(const String &p_function, const String &p_file, in
300329 data[" error_type" ] = String (error_type_as_string[int (p_error_type)]);
301330
302331 SentrySDK::get_singleton ()->add_breadcrumb (
303- error_value ,
332+ error_message ,
304333 " error" ,
305334 sentry::get_sentry_level_for_godot_error_type ((GodotErrorType)p_error_type),
306335 " error" ,
0 commit comments