22# frozen_string_literal: true
33
44require_relative "enums/error_reporter"
5+ require_relative "handlers"
56
67# Try to require all supported error reporting libraries
78# Users may have multiple installed, so we should load all of them
@@ -19,33 +20,62 @@ module LogStruct
1920 # but the operation should be allowed to continue (e.g. scrubbing log data.)
2021 class MultiErrorReporter
2122 # Class variable to store the selected reporter
22- @reporter = T . let ( nil , T . nilable ( ErrorReporter ) )
23+ class CallableReporterWrapper
24+ extend T ::Sig
25+
26+ sig { params ( callable : T . untyped ) . void }
27+ def initialize ( callable )
28+ @callable = callable
29+ end
30+
31+ sig { returns ( T . untyped ) }
32+ attr_reader :callable
33+ alias_method :original , :callable
34+
35+ sig { params ( error : StandardError , context : T . nilable ( T ::Hash [ Symbol , T . untyped ] ) , source : Source ) . void }
36+ def call ( error , context , source )
37+ case callable_arity
38+ when 3
39+ callable . call ( error , context , source )
40+ when 2
41+ callable . call ( error , context )
42+ when 1
43+ callable . call ( error )
44+ else
45+ callable . call ( error , context , source )
46+ end
47+ end
48+
49+ private
50+
51+ sig { returns ( Integer ) }
52+ def callable_arity
53+ callable . respond_to? ( :arity ) ? callable . arity : -1
54+ end
55+ end
56+
57+ ReporterImpl = T . type_alias { T . any ( ErrorReporter , CallableReporterWrapper ) }
58+
59+ @reporter_impl = T . let ( nil , T . nilable ( ReporterImpl ) )
2360
2461 class << self
2562 extend T ::Sig
2663
27- sig { returns ( ErrorReporter ) }
64+ sig { returns ( ReporterImpl ) }
2865 def reporter
29- @reporter ||= detect_reporter
66+ reporter_impl
3067 end
3168
3269 # Set the reporter to use (user-friendly API that accepts symbols)
33- sig { params ( reporter_type : T . any ( ErrorReporter , Symbol ) ) . returns ( ErrorReporter ) }
70+ sig { params ( reporter_type : T . any ( ErrorReporter , Symbol , Handlers :: ErrorReporter ) ) . returns ( ReporterImpl ) }
3471 def reporter = ( reporter_type )
35- @reporter = case reporter_type
72+ @reporter_impl = case reporter_type
3673 when ErrorReporter
3774 reporter_type
3875 when Symbol
39- case reporter_type
40- when :sentry then ErrorReporter ::Sentry
41- when :bugsnag then ErrorReporter ::Bugsnag
42- when :rollbar then ErrorReporter ::Rollbar
43- when :honeybadger then ErrorReporter ::Honeybadger
44- when :rails_logger then ErrorReporter ::RailsLogger
45- else
46- valid_types = ErrorReporter . values . map { |v | ":#{ v . serialize } " } . join ( ", " )
47- raise ArgumentError , "Unknown reporter type: #{ reporter_type } . Valid types are: #{ valid_types } "
48- end
76+ resolve_symbol_reporter ( reporter_type )
77+ else
78+ wrap_callable_reporter ( reporter_type )
4979 end
5080 end
5181
@@ -69,7 +99,9 @@ def detect_reporter
6999 sig { params ( error : StandardError , context : T ::Hash [ T . untyped , T . untyped ] ) . void }
70100 def report_error ( error , context = { } )
71101 # Call the appropriate reporter method based on what's available
72- case reporter
102+ impl = reporter_impl
103+
104+ case impl
73105 when ErrorReporter ::Sentry
74106 report_to_sentry ( error , context )
75107 when ErrorReporter ::Bugsnag
@@ -78,13 +110,43 @@ def report_error(error, context = {})
78110 report_to_rollbar ( error , context )
79111 when ErrorReporter ::Honeybadger
80112 report_to_honeybadger ( error , context )
81- else
113+ when ErrorReporter :: RailsLogger
82114 fallback_logging ( error , context )
115+ when CallableReporterWrapper
116+ impl . call ( error , context , Source ::Internal )
83117 end
84118 end
85119
86120 private
87121
122+ sig { returns ( ReporterImpl ) }
123+ def reporter_impl
124+ @reporter_impl ||= detect_reporter
125+ end
126+
127+ sig { params ( symbol : Symbol ) . returns ( ErrorReporter ) }
128+ def resolve_symbol_reporter ( symbol )
129+ case symbol
130+ when :sentry then ErrorReporter ::Sentry
131+ when :bugsnag then ErrorReporter ::Bugsnag
132+ when :rollbar then ErrorReporter ::Rollbar
133+ when :honeybadger then ErrorReporter ::Honeybadger
134+ when :rails_logger then ErrorReporter ::RailsLogger
135+ else
136+ valid_types = ErrorReporter . values . map { |v | ":#{ v . serialize } " } . join ( ", " )
137+ raise ArgumentError , "Unknown reporter type: #{ symbol } . Valid types are: #{ valid_types } "
138+ end
139+ end
140+
141+ sig { params ( callable : T . untyped ) . returns ( CallableReporterWrapper ) }
142+ def wrap_callable_reporter ( callable )
143+ unless callable . respond_to? ( :call )
144+ raise ArgumentError , "Reporter must respond to #call"
145+ end
146+
147+ CallableReporterWrapper . new ( callable )
148+ end
149+
88150 # Report to Sentry
89151 sig { params ( error : StandardError , context : T ::Hash [ T . untyped , T . untyped ] ) . void }
90152 def report_to_sentry ( error , context = { } )
0 commit comments