airbrake-ruby 3.2.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/lib/airbrake-ruby.rb +554 -0
  3. data/lib/airbrake-ruby/async_sender.rb +119 -0
  4. data/lib/airbrake-ruby/backtrace.rb +194 -0
  5. data/lib/airbrake-ruby/code_hunk.rb +53 -0
  6. data/lib/airbrake-ruby/config.rb +238 -0
  7. data/lib/airbrake-ruby/config/validator.rb +63 -0
  8. data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
  9. data/lib/airbrake-ruby/file_cache.rb +48 -0
  10. data/lib/airbrake-ruby/filter_chain.rb +95 -0
  11. data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
  12. data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
  13. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
  14. data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
  15. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +90 -0
  16. data/lib/airbrake-ruby/filters/git_repository_filter.rb +42 -0
  17. data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
  18. data/lib/airbrake-ruby/filters/keys_blacklist.rb +50 -0
  19. data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
  20. data/lib/airbrake-ruby/filters/keys_whitelist.rb +49 -0
  21. data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
  22. data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
  23. data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
  24. data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
  25. data/lib/airbrake-ruby/hash_keyable.rb +37 -0
  26. data/lib/airbrake-ruby/ignorable.rb +44 -0
  27. data/lib/airbrake-ruby/nested_exception.rb +39 -0
  28. data/lib/airbrake-ruby/notice.rb +165 -0
  29. data/lib/airbrake-ruby/notice_notifier.rb +228 -0
  30. data/lib/airbrake-ruby/performance_notifier.rb +161 -0
  31. data/lib/airbrake-ruby/promise.rb +99 -0
  32. data/lib/airbrake-ruby/response.rb +71 -0
  33. data/lib/airbrake-ruby/stat.rb +56 -0
  34. data/lib/airbrake-ruby/sync_sender.rb +111 -0
  35. data/lib/airbrake-ruby/tdigest.rb +393 -0
  36. data/lib/airbrake-ruby/time_truncate.rb +17 -0
  37. data/lib/airbrake-ruby/truncator.rb +115 -0
  38. data/lib/airbrake-ruby/version.rb +6 -0
  39. data/spec/airbrake_spec.rb +171 -0
  40. data/spec/async_sender_spec.rb +154 -0
  41. data/spec/backtrace_spec.rb +438 -0
  42. data/spec/code_hunk_spec.rb +118 -0
  43. data/spec/config/validator_spec.rb +189 -0
  44. data/spec/config_spec.rb +281 -0
  45. data/spec/deploy_notifier_spec.rb +41 -0
  46. data/spec/file_cache.rb +36 -0
  47. data/spec/filter_chain_spec.rb +83 -0
  48. data/spec/filters/context_filter_spec.rb +25 -0
  49. data/spec/filters/dependency_filter_spec.rb +14 -0
  50. data/spec/filters/exception_attributes_filter_spec.rb +63 -0
  51. data/spec/filters/gem_root_filter_spec.rb +44 -0
  52. data/spec/filters/git_last_checkout_filter_spec.rb +48 -0
  53. data/spec/filters/git_repository_filter.rb +53 -0
  54. data/spec/filters/git_revision_filter_spec.rb +126 -0
  55. data/spec/filters/keys_blacklist_spec.rb +236 -0
  56. data/spec/filters/keys_whitelist_spec.rb +205 -0
  57. data/spec/filters/root_directory_filter_spec.rb +42 -0
  58. data/spec/filters/sql_filter_spec.rb +219 -0
  59. data/spec/filters/system_exit_filter_spec.rb +14 -0
  60. data/spec/filters/thread_filter_spec.rb +279 -0
  61. data/spec/fixtures/notroot.txt +7 -0
  62. data/spec/fixtures/project_root/code.rb +221 -0
  63. data/spec/fixtures/project_root/empty_file.rb +0 -0
  64. data/spec/fixtures/project_root/long_line.txt +1 -0
  65. data/spec/fixtures/project_root/short_file.rb +3 -0
  66. data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
  67. data/spec/helpers.rb +9 -0
  68. data/spec/ignorable_spec.rb +14 -0
  69. data/spec/nested_exception_spec.rb +75 -0
  70. data/spec/notice_notifier_spec.rb +436 -0
  71. data/spec/notice_notifier_spec/options_spec.rb +266 -0
  72. data/spec/notice_spec.rb +297 -0
  73. data/spec/performance_notifier_spec.rb +287 -0
  74. data/spec/promise_spec.rb +165 -0
  75. data/spec/response_spec.rb +82 -0
  76. data/spec/spec_helper.rb +102 -0
  77. data/spec/stat_spec.rb +35 -0
  78. data/spec/sync_sender_spec.rb +140 -0
  79. data/spec/tdigest_spec.rb +230 -0
  80. data/spec/time_truncate_spec.rb +13 -0
  81. data/spec/truncator_spec.rb +238 -0
  82. metadata +278 -0
@@ -0,0 +1,119 @@
1
+ module Airbrake
2
+ # Responsible for sending notices to Airbrake asynchronously. The class
3
+ # supports an unlimited number of worker threads and an unlimited queue size
4
+ # (both values are configurable).
5
+ #
6
+ # @see SyncSender
7
+ # @api private
8
+ # @since v1.0.0
9
+ class AsyncSender
10
+ # @param [Airbrake::Config] config
11
+ def initialize(config)
12
+ @config = config
13
+ @unsent = SizedQueue.new(config.queue_size)
14
+ @sender = SyncSender.new(config)
15
+ @closed = false
16
+ @workers = ThreadGroup.new
17
+ @mutex = Mutex.new
18
+ @pid = nil
19
+ end
20
+
21
+ # Asynchronously sends a notice to Airbrake.
22
+ #
23
+ # @param [Airbrake::Notice] notice A notice that was generated by the
24
+ # library
25
+ # @return [Airbrake::Promise]
26
+ def send(notice, promise)
27
+ return will_not_deliver(notice) if @unsent.size >= @unsent.max
28
+
29
+ @unsent << [notice, promise]
30
+ promise
31
+ end
32
+
33
+ # Closes the instance making it a no-op (it shut downs all worker
34
+ # threads). Before closing, waits on all unsent notices to be sent.
35
+ #
36
+ # @return [void]
37
+ # @raise [Airbrake::Error] when invoked more than one time
38
+ def close
39
+ threads = @mutex.synchronize do
40
+ raise Airbrake::Error, 'attempted to close already closed sender' if closed?
41
+
42
+ unless @unsent.empty?
43
+ msg = "#{LOG_LABEL} waiting to send #{@unsent.size} unsent notice(s)..."
44
+ @config.logger.debug(msg + ' (Ctrl-C to abort)')
45
+ end
46
+
47
+ @config.workers.times { @unsent << [:stop, Airbrake::Promise.new] }
48
+ @closed = true
49
+ @workers.list.dup
50
+ end
51
+
52
+ threads.each(&:join)
53
+ @config.logger.debug("#{LOG_LABEL} closed")
54
+ end
55
+
56
+ # Checks whether the sender is closed and thus usable.
57
+ # @return [Boolean]
58
+ def closed?
59
+ @closed
60
+ end
61
+
62
+ # Checks if an active sender has any workers. A sender doesn't have any
63
+ # workers only in two cases: when it was closed or when all workers
64
+ # crashed. An *active* sender doesn't have any workers only when something
65
+ # went wrong.
66
+ #
67
+ # Workers are expected to crash when you +fork+ the process the workers are
68
+ # living in. In this case we detect a +fork+ and try to revive them here.
69
+ #
70
+ # Another possible scenario that crashes workers is when you close the
71
+ # instance on +at_exit+, but some other +at_exit+ hook prevents the process
72
+ # from exiting.
73
+ #
74
+ # @return [Boolean] true if an instance wasn't closed, but has no workers
75
+ # @see https://goo.gl/oydz8h Example of at_exit that prevents exit
76
+ def has_workers?
77
+ @mutex.synchronize do
78
+ return false if @closed
79
+
80
+ if @pid != Process.pid && @workers.list.empty?
81
+ @pid = Process.pid
82
+ spawn_workers
83
+ end
84
+
85
+ !@closed && @workers.list.any?
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def spawn_workers
92
+ @workers = ThreadGroup.new
93
+ @config.workers.times { @workers.add(spawn_worker) }
94
+ @workers.enclose
95
+ end
96
+
97
+ def spawn_worker
98
+ Thread.new do
99
+ while (message = @unsent.pop)
100
+ break if message.first == :stop
101
+ @sender.send(*message)
102
+ end
103
+ end
104
+ end
105
+
106
+ def will_not_deliver(notice)
107
+ backtrace = notice[:errors][0][:backtrace].map do |line|
108
+ "#{line[:file]}:#{line[:line]} in `#{line[:function]}'"
109
+ end
110
+ @config.logger.error(
111
+ "#{LOG_LABEL} AsyncSender has reached its capacity of " \
112
+ "#{@unsent.max} and the following notice will not be delivered " \
113
+ "Error: #{notice[:errors][0][:type]} - #{notice[:errors][0][:message]}\n" \
114
+ "Backtrace: \n" + backtrace.join("\n")
115
+ )
116
+ nil
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,194 @@
1
+ module Airbrake
2
+ # Represents a cross-Ruby backtrace from exceptions (including JRuby Java
3
+ # exceptions). Provides information about stack frames (such as line number,
4
+ # file and method) in convenient for Airbrake format.
5
+ #
6
+ # @example
7
+ # begin
8
+ # raise 'Oops!'
9
+ # rescue
10
+ # Backtrace.parse($!, Logger.new(STDOUT))
11
+ # end
12
+ #
13
+ # @api private
14
+ # @since v1.0.0
15
+ module Backtrace
16
+ module Patterns
17
+ # @return [Regexp] the pattern that matches standard Ruby stack frames,
18
+ # such as ./spec/notice_spec.rb:43:in `block (3 levels) in <top (required)>'
19
+ RUBY = %r{\A
20
+ (?<file>.+) # Matches './spec/notice_spec.rb'
21
+ :
22
+ (?<line>\d+) # Matches '43'
23
+ :in\s
24
+ `(?<function>.*)' # Matches "`block (3 levels) in <top (required)>'"
25
+ \z}x
26
+
27
+ # @return [Regexp] the pattern that matches JRuby Java stack frames, such
28
+ # as org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
29
+ JAVA = %r{\A
30
+ (?<function>.+) # Matches 'org.jruby.ast.NewlineNode.interpret'
31
+ \(
32
+ (?<file>
33
+ (?:uri:classloader:/.+(?=:)) # Matches '/META-INF/jruby.home/protocol.rb'
34
+ |
35
+ (?:uri_3a_classloader_3a_.+(?=:)) # Matches 'uri_3a_classloader_3a_/gems/...'
36
+ |
37
+ [^:]+ # Matches 'NewlineNode.java'
38
+ )
39
+ :?
40
+ (?<line>\d+)? # Matches '105'
41
+ \)
42
+ \z}x
43
+
44
+ # @return [Regexp] the pattern that tries to assume what a generic stack
45
+ # frame might look like, when exception's backtrace is set manually.
46
+ GENERIC = %r{\A
47
+ (?:from\s)?
48
+ (?<file>.+) # Matches '/foo/bar/baz.ext'
49
+ :
50
+ (?<line>\d+)? # Matches '43' or nothing
51
+ (?:
52
+ in\s`(?<function>.+)' # Matches "in `func'"
53
+ |
54
+ :in\s(?<function>.+) # Matches ":in func"
55
+ )? # ... or nothing
56
+ \z}x
57
+
58
+ # @return [Regexp] the pattern that matches exceptions from PL/SQL such as
59
+ # ORA-06512: at "STORE.LI_LICENSES_PACK", line 1945
60
+ # @note This is raised by https://github.com/kubo/ruby-oci8
61
+ OCI = /\A
62
+ (?:
63
+ ORA-\d{5}
64
+ :\sat\s
65
+ (?:"(?<function>.+)",\s)?
66
+ line\s(?<line>\d+)
67
+ |
68
+ #{GENERIC}
69
+ )
70
+ \z/x
71
+
72
+ # @return [Regexp] the pattern that matches CoffeeScript backtraces
73
+ # usually coming from Rails & ExecJS
74
+ EXECJS = /\A
75
+ (?:
76
+ # Matches 'compile ((execjs):6692:19)'
77
+ (?<function>.+)\s\((?<file>.+):(?<line>\d+):\d+\)
78
+ |
79
+ # Matches 'bootstrap_node.js:467:3'
80
+ (?<file>.+):(?<line>\d+):\d+(?<function>)
81
+ |
82
+ # Matches the Ruby part of the backtrace
83
+ #{RUBY}
84
+ )
85
+ \z/x
86
+ end
87
+
88
+ # @return [Integer] how many first frames should include code hunks
89
+ CODE_FRAME_LIMIT = 10
90
+
91
+ # Parses an exception's backtrace.
92
+ #
93
+ # @param [Exception] exception The exception, which contains a backtrace to
94
+ # parse
95
+ # @return [Array<Hash{Symbol=>String,Integer}>] the parsed backtrace
96
+ def self.parse(config, exception)
97
+ return [] if exception.backtrace.nil? || exception.backtrace.none?
98
+ parse_backtrace(config, exception)
99
+ end
100
+
101
+ # Checks whether the given exception was generated by JRuby's VM.
102
+ #
103
+ # @param [Exception] exception
104
+ # @return [Boolean]
105
+ def self.java_exception?(exception)
106
+ if defined?(Java::JavaLang::Throwable) &&
107
+ exception.is_a?(Java::JavaLang::Throwable)
108
+ return true
109
+ end
110
+
111
+ return false unless exception.respond_to?(:backtrace)
112
+
113
+ (Patterns::JAVA =~ exception.backtrace.first) != nil
114
+ end
115
+
116
+ class << self
117
+ private
118
+
119
+ def best_regexp_for(exception)
120
+ if java_exception?(exception)
121
+ Patterns::JAVA
122
+ elsif oci_exception?(exception)
123
+ Patterns::OCI
124
+ elsif execjs_exception?(exception)
125
+ Patterns::EXECJS
126
+ else
127
+ Patterns::RUBY
128
+ end
129
+ end
130
+
131
+ def oci_exception?(exception)
132
+ defined?(OCIError) && exception.is_a?(OCIError)
133
+ end
134
+
135
+ def execjs_exception?(exception)
136
+ return false unless defined?(ExecJS::RuntimeError)
137
+ return true if exception.is_a?(ExecJS::RuntimeError)
138
+ return true if exception.cause && exception.cause.is_a?(ExecJS::RuntimeError)
139
+
140
+ false
141
+ end
142
+
143
+ def stack_frame(config, regexp, stackframe)
144
+ if (match = match_frame(regexp, stackframe))
145
+ return {
146
+ file: match[:file],
147
+ line: (Integer(match[:line]) if match[:line]),
148
+ function: match[:function]
149
+ }
150
+ end
151
+
152
+ config.logger.error(
153
+ "can't parse '#{stackframe}' (please file an issue so we can fix " \
154
+ "it: https://github.com/airbrake/airbrake-ruby/issues/new)"
155
+ )
156
+ { file: nil, line: nil, function: stackframe }
157
+ end
158
+
159
+ def match_frame(regexp, stackframe)
160
+ match = regexp.match(stackframe)
161
+ return match if match
162
+
163
+ Patterns::GENERIC.match(stackframe)
164
+ end
165
+
166
+ def parse_backtrace(config, exception)
167
+ regexp = best_regexp_for(exception)
168
+ root_directory = config.root_directory.to_s
169
+
170
+ exception.backtrace.map.with_index do |stackframe, i|
171
+ frame = stack_frame(config, regexp, stackframe)
172
+ next(frame) if !config.code_hunks || frame[:file].nil?
173
+
174
+ if !root_directory.empty?
175
+ populate_code(config, frame) if frame_in_root?(frame, root_directory)
176
+ elsif i < CODE_FRAME_LIMIT
177
+ populate_code(config, frame)
178
+ end
179
+
180
+ frame
181
+ end
182
+ end
183
+
184
+ def populate_code(config, frame)
185
+ code = Airbrake::CodeHunk.new(config).get(frame[:file], frame[:line])
186
+ frame[:code] = code if code
187
+ end
188
+
189
+ def frame_in_root?(frame, root_directory)
190
+ frame[:file].start_with?(root_directory) && frame[:file] !~ %r{vendor/bundle}
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,53 @@
1
+ module Airbrake
2
+ # Represents a small hunk of code consisting of a base line and a couple lines
3
+ # around it
4
+ # @api private
5
+ class CodeHunk
6
+ # @return [Integer] the maximum length of a line
7
+ MAX_LINE_LEN = 200
8
+
9
+ # @return [Integer] how many lines should be read around the base line
10
+ NLINES = 2
11
+
12
+ def initialize(config)
13
+ @config = config
14
+ end
15
+
16
+ # @param [String] file The file to read
17
+ # @param [Integer] line The base line in the file
18
+ # @return [Hash{Integer=>String}, nil] lines of code around the base line
19
+ def get(file, line)
20
+ return unless File.exist?(file)
21
+ return unless line
22
+
23
+ lines = get_lines(file, [line - NLINES, 1].max, line + NLINES) || {}
24
+ return { 1 => '' } if lines.empty?
25
+
26
+ lines
27
+ end
28
+
29
+ private
30
+
31
+ def get_from_cache(file)
32
+ Airbrake::FileCache[file] ||= File.foreach(file)
33
+ rescue StandardError => ex
34
+ @config.logger.error(
35
+ "#{self.class.name}: can't read code hunk for #{file}: #{ex}"
36
+ )
37
+ nil
38
+ end
39
+
40
+ def get_lines(file, start_line, end_line)
41
+ return unless (cached_file = get_from_cache(file))
42
+
43
+ lines = {}
44
+ cached_file.with_index(1) do |l, i|
45
+ next if i < start_line
46
+ break if i > end_line
47
+
48
+ lines[i] = l[0...MAX_LINE_LEN].rstrip
49
+ end
50
+ lines
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,238 @@
1
+ module Airbrake
2
+ # Represents the Airbrake config. A config contains all the options that you
3
+ # can use to configure an Airbrake instance.
4
+ #
5
+ # @api public
6
+ # @since v1.0.0
7
+ class Config
8
+ # @return [Integer] the project identificator. This value *must* be set.
9
+ # @api public
10
+ attr_accessor :project_id
11
+
12
+ # @return [String] the project key. This value *must* be set.
13
+ # @api public
14
+ attr_accessor :project_key
15
+
16
+ # @return [Hash] the proxy parameters such as (:host, :port, :user and
17
+ # :password)
18
+ # @api public
19
+ attr_accessor :proxy
20
+
21
+ # @return [Logger] the default logger used for debug output
22
+ # @api public
23
+ attr_reader :logger
24
+
25
+ # @return [String] the version of the user's application
26
+ # @api public
27
+ attr_accessor :app_version
28
+
29
+ # @return [Hash{String=>String}] arbitrary versions that your app wants to
30
+ # track
31
+ # @api public
32
+ # @since v2.10.0
33
+ attr_accessor :versions
34
+
35
+ # @return [Integer] the max number of notices that can be queued up
36
+ # @api public
37
+ attr_accessor :queue_size
38
+
39
+ # @return [Integer] the number of worker threads that process the notice
40
+ # queue
41
+ # @api public
42
+ attr_accessor :workers
43
+
44
+ # @return [String] the host, which provides the API endpoint to which
45
+ # exceptions should be sent
46
+ # @api public
47
+ attr_accessor :host
48
+
49
+ # @return [String, Pathname] the working directory of your project
50
+ # @api public
51
+ attr_accessor :root_directory
52
+
53
+ # @return [String, Symbol] the environment the application is running in
54
+ # @api public
55
+ attr_accessor :environment
56
+
57
+ # @return [Array<String,Symbol,Regexp>] the array of environments that
58
+ # forbids sending exceptions when the application is running in them.
59
+ # Other possible environments not listed in the array will allow sending
60
+ # occurring exceptions.
61
+ # @api public
62
+ attr_accessor :ignore_environments
63
+
64
+ # @return [Integer] The HTTP timeout in seconds.
65
+ # @api public
66
+ attr_accessor :timeout
67
+
68
+ # @return [Array<String, Symbol, Regexp>] the keys, which should be
69
+ # filtered
70
+ # @api public
71
+ # @since v1.2.0
72
+ attr_accessor :blacklist_keys
73
+
74
+ # @return [Array<String, Symbol, Regexp>] the keys, which shouldn't be
75
+ # filtered
76
+ # @api public
77
+ # @since v1.2.0
78
+ attr_accessor :whitelist_keys
79
+
80
+ # @return [Boolean] true if the library should attach code hunks to each
81
+ # frame in a backtrace, false otherwise
82
+ # @api public
83
+ # @since v2.5.0
84
+ attr_accessor :code_hunks
85
+
86
+ # @return [Boolean] true if the library should send performance stats
87
+ # information to Airbrake (routes, SQL queries), false otherwise
88
+ # @api public
89
+ # @since v3.2.0
90
+ attr_accessor :performance_stats
91
+
92
+ # @return [Integer] how many seconds to wait before sending collected route
93
+ # stats
94
+ # @api public
95
+ # @since v3.2.0
96
+ attr_accessor :performance_stats_flush_period
97
+
98
+ # @param [Hash{Symbol=>Object}] user_config the hash to be used to build the
99
+ # config
100
+ # rubocop:disable Metrics/AbcSize
101
+ def initialize(user_config = {})
102
+ @validator = Config::Validator.new(self)
103
+
104
+ self.proxy = {}
105
+ self.queue_size = 100
106
+ self.workers = 1
107
+ self.code_hunks = true
108
+
109
+ self.logger = Logger.new(STDOUT)
110
+ logger.level = Logger::WARN
111
+
112
+ self.project_id = user_config[:project_id]
113
+ self.project_key = user_config[:project_key]
114
+ self.host = 'https://api.airbrake.io'
115
+
116
+ self.ignore_environments = []
117
+
118
+ self.timeout = user_config[:timeout]
119
+
120
+ self.blacklist_keys = []
121
+ self.whitelist_keys = []
122
+
123
+ self.root_directory = File.realpath(
124
+ (defined?(Bundler) && Bundler.root) ||
125
+ Dir.pwd
126
+ )
127
+
128
+ self.versions = {}
129
+ self.performance_stats = false
130
+ self.performance_stats_flush_period = 15
131
+
132
+ merge(user_config)
133
+ end
134
+ # rubocop:enable Metrics/AbcSize
135
+
136
+ # The full URL to the Airbrake Notice API. Based on the +:host+ option.
137
+ # @return [URI] the endpoint address
138
+ def endpoint
139
+ @endpoint ||=
140
+ begin
141
+ self.host = ('https://' << host) if host !~ %r{\Ahttps?://}
142
+ api = "api/v3/projects/#{project_id}/notices"
143
+ URI.join(host, api)
144
+ end
145
+ end
146
+
147
+ # Sets the logger. Never allows to assign `nil` as the logger.
148
+ # @return [Logger] the logger
149
+ def logger=(logger)
150
+ @logger = logger || @logger
151
+ end
152
+
153
+ # Merges the given +config_hash+ with itself.
154
+ #
155
+ # @example
156
+ # config.merge(host: 'localhost:8080')
157
+ #
158
+ # @return [self] the merged config
159
+ def merge(config_hash)
160
+ config_hash.each_pair { |option, value| set_option(option, value) }
161
+ self
162
+ end
163
+
164
+ # @return [Boolean] true if the config meets the requirements, false
165
+ # otherwise
166
+ def valid?
167
+ return true if ignored_environment?
168
+
169
+ return false unless @validator.valid_project_id?
170
+ return false unless @validator.valid_project_key?
171
+ return false unless @validator.valid_environment?
172
+
173
+ true
174
+ end
175
+
176
+ def validation_error_message
177
+ @validator.error_message
178
+ end
179
+
180
+ # @return [Boolean] true if the config ignores current environment, false
181
+ # otherwise
182
+ def ignored_environment?
183
+ if ignore_environments.any? && environment.nil?
184
+ logger.warn("#{LOG_LABEL} the 'environment' option is not set, " \
185
+ "'ignore_environments' has no effect")
186
+ end
187
+
188
+ env = environment.to_s
189
+ ignore_environments.any? do |pattern|
190
+ if pattern.is_a?(Regexp)
191
+ env.match(pattern)
192
+ else
193
+ env == pattern.to_s
194
+ end
195
+ end
196
+ end
197
+
198
+ def route_stats
199
+ logger.warn(
200
+ "#{LOG_LABEL} the 'route_stats' option is deprecated. " \
201
+ "Use 'performance_stats' instead"
202
+ )
203
+ @performance_stats
204
+ end
205
+
206
+ def route_stats=(value)
207
+ logger.warn(
208
+ "#{LOG_LABEL} the 'route_stats' option is deprecated. " \
209
+ "Use 'performance_stats_flush_period' instead"
210
+ )
211
+ @performance_stats = value
212
+ end
213
+
214
+ def route_stats_flush_period
215
+ logger.warn(
216
+ "#{LOG_LABEL} the 'route_stats_flush_period' option is deprecated. " \
217
+ "Use 'performance_stats_flush_period' instead"
218
+ )
219
+ @performance_stats_flush_period
220
+ end
221
+
222
+ def route_stats_flush_period=(value)
223
+ logger.warn(
224
+ "#{LOG_LABEL} the 'route_stats_flush_period' option is deprecated. " \
225
+ "Use 'performance_stats' instead"
226
+ )
227
+ @performance_stats_flush_period = value
228
+ end
229
+
230
+ private
231
+
232
+ def set_option(option, value)
233
+ __send__("#{option}=", value)
234
+ rescue NoMethodError
235
+ raise Airbrake::Error, "unknown option '#{option}'"
236
+ end
237
+ end
238
+ end