portertech-sensu 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +961 -0
  3. data/MIT-LICENSE.txt +20 -0
  4. data/README.md +65 -0
  5. data/exe/sensu-api +10 -0
  6. data/exe/sensu-client +10 -0
  7. data/exe/sensu-install +195 -0
  8. data/exe/sensu-server +10 -0
  9. data/lib/sensu/api/http_handler.rb +434 -0
  10. data/lib/sensu/api/process.rb +79 -0
  11. data/lib/sensu/api/routes/aggregates.rb +196 -0
  12. data/lib/sensu/api/routes/checks.rb +44 -0
  13. data/lib/sensu/api/routes/clients.rb +171 -0
  14. data/lib/sensu/api/routes/events.rb +86 -0
  15. data/lib/sensu/api/routes/health.rb +45 -0
  16. data/lib/sensu/api/routes/info.rb +37 -0
  17. data/lib/sensu/api/routes/request.rb +44 -0
  18. data/lib/sensu/api/routes/resolve.rb +32 -0
  19. data/lib/sensu/api/routes/results.rb +153 -0
  20. data/lib/sensu/api/routes/settings.rb +23 -0
  21. data/lib/sensu/api/routes/silenced.rb +182 -0
  22. data/lib/sensu/api/routes/stashes.rb +107 -0
  23. data/lib/sensu/api/routes.rb +88 -0
  24. data/lib/sensu/api/utilities/filter_response_content.rb +44 -0
  25. data/lib/sensu/api/utilities/publish_check_request.rb +107 -0
  26. data/lib/sensu/api/utilities/publish_check_result.rb +39 -0
  27. data/lib/sensu/api/utilities/resolve_event.rb +29 -0
  28. data/lib/sensu/api/utilities/servers_info.rb +43 -0
  29. data/lib/sensu/api/utilities/transport_info.rb +43 -0
  30. data/lib/sensu/api/validators/check.rb +55 -0
  31. data/lib/sensu/api/validators/client.rb +35 -0
  32. data/lib/sensu/api/validators/invalid.rb +8 -0
  33. data/lib/sensu/cli.rb +69 -0
  34. data/lib/sensu/client/http_socket.rb +217 -0
  35. data/lib/sensu/client/process.rb +655 -0
  36. data/lib/sensu/client/socket.rb +207 -0
  37. data/lib/sensu/client/utils.rb +53 -0
  38. data/lib/sensu/client/validators/check.rb +53 -0
  39. data/lib/sensu/constants.rb +17 -0
  40. data/lib/sensu/daemon.rb +396 -0
  41. data/lib/sensu/sandbox.rb +19 -0
  42. data/lib/sensu/server/filter.rb +227 -0
  43. data/lib/sensu/server/handle.rb +201 -0
  44. data/lib/sensu/server/mutate.rb +92 -0
  45. data/lib/sensu/server/process.rb +1646 -0
  46. data/lib/sensu/server/socket.rb +54 -0
  47. data/lib/sensu/server/tessen.rb +170 -0
  48. data/lib/sensu/utilities.rb +398 -0
  49. data/lib/sensu.rb +3 -0
  50. data/sensu.gemspec +36 -0
  51. metadata +322 -0
@@ -0,0 +1,396 @@
1
+ require "rubygems"
2
+
3
+ gem "eventmachine", "1.2.7"
4
+
5
+ gem "portertech-sensu-json", "2.2.1"
6
+ gem "portertech-sensu-logger", "1.4.0"
7
+ gem "portertech-sensu-settings", "10.18.0"
8
+ gem "sensu-extension", "1.5.2"
9
+ gem "portertech-sensu-extensions", "1.12.0"
10
+ gem "sensu-transport", "8.3.0"
11
+ gem "portertech-sensu-spawn", "2.6.1"
12
+ gem "sensu-redis", "2.4.0"
13
+
14
+ require "time"
15
+ require "uri"
16
+
17
+ if RUBY_PLATFORM =~ /aix/ || RUBY_PLATFORM =~ /solaris/
18
+ require "em/pure_ruby"
19
+ end
20
+
21
+ require "sensu/json"
22
+ require "sensu/logger"
23
+ require "sensu/settings"
24
+ require "sensu/extensions"
25
+ require "sensu/transport"
26
+ require "sensu/spawn"
27
+ require "sensu/redis"
28
+
29
+ require "sensu/constants"
30
+ require "sensu/utilities"
31
+ require "sensu/cli"
32
+
33
+ module Sensu
34
+ module Daemon
35
+ include Utilities
36
+
37
+ attr_reader :start_time, :settings
38
+
39
+ # Initialize the Sensu process. Set the start time, initial
40
+ # service state, double the maximum number of EventMachine timers,
41
+ # set up the logger, and load settings. This method will load
42
+ # extensions and setup Sensu Spawn if the Sensu process is not the
43
+ # Sensu API. This method can and optionally daemonize the process
44
+ # and/or create a PID file.
45
+ #
46
+ # @param options [Hash]
47
+ def initialize(options={})
48
+ @start_time = Time.now.to_i
49
+ @state = :initializing
50
+ @timers = {:run => []}
51
+ unless EM::reactor_running?
52
+ EM::epoll
53
+ EM::set_max_timers(200000)
54
+ EM::error_handler do |error|
55
+ unexpected_error(error)
56
+ end
57
+ end
58
+ setup_logger(options)
59
+ load_settings(options)
60
+ unless sensu_service_name == "api"
61
+ load_extensions(options)
62
+ setup_spawn
63
+ end
64
+ setup_process(options)
65
+ end
66
+
67
+ # Handle an unexpected error. This method is used for EM global
68
+ # catch-all error handling, accepting an error object. Error
69
+ # handling is opt-in via a configuration option, e.g. `"sensu":
70
+ # {"global_error_handler": true}`. If a user does not opt-in, the
71
+ # provided error will be raised (uncaught). If a user opts-in via
72
+ # configuration, the error will be logged and ignored :itsfine:.
73
+ #
74
+ # @param error [Object]
75
+ def unexpected_error(error)
76
+ if @settings && @settings[:sensu][:global_error_handler]
77
+ backtrace = error.backtrace.join("\n")
78
+ if @logger
79
+ @logger.warn("global catch-all error handling enabled")
80
+ @logger.fatal("unexpected error - please address this immediately", {
81
+ :error => error.to_s,
82
+ :error_class => error.class,
83
+ :backtrace => backtrace
84
+ })
85
+ else
86
+ puts "global catch-all error handling enabled"
87
+ puts "unexpected error - please address this immediately: #{error.to_s}\n#{error.class}\n#{backtrace}"
88
+ end
89
+ else
90
+ raise error
91
+ end
92
+ end
93
+
94
+ # Set up the Sensu logger and its process signal traps for log
95
+ # rotation and debug log level toggling. This method creates the
96
+ # logger instance variable: `@logger`.
97
+ #
98
+ # https://github.com/sensu/sensu-logger
99
+ #
100
+ # @param options [Hash]
101
+ def setup_logger(options={})
102
+ @logger = Logger.get(options)
103
+ @logger.setup_signal_traps
104
+ end
105
+
106
+ # Log setting or extension loading notices, sensitive information
107
+ # is redacted.
108
+ #
109
+ # @param notices [Array] to be logged.
110
+ # @param level [Symbol] to log the notices at.
111
+ def log_notices(notices=[], level=:warn)
112
+ notices.each do |concern|
113
+ message = concern.delete(:message)
114
+ @logger.send(level, message, redact_sensitive(concern))
115
+ end
116
+ end
117
+
118
+ # Determine if the Sensu settings are valid, if there are load or
119
+ # validation errors, and immediately exit the process with the
120
+ # appropriate exit status code. This method is used to determine
121
+ # if the latest configuration changes are valid prior to
122
+ # restarting the Sensu service, triggered by a CLI argument, e.g.
123
+ # `--validate_config`.
124
+ #
125
+ # @param settings [Object]
126
+ def validate_settings!(settings)
127
+ if settings.errors.empty?
128
+ puts "configuration is valid"
129
+ exit
130
+ else
131
+ puts "configuration is invalid"
132
+ puts Sensu::JSON.dump({:errors => @settings.errors}, :pretty => true)
133
+ exit 2
134
+ end
135
+ end
136
+
137
+ # Print the Sensu settings (JSON) to STDOUT and immediately exit
138
+ # the process with the appropriate exit status code. This method
139
+ # is used while troubleshooting configuration issues, triggered by
140
+ # a CLI argument, e.g. `--print_config`. Sensu settings with
141
+ # sensitive values (e.g. passwords) are first redacted.
142
+ #
143
+ # @param settings [Object]
144
+ def print_settings!(settings)
145
+ redacted_settings = redact_sensitive(settings.to_hash)
146
+ @logger.warn("outputting compiled configuration and exiting")
147
+ puts Sensu::JSON.dump(redacted_settings, :pretty => true)
148
+ exit(settings.errors.empty? ? 0 : 2)
149
+ end
150
+
151
+ # Load Sensu settings. This method creates the settings instance
152
+ # variable: `@settings`. If the `validate_config` option is true,
153
+ # this method calls `validate_settings!()` to validate the latest
154
+ # compiled configuration settings and will then exit the process.
155
+ # If the `print_config` option is true, this method calls
156
+ # `print_settings!()` to output the compiled configuration
157
+ # settings and will then exit the process. If there are loading or
158
+ # validation errors, they will be logged (notices), and this
159
+ # method will exit(2) the process.
160
+ #
161
+ #
162
+ # https://github.com/sensu/sensu-settings
163
+ #
164
+ # @param options [Hash]
165
+ def load_settings(options={})
166
+ @settings = Settings.get(options)
167
+ validate_settings!(@settings) if options[:validate_config]
168
+ log_notices(@settings.warnings)
169
+ log_notices(@settings.errors, :fatal)
170
+ print_settings!(@settings) if options[:print_config]
171
+ unless @settings.errors.empty?
172
+ @logger.fatal("SENSU NOT RUNNING!")
173
+ exit 2
174
+ end
175
+ @settings.set_env!
176
+ end
177
+
178
+ # Load Sensu extensions and log any notices. Set the logger and
179
+ # settings for each extension instance. This method creates the
180
+ # extensions instance variable: `@extensions`.
181
+ #
182
+ # https://github.com/sensu/sensu-extensions
183
+ # https://github.com/sensu/sensu-extension
184
+ #
185
+ # @param options [Hash]
186
+ def load_extensions(options={})
187
+ extensions_options = options.merge(:extensions => @settings[:extensions])
188
+ @extensions = Extensions.get(extensions_options)
189
+ log_notices(@extensions.warnings)
190
+ extension_settings = @settings.to_hash.dup
191
+ @extensions.all.each do |extension|
192
+ extension.logger = @logger
193
+ extension.settings = extension_settings
194
+ end
195
+ end
196
+
197
+ # Set up Sensu spawn, creating a worker to create, control, and
198
+ # limit spawned child processes. This method adjusts the
199
+ # EventMachine thread pool size to accommodate the concurrent
200
+ # process spawn limit and other Sensu process operations.
201
+ #
202
+ # https://github.com/sensu/sensu-spawn
203
+ def setup_spawn
204
+ @logger.info("configuring sensu spawn", :settings => @settings[:sensu][:spawn])
205
+ threadpool_size = @settings[:sensu][:spawn][:limit] + 10
206
+ @logger.debug("setting eventmachine threadpool size", :size => threadpool_size)
207
+ EM::threadpool_size = threadpool_size
208
+ Spawn.setup(@settings[:sensu][:spawn])
209
+ end
210
+
211
+ # Manage the current process, optionally daemonize and/or write
212
+ # the current process ID to a PID file.
213
+ #
214
+ # @param options [Hash]
215
+ def setup_process(options)
216
+ daemonize if options[:daemonize]
217
+ write_pid(options[:pid_file]) if options[:pid_file]
218
+ end
219
+
220
+ # Start the Sensu service and set the service state to `:running`.
221
+ # This method will likely be overridden by a subclass. Yield if a
222
+ # block is provided.
223
+ def start
224
+ @state = :running
225
+ yield if block_given?
226
+ end
227
+
228
+ # Pause the Sensu service and set the service state to `:paused`.
229
+ # This method will likely be overridden by a subclass.
230
+ def pause
231
+ @state = :paused
232
+ end
233
+
234
+ # Resume the paused Sensu service and set the service state to
235
+ # `:running`. This method will likely be overridden by a subclass.
236
+ def resume
237
+ @state = :running
238
+ end
239
+
240
+ # Stop the Sensu service and set the service state to `:stopped`.
241
+ # This method will likely be overridden by a subclass. This method
242
+ # should stop the EventMachine event loop.
243
+ def stop
244
+ @state = :stopped
245
+ @logger.warn("stopping reactor")
246
+ EM::stop_event_loop
247
+ end
248
+
249
+ # Set up process signal traps. This method uses the `STOP_SIGNALS`
250
+ # constant to determine which process signals will result in a
251
+ # graceful service stop. A periodic timer must be used to poll for
252
+ # received signals, as Mutex#lock cannot be used within the
253
+ # context of `trap()`.
254
+ def setup_signal_traps
255
+ @signals = []
256
+ STOP_SIGNALS.each do |signal|
257
+ Signal.trap(signal) do
258
+ @signals << signal
259
+ end
260
+ end
261
+ EM::PeriodicTimer.new(1) do
262
+ signal = @signals.shift
263
+ if STOP_SIGNALS.include?(signal)
264
+ @logger.warn("received signal", :signal => signal)
265
+ stop
266
+ end
267
+ end
268
+ end
269
+
270
+ # Set up the Sensu transport connection. Sensu uses a transport
271
+ # API, allowing it to use various message brokers. By default,
272
+ # Sensu will use the built-in "rabbitmq" transport. The Sensu
273
+ # service will stop gracefully in the event of a transport error,
274
+ # and pause/resume in the event of connectivity issues. This
275
+ # method creates the transport instance variable: `@transport`.
276
+ #
277
+ # https://github.com/sensu/sensu-transport
278
+ #
279
+ # @yield [Object] passes initialized and connected Transport
280
+ # connection object to the callback/block.
281
+ def setup_transport
282
+ transport_name = @settings[:transport][:name]
283
+ transport_settings = @settings[transport_name]
284
+ @logger.debug("connecting to transport", {
285
+ :name => transport_name,
286
+ :settings => transport_settings
287
+ })
288
+ Transport.logger = @logger
289
+ Transport.connect(transport_name, transport_settings) do |connection|
290
+ @transport = connection
291
+ @transport.on_error do |error|
292
+ @logger.error("transport connection error", :error => error.to_s)
293
+ if @settings[:transport][:reconnect_on_error]
294
+ @transport.reconnect
295
+ else
296
+ stop
297
+ end
298
+ end
299
+ @transport.before_reconnect do
300
+ unless testing?
301
+ @logger.warn("reconnecting to transport")
302
+ pause
303
+ end
304
+ end
305
+ @transport.after_reconnect do
306
+ @logger.info("reconnected to transport")
307
+ resume
308
+ end
309
+ yield(@transport) if block_given?
310
+ end
311
+ end
312
+
313
+ # Set up the Redis connection. Sensu uses Redis as a data store,
314
+ # to store the client registry, current events, etc. The Sensu
315
+ # service will stop gracefully in the event of a Redis error, and
316
+ # pause/resume in the event of connectivity issues. This method
317
+ # creates the Redis instance variable: `@redis`.
318
+ #
319
+ # https://github.com/sensu/sensu-redis
320
+ #
321
+ # @yield [Object] passes initialized and connected Redis
322
+ # connection object to the callback/block.
323
+ def setup_redis
324
+ @logger.debug("connecting to redis", :settings => @settings[:redis])
325
+ Redis.logger = @logger
326
+ Redis.connect(@settings[:redis]) do |connection|
327
+ @redis = connection
328
+ @redis.on_error do |error|
329
+ @logger.error("redis connection error", :error => error.to_s)
330
+ end
331
+ @redis.before_reconnect do
332
+ unless testing?
333
+ @logger.warn("reconnecting to redis")
334
+ pause
335
+ end
336
+ end
337
+ @redis.after_reconnect do
338
+ @logger.info("reconnected to redis")
339
+ resume
340
+ end
341
+ yield(@redis) if block_given?
342
+ end
343
+ end
344
+
345
+ private
346
+
347
+ # Get the Sensu service name.
348
+ #
349
+ # @return [String] Sensu service name.
350
+ def sensu_service_name
351
+ File.basename($0).split("-").last
352
+ end
353
+
354
+ # Write the current process ID (PID) to a file (PID file). This
355
+ # method will cause the Sensu service to exit (2) if the PID file
356
+ # cannot be written to.
357
+ #
358
+ # @param file [String] to write the current PID to.
359
+ def write_pid(file)
360
+ begin
361
+ File.open(file, "w") do |pid_file|
362
+ pid_file.puts(Process.pid)
363
+ end
364
+ rescue
365
+ @logger.fatal("could not write to pid file", :pid_file => file)
366
+ @logger.fatal("SENSU NOT RUNNING!")
367
+ exit 2
368
+ end
369
+ end
370
+
371
+ # Daemonize the current process. Seed the random number generator,
372
+ # fork (& exit), detach from controlling terminal, ignore SIGHUP,
373
+ # fork (& exit), use root '/' as the current working directory,
374
+ # and close STDIN/OUT/ERR since the process is no longer attached
375
+ # to a terminal.
376
+ def daemonize
377
+ Kernel.srand
378
+ exit if Kernel.fork
379
+ unless Process.setsid
380
+ @logger.fatal("cannot detach from controlling terminal")
381
+ @logger.fatal("SENSU NOT RUNNING!")
382
+ exit 2
383
+ end
384
+ Signal.trap("SIGHUP", "IGNORE")
385
+ exit if Kernel.fork
386
+ Dir.chdir("/")
387
+ ObjectSpace.each_object(IO) do |io|
388
+ unless [STDIN, STDOUT, STDERR].include?(io)
389
+ begin
390
+ io.close unless io.closed?
391
+ rescue; end
392
+ end
393
+ end
394
+ end
395
+ end
396
+ end
@@ -0,0 +1,19 @@
1
+ module Sensu
2
+ module Sandbox
3
+ # Evaluate a Ruby expression within the context of a simple
4
+ # "sandbox", a Proc in a module method. As of Ruby 2.3.0,
5
+ # `$SAFE` no longer supports levels > 1, so its use has been
6
+ # removed from this method. A single value is provided to the
7
+ # "sandbox".
8
+ #
9
+ # @param expression [String] to be evaluated.
10
+ # @param value [Object] to provide the "sandbox" with.
11
+ # @return [Object]
12
+ def self.eval(expression, value=nil)
13
+ result = Proc.new do
14
+ Kernel.eval(expression)
15
+ end
16
+ result.call
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,227 @@
1
+ module Sensu
2
+ module Server
3
+ module Filter
4
+ # Determine if an event handler is silenced.
5
+ #
6
+ # @param handler [Hash] definition.
7
+ # @param event [Hash]
8
+ # @return [TrueClass, FalseClass]
9
+ def handler_silenced?(handler, event)
10
+ event[:silenced] && !handler[:handle_silenced]
11
+ end
12
+
13
+ # Determine if handling is disabled for an event. Check
14
+ # definitions can disable event handling with an attribute,
15
+ # `:handle`, by setting it to `false`.
16
+ #
17
+ # @param event [Hash]
18
+ # @return [TrueClass, FalseClass]
19
+ def handling_disabled?(event)
20
+ event[:check][:handle] == false
21
+ end
22
+
23
+ # Determine if an event with an action should be handled. An
24
+ # event action of `:flapping` indicates that the event state is
25
+ # flapping, and the event should not be handled unless its
26
+ # handler has `:handle_flapping` set to `true`.
27
+ #
28
+ # @param handler [Hash] definition.
29
+ # @param event [Hash]
30
+ # @return [TrueClass, FalseClass]
31
+ def handle_action?(handler, event)
32
+ event[:action] != :flapping ||
33
+ (event[:action] == :flapping && !!handler[:handle_flapping])
34
+ end
35
+
36
+ # Determine if an event with a check severity will be handled.
37
+ # Event handlers can specify the check severities they will
38
+ # handle, using the definition attribute `:severities`. The
39
+ # possible severities are "ok", "warning", "critical", and
40
+ # "unknown". Handler severity filtering is bypassed when the
41
+ # event `:action` is `:resolve` and a previous check history
42
+ # status identifies a severity specified in the handler
43
+ # definition. It's possible for a check history status of 0 to
44
+ # have had the flapping action, so we are unable to consider
45
+ # every past 0 to indicate a resolution.
46
+ #
47
+ # @param handler [Hash] definition.
48
+ # @param event [Hash]
49
+ # @return [TrueClass, FalseClass]
50
+ def handle_severity?(handler, event)
51
+ if handler.has_key?(:severities)
52
+ case event[:action]
53
+ when :resolve
54
+ event[:check][:history].any? do |status|
55
+ severity = SEVERITIES[status.to_i] || "unknown"
56
+ handler[:severities].include?(severity)
57
+ end
58
+ else
59
+ severity = SEVERITIES[event[:check][:status]] || "unknown"
60
+ handler[:severities].include?(severity)
61
+ end
62
+ else
63
+ true
64
+ end
65
+ end
66
+
67
+ # Determine if a filter is to be evoked for the current time. A
68
+ # filter can be configured with a time window defining when it
69
+ # is to be evoked, e.g. Monday through Friday, 9-5.
70
+ #
71
+ # @param filter [Hash] definition.
72
+ # @return [TrueClass, FalseClass]
73
+ def in_filter_time_windows?(filter)
74
+ if filter[:when]
75
+ in_time_windows?(filter[:when])
76
+ else
77
+ true
78
+ end
79
+ end
80
+
81
+ # Determine if an event is filtered by a native filter.
82
+ #
83
+ # @param filter_name [String]
84
+ # @param event [Hash]
85
+ # @yield [filtered] callback/block called with a single
86
+ # parameter to indicate if the event was filtered.
87
+ # @yieldparam filtered [TrueClass,FalseClass] indicating if the
88
+ # event was filtered.
89
+ # @yieldparam filter_name [String] name of the filter being evaluated
90
+ def native_filter(filter_name, event)
91
+ filter = @settings[:filters][filter_name]
92
+ if in_filter_time_windows?(filter)
93
+ matched = attributes_match?(event, filter[:attributes])
94
+ yield(filter[:negate] ? matched : !matched, filter_name)
95
+ else
96
+ yield(false, filter_name)
97
+ end
98
+ end
99
+
100
+ # Determine if an event is filtered by a filter extension.
101
+ #
102
+ # @param filter_name [String]
103
+ # @param event [Hash]
104
+ # @yield [filtered] callback/block called with a single
105
+ # parameter to indicate if the event was filtered.
106
+ # @yieldparam filtered [TrueClass,FalseClass] indicating if the
107
+ # event was filtered.
108
+ # @yieldparam filter_name [String] name of the filter being evaluated
109
+ def extension_filter(filter_name, event)
110
+ extension = @extensions[:filters][filter_name]
111
+ if in_filter_time_windows?(extension.definition)
112
+ extension.safe_run(event) do |output, status|
113
+ yield(status == 0, filter_name)
114
+ end
115
+ else
116
+ yield(false, filter_name)
117
+ end
118
+ end
119
+
120
+ # Determine if an event is filtered by an event filter, native
121
+ # or extension. This method first checks for the existence of a
122
+ # native filter, then checks for an extension if a native filter
123
+ # is not defined. The provided callback is called with a single
124
+ # parameter, indicating if the event was filtered by a filter.
125
+ # If a filter does not exist for the provided name, the event is
126
+ # not filtered.
127
+ #
128
+ # @param filter_name [String]
129
+ # @param event [Hash]
130
+ # @yield [filtered] callback/block called with a single
131
+ # parameter to indicate if the event was filtered.
132
+ # @yieldparam filtered [TrueClass,FalseClass] indicating if the
133
+ # event was filtered.
134
+ # @yieldparam filter_name [String] name of the filter being evaluated
135
+ def event_filter(filter_name, event)
136
+ case
137
+ when @settings.filter_exists?(filter_name)
138
+ native_filter(filter_name, event) do |filtered|
139
+ yield(filtered, filter_name)
140
+ end
141
+ when @extensions.filter_exists?(filter_name)
142
+ extension_filter(filter_name, event) do |filtered|
143
+ yield(filtered, filter_name)
144
+ end
145
+ else
146
+ @logger.error("unknown filter", :filter_name => filter_name)
147
+ yield(false, filter_name)
148
+ end
149
+ end
150
+
151
+ # Determine if an event is filtered for a handler. If a handler
152
+ # specifies one or more filters, via `:filters` or `:filter`,
153
+ # the `event_filter()` method is called for each of them. If any
154
+ # of the filters return `true`, the event is filtered for the
155
+ # handler. If no filters are defined in the handler definition,
156
+ # the event is not filtered.
157
+ #
158
+ # @param handler [Hash] definition.
159
+ # @param event [Hash]
160
+ # @yield [filtered] callback/block called with a single
161
+ # parameter to indicate if the event was filtered.
162
+ # @yieldparam filtered [TrueClass,FalseClass] indicating if the
163
+ # event was filtered.
164
+ def event_filtered?(handler, event)
165
+ if handler.has_key?(:filters) || handler.has_key?(:filter)
166
+ filter_list = Array(handler[:filters] || handler[:filter]).dup
167
+ filter = Proc.new do |filter_list|
168
+ filter_name = filter_list.shift
169
+ if filter_name.nil?
170
+ yield(false)
171
+ else
172
+ event_filter(filter_name, event) do |filtered|
173
+ filtered ? yield(true, filter_name) : EM.next_tick { filter.call(filter_list) }
174
+ end
175
+ end
176
+ end
177
+ filter.call(filter_list)
178
+ else
179
+ yield(false)
180
+ end
181
+ end
182
+
183
+ # Attempt to filter an event for a handler. This method will
184
+ # check to see if handling is disabled, if the event action is
185
+ # handled, if the event check severity is handled, if the
186
+ # handler is subdued, and if the event is filtered by any of the
187
+ # filters specified in the handler definition.
188
+ #
189
+ # @param handler [Hash] definition.
190
+ # @param event [Hash]
191
+ # @yield [event] callback/block called if the event has not been
192
+ # filtered.
193
+ # @yieldparam event [Hash]
194
+ def filter_event(handler, event)
195
+ handler_info = case handler[:type]
196
+ when "extension" then handler.definition
197
+ else handler
198
+ end
199
+ details = {:handler => handler_info, :event => event}
200
+ filter_message = case
201
+ when handling_disabled?(event)
202
+ "event handling disabled for event"
203
+ when !handle_action?(handler, event)
204
+ "handler does not handle action"
205
+ when !handle_severity?(handler, event)
206
+ "handler does not handle event severity"
207
+ when handler_silenced?(handler, event)
208
+ "handler is silenced"
209
+ end
210
+ if filter_message
211
+ @logger.info(filter_message, details)
212
+ @in_progress[:events] -= 1 if @in_progress
213
+ else
214
+ event_filtered?(handler, event) do |filtered, filter_name|
215
+ unless filtered
216
+ yield(event)
217
+ else
218
+ details[:filter] = filter_name
219
+ @logger.info("event was filtered", details)
220
+ @in_progress[:events] -= 1 if @in_progress
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end