fluent-plugin-sensu 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-sensu.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015- MIYAKAWA Taku
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,344 @@
1
+ # fluent-plugin-sensu
2
+
3
+ [Fluentd](http://fluentd.org) output plugin to send check results to
4
+ [sensu-client](https://sensuapp.org/docs/latest/clients),
5
+ which is an agent process of Sensu monitoring framework.
6
+
7
+ ## Configuration
8
+
9
+ ### Example configuration
10
+
11
+ ```apache
12
+ <match ddos>
13
+ type sensu
14
+
15
+ # Connection settings
16
+ server localhost
17
+ port 3030
18
+
19
+ # Payload settings
20
+
21
+ ## The check is named "ddos_detection"
22
+ check_name ddos_detection
23
+
24
+ ## The severity is read from the field "level"
25
+ check_status_field level
26
+ </match>
27
+ ```
28
+
29
+ ### Plugin type
30
+
31
+ Tye type of this plugin is `sensu`.
32
+ Specify `type sensu` in the match section.
33
+
34
+ ### Connection setting
35
+
36
+ * `server` (default is "localhost")
37
+ * The IP address or the hostname of the host running sensu-client daemon.
38
+ * `port` (default is 3030)
39
+ * The TCP port number of the [Sensu client socket](
40
+ https://sensuapp.org/docs/latest/clients#client-socket-input)
41
+ on which sensu-client daemon is listening.
42
+
43
+ ### Check payload setting
44
+
45
+ The payload of a check result is a JSON object
46
+ which contains attributes as follows.
47
+ Attributes are indicated
48
+ by [JSONPath](http://goessner.net/articles/JsonPath/) expressions.
49
+
50
+ * `$.name`
51
+ * The check name to identify the check.
52
+ * `$.output`
53
+ * An arbitrary string to describe the check result.
54
+ * This attribute is often used to contain metric values.
55
+ * `$.status`
56
+ * The severity of the check result.
57
+ * 0 (OK), 1 (WARNING), 2 (CRITICAL) or 3 (UNKNOWN or CUSTOM).
58
+
59
+ The check result can also contain other attributes.
60
+ This plugin supports the attributes below.
61
+
62
+ * `$.type`
63
+ * Either "standard" or "metric".
64
+ * If the attribute is set to "standard", the sensu-server creates an event
65
+ only when the status is not OK or when the status is changed to OK.
66
+ * If the attribute is set to "metric", the sensu-server creates an event
67
+ even if the status is OK.
68
+ It is useful when Sensu sends check results to metrics collectors
69
+ such as [Graphite](http://graphite.wikidot.com/).
70
+ * `$.ttl`
71
+ * The time to live (TTL) in seconds,
72
+ until the check result is considered stale.
73
+ If TTL expires, sensu-server creates an event.
74
+ * This attribute is useful when you want to be notified
75
+ when logs are not output for a certain period.
76
+ * Same as `freshness_threshold` in Nagios.
77
+ * `$.handlers`
78
+ * The names of handlers which process events created for the check.
79
+ * `$.low_flap_threshold` and `$.high_flap_threshold`
80
+ * Threshold percentages to determine the status is considered "flapping,"
81
+ or the state is changed too frequently.
82
+ * Same as the options in Nagios.
83
+ See the description of [Flap Detection](
84
+ https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/flapping.html)
85
+ in Nagios.
86
+ * `$.source`
87
+ * The source of the check, such as servers or network switches.
88
+ * If this attribute is not specified,
89
+ the host of sensu-client is considered as the source.
90
+ * `$.executed`
91
+ * The timestamp on which the check is executed.
92
+ * Note that there is also another timestamp attribute named `issued`,
93
+ which is automatically measured by sensu-client process.
94
+ Uchiwa, the default dashboard of Sensu, displays `issued`
95
+ as the timestamp of check results.
96
+
97
+ This plugin additionally adds "fluentd" attribute to the check result.
98
+ The value of the attribute is a JSON object
99
+ whoes elements are input to the plugin.
100
+
101
+ * `$.fluentd.tag`
102
+ * The tag of the Fluentd data.
103
+ * `$.fluentd.time`
104
+ * The time of the Fluentd data, in seconds since the Unix epoch.
105
+ * `$.fluentd.record`
106
+ * The record of the Fluentd data.
107
+
108
+ #### `name` attribute
109
+
110
+ The check name is determined as below.
111
+
112
+ 1. The field specified by `check_name_field` option,
113
+ if present and valid (highest priority)
114
+ * The valid values are strings composed of ASCII alphanumerics,
115
+ underscores, periods, and hyphens.
116
+ 2. or `check_name` option, if present
117
+ * The valid values are same as above.
118
+ 3. or the tag name, if valid
119
+ * The valid values are same as above.
120
+ 4. or "fluent-plugin-sensu" (lowest priority)
121
+
122
+ #### `output` attribute
123
+
124
+ The check output is determined as below.
125
+
126
+ 1. The field specified by `check_output_field` option,
127
+ if present (highest priority)
128
+ 2. or `check_output` option, if present
129
+ 3. or JSON notation of the record (lowest priority)
130
+
131
+ #### `status` attribute
132
+
133
+ The severity of the check result is determined as below.
134
+
135
+ 1. The field specified by `check_status_field` option,
136
+ if present and permitted (highest priority)
137
+ * The values permitted to the field for each status (case insensitive):
138
+ * status 0: an integer ``0``
139
+ and strings ``"0"``, ``"OK"``
140
+ * status 1: an integer ``1``
141
+ and strings ``"1"``, ``"WARNING"``, ``"warn"``
142
+ * status 2: an integer ``2``
143
+ and strings ``"2"``, ``"CRITICAL"``, ``"crit"``
144
+ * status 3: an integer ``3``
145
+ and strings ``"3"``, ``"UNKNOWN"``, ``"CUSTOM"``
146
+ 2. or `check_status` option, if present
147
+ * The permitted values for each status (case insensitive):
148
+ * status 0: ``0`` and ``OK``
149
+ * status 1: ``1``, ``WARNING``, ``warn``
150
+ * status 2: ``2``, ``CRITICAL``, ``crit``
151
+ * status 3: ``3``, ``UNKNOWN``, ``CUSTOM``
152
+ * If the value is not permitted, it causes a configuration error.
153
+ 3. or `3`, which means UNKNOWN or CUSTOM (lowest priority)
154
+
155
+ "warn" and "crit" come from
156
+ [fluent-plugin-notifier](https://github.com/tagomoris/fluent-plugin-notifier).
157
+
158
+ #### `type` attribute
159
+
160
+ The check type is determined as below.
161
+
162
+ 1. `check_type` option (highest priority)
163
+ * The value must be a string ``"standard"`` or ``"metric"``.
164
+ 2. or "standard" (lowest priority)
165
+
166
+ #### `ttl` attribute
167
+
168
+ The TTL seconds till expiration is determined as below.
169
+
170
+ 1. `check_ttl` option (highest priority)
171
+ * The value must be an integer which represents the TTL seconds.
172
+ 2. or N/A (lowest priority)
173
+ * It means no expiration detection is performed.
174
+
175
+ #### `handlers` attributes
176
+
177
+ The handlers which process check results are determined as below.
178
+
179
+ 1. `check_handlers` option (highest priority)
180
+ * The value must be an array of strings which represent handler names.
181
+ 2. or `["default"]` (lowest priority)
182
+
183
+ #### `low_flap_threshold` and `high_flap_threshold` attributes
184
+
185
+ The threshold percentages for flap detection are determined as below.
186
+
187
+ 1. `check_low_flap_threshold`
188
+ and `check_high_flap_threshold` options (highest priority)
189
+ * The values must be integers of threshold percentages.
190
+ 2. or N/A (lowest priority)
191
+ * It means no flap detection is performed.
192
+
193
+ The two options either must be specified together,
194
+ not specified at all.
195
+
196
+ If the options are specified,
197
+ the following condition must be true:
198
+ `0 <= check_low_flap_threshold <= check_high_flap_threshold <= 100`.
199
+
200
+ #### `source` attribute
201
+
202
+ The source of the checks is determined as below.
203
+
204
+ 1. The field specified by `check_source_field` option,
205
+ if present and valid (highest priority)
206
+ 2. or `check_source` option
207
+ 3. or N/A (lowest priority)
208
+ * It means the host of sensu-client is considered as the check source.
209
+
210
+ #### `executed` attribute
211
+
212
+ The executed timestamp is determined as below.
213
+
214
+ 1. The field specified by `check_executed_field`
215
+ if present and valid (highest priority)
216
+ * The value must be an integer
217
+ which represents seconds since the Unix epoch.
218
+ 2. The time of the Fluentd record (lowest priority)
219
+
220
+ ### Buffering
221
+
222
+ The default value of `flush_interval` option is set to 1 second.
223
+ It means that check results are delayed at most 1 second
224
+ before being sent.
225
+
226
+ Except for `flush_interval`,
227
+ the plugin uses default options
228
+ for buffered output plugins (defined in Fluent::BufferedOutput class).
229
+
230
+ You can override buffering options in the configuration.
231
+ For example:
232
+
233
+ ```apache
234
+ <match ddos>
235
+ type sensu
236
+ ...snip...
237
+ buffer_type file
238
+ buffer_path /var/lib/fluentd/buffer/ddos
239
+ flush_interval 0.1
240
+ try_flush_interval 0.1
241
+ </match>
242
+ ```
243
+
244
+ ## Use case: "too many server errors" alert
245
+
246
+ #### Situation
247
+
248
+ Assume you have a web server which runs:
249
+
250
+ * Apache HTTP server
251
+ * Fluentd
252
+ * sensu-client
253
+ * which listens to the TCP port 3030 for [Sensu client socket](
254
+ https://sensuapp.org/docs/latest/clients#client-socket-input).
255
+
256
+ You want to be notified when Apache responds too many server errors,
257
+ for example 5 errors per minute as WARNING,
258
+ and 50 errors per minute as CRITICAL.
259
+
260
+ #### Configuration
261
+
262
+ The setting for Fluentd utilizes
263
+ [fluent-plugin-datacounter](https://github.com/tagomoris/fluent-plugin-datacounter),
264
+ [fluent-plugin-record-reformer](https://github.com/sonots/fluent-plugin-record-reformer),
265
+ and of course [fluent-plugin-sensu](https://github.com/miyakawataku/fluent-plugin-sensu).
266
+ Install those plugins and add configuration as below.
267
+
268
+ ```apache
269
+ # Parse Apache access log
270
+ <source>
271
+ type tail
272
+ tag access
273
+ format apache2
274
+
275
+ # The paths vary by setup
276
+ path /var/log/httpd/access_log
277
+ pos_file /var/lib/fluentd/pos/httpd-access_log.pos
278
+ </source>
279
+
280
+ # Count 5xx errors per minute
281
+ <match access>
282
+ type datacounter
283
+ tag count.access
284
+ unit minute
285
+ aggregate all
286
+ count_key code
287
+ pattern1 error ^5\d\d$
288
+ </match>
289
+
290
+ # Calculate the severity level
291
+ <match count.access>
292
+ type record_reformer
293
+ tag server_errors
294
+ enable_ruby true
295
+ <record>
296
+ level ${error_count < 5 ? 'OK' : error_count < 50 ? 'WARNING' : 'CRITICAL'}
297
+ </record>
298
+ </match>
299
+
300
+ # Send checks to sensu-client
301
+ <match server_errors>
302
+ type sensu
303
+ server localhost
304
+ port 3030
305
+
306
+ check_name server_errors
307
+ check_type standard
308
+ check_status_field level
309
+ check_ttl 100
310
+ </match>
311
+ ```
312
+
313
+ The TTL is set to 100 seconds here,
314
+ because the check must be sent for each 60 seconds,
315
+ plus 40 seconds as a margin.
316
+
317
+ ### Alternatives
318
+
319
+ You can use [record\_transformer](
320
+ http://docs.fluentd.org/articles/filter_record_transformer) filter
321
+ instead of `fluent-plugin-record-reformer`
322
+ on Fluentd 0.12.0 and above.
323
+
324
+ If you are concerned with scalability,
325
+ [fluent-plugin-norikra](https://github.com/norikra/fluent-plugin-norikra)
326
+ may be a better option than datacounter and record\_reformer.
327
+
328
+ Another alternative configuration for the use case is
329
+ sending the error count to Graphite using [fluent-plugin-graphite](
330
+ https://github.com/studio3104/fluent-plugin-graphite),
331
+ and making Sensu monitor the value on Graphite with [check-data.rb](
332
+ https://github.com/sensu/sensu-community-plugins/blob/master/plugins/graphite/check-data.rb).
333
+
334
+ ## Installation
335
+
336
+ Install `fluent-plugin-sensu` gem.
337
+
338
+ # Contributing
339
+
340
+ Submit an [issue](https://github.com/miyakawataku/fluent-plugin-sensu/issues)
341
+ or a [pull request](https://github.com/miyakawataku/fluent-plugin-sensu/pulls).
342
+
343
+ Feedback to [@miyakawa\_taku](https://twitter.com/miyakawa_taku) on Twitter
344
+ is also welcome.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # vim: et sw=2 sts=2
3
+
4
+ require "bundler/gem_tasks"
5
+ require 'rake/testtask'
6
+
7
+ Rake::TestTask.new(:test) do |test|
8
+ test.libs << 'lib' << 'test'
9
+ test.pattern = 'test/**/test_*.rb'
10
+ test.verbose = true
11
+ end
12
+
13
+ task :default => :test
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ # vim: et sw=2 sts=2
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "fluent-plugin-sensu"
9
+ spec.version = '0.0.1'
10
+ spec.authors = ["MIYAKAWA Taku"]
11
+ spec.email = ["miyakawa.taku@gmail.com"]
12
+ spec.description = 'Fluentd output plugin to send checks' +
13
+ ' to sensu-client.'
14
+ spec.summary = spec.description
15
+ spec.homepage = ""
16
+ spec.license = "Apache License, v2.0"
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rr"
27
+ spec.add_runtime_dependency "fluentd"
28
+ end
@@ -0,0 +1,288 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # vim: et sw=2 sts=2
3
+
4
+ module Fluent
5
+
6
+ # Fluentd output plugin to send checks to sensu-client.
7
+ class SensuOutput < Fluent::BufferedOutput
8
+ Fluent::Plugin.register_output('sensu', self)
9
+
10
+ #
11
+ # Config parameters
12
+ #
13
+
14
+ # The IP address or the hostname of the host running sensu-client.
15
+ config_param :server, :string, :default => 'localhost'
16
+
17
+ # The port to which sensu-client is listening.
18
+ config_param :port, :integer, :default => 3030
19
+
20
+ # Options for "name" attribute.
21
+ config_param :check_name, :string, :default => nil
22
+ config_param :check_name_field, :string, :default => nil
23
+
24
+ # Pattern for check names.
25
+ CHECK_NAME_PATTERN = /\A[\w.-]+\z/
26
+
27
+ # Options for "output" attribute.
28
+ config_param :check_output_field, :string, :default => nil
29
+ config_param :check_output, :string, :default => nil
30
+
31
+ # Options for "status" attribute.
32
+ config_param :check_status_field, :string, :default => nil
33
+ config_param(:check_status, :default => 3) { |status_str|
34
+ check_status = SensuOutput.normalize_status(status_str)
35
+ if not check_status
36
+ raise Fluent::ConfigError,
37
+ "invalid 'check_status': #{status_str}; 'check_status' must be" +
38
+ " 0/1/2/3, OK/WARNING/CRITICAL/CUSTOM, warn/crit"
39
+ end
40
+ check_status
41
+ }
42
+
43
+ # Pattern for OK status.
44
+ OK_PATTERN = /\A(0|OK)\z/i
45
+
46
+ # Pattern for WARNING status.
47
+ WARNING_PATTERN = /\A(1|WARNING|warn)\z/i
48
+
49
+ # Pattern for CRITICAL status.
50
+ CRITICAL_PATTERN = /\A(2|CRITICAL|crit)\z/i
51
+
52
+ # Pattern for UNKNOWN status.
53
+ UNKNOWN_PATTERN = /\A(3|UNKNOWN|CUSTOM)\z/i
54
+
55
+ # Option for "type" attribute.
56
+ config_param(:check_type, :default => 'standard') { |check_type|
57
+ if not ['standard', 'metric'].include?(check_type)
58
+ raise Fluent::ConfigError,
59
+ "invalid 'check_type': #{check_type}; 'check_type' must be" +
60
+ ' either "standard" or "metric".'
61
+ end
62
+ check_type
63
+ }
64
+
65
+ # Option for "ttl" attribute.
66
+ config_param :check_ttl, :integer, :default => nil
67
+
68
+ # Option for "handlers" attribute.
69
+ config_param(:check_handlers, :default => ['default']) { |handlers_str|
70
+ error_message = "invalid 'check_handlers': #{handlers_str};" +
71
+ " 'check_handlers' must be an array of strings."
72
+ obj = JSON.load(handlers_str)
73
+ raise Fluent::ConfigError, error_message if not obj.is_a?(Array)
74
+ array = obj
75
+ all_string_elements = array.all? { |e| e.is_a?(String) }
76
+ raise Fluent::ConfigError, error_message if not all_string_elements
77
+ array
78
+ }
79
+
80
+ # Options for flapping thresholds.
81
+ config_param :check_low_flap_threshold, :integer, :default => nil
82
+ config_param :check_high_flap_threshold, :integer, :default => nil
83
+
84
+ # Options for "source" attribute.
85
+ config_param :check_source_field, :string, :default => nil
86
+ config_param :check_source, :string, :default => nil
87
+
88
+ # Options for "executed" attribute.
89
+ config_param :check_executed_field, :string, :default => nil
90
+
91
+ # Load modules.
92
+ private
93
+ def initialize
94
+ super
95
+ require 'json'
96
+ end
97
+
98
+ # Read and validate the configuration.
99
+ public
100
+ def configure(conf)
101
+ super
102
+ reject_invalid_check_name
103
+ reject_invalid_flapping_thresholds
104
+ end
105
+
106
+ # Reject check_name option if invalid
107
+ private
108
+ def reject_invalid_check_name
109
+ if @check_name && @check_name !~ CHECK_NAME_PATTERN
110
+ raise ConfigError,
111
+ "check_name must be a string consisting of one or more" +
112
+ " ASCII alphanumerics, underscores, periods, and hyphens"
113
+ end
114
+ end
115
+
116
+ # Reject invalid check_low_flap_threshold and check_high_flap_threshold
117
+ private
118
+ def reject_invalid_flapping_thresholds
119
+ if @check_low_flap_threshold.nil? ^ @check_high_flap_threshold.nil?
120
+ raise ConfigError,
121
+ "'check_low_flap_threshold' and 'check_high_flap_threshold'" +
122
+ " specified togher, or not specified at all."
123
+ end
124
+ if @check_low_flap_threshold
125
+ in_order = 0 <= @check_low_flap_threshold &&
126
+ @check_low_flap_threshold <= @check_high_flap_threshold &&
127
+ @check_high_flap_threshold <= 100
128
+ if not in_order
129
+ raise ConfigError,
130
+ "the following condition must be true:" +
131
+ " 0 <= check_low_flap_threshold <= check_high_flap_threshold <= 100"
132
+ end
133
+ end
134
+ end
135
+
136
+ # Pack the tuple (tag, time, record).
137
+ public
138
+ def format(tag, time, record)
139
+ [tag, time, record].to_msgpack
140
+ end
141
+
142
+ # Send a check
143
+ public
144
+ def write(chunk)
145
+ results = []
146
+ chunk.msgpack_each { |(tag, time, record)|
147
+ payload = {
148
+ 'name' => determine_check_name(tag, record),
149
+ 'output' => determine_output(tag, record),
150
+ 'status' => determine_status(tag, record),
151
+ 'type' => @check_type,
152
+ 'handlers' => @check_handlers,
153
+ 'executed' => determine_executed_time(tag, time, record),
154
+ 'fluentd' => {
155
+ 'tag' => tag,
156
+ 'time' => time.to_i,
157
+ 'record' => record,
158
+ },
159
+ }
160
+ add_attribute_if_present(
161
+ payload, 'ttl', @check_ttl)
162
+ add_attribute_if_present(
163
+ payload, 'low_flap_threshold', @check_low_flap_threshold)
164
+ add_attribute_if_present(
165
+ payload, 'high_flap_threshold', @check_high_flap_threshold)
166
+ add_attribute_if_present(
167
+ payload, 'source', determine_source(tag, record))
168
+ send_check(@server, @port, payload)
169
+ }
170
+ end
171
+
172
+ # Send a check to sensu-client.
173
+ private
174
+ def send_check(server, port, payload)
175
+ json = payload.to_json
176
+ sensu_client = TCPSocket.open(@server, @port)
177
+ begin
178
+ sensu_client.puts(json)
179
+ ensure
180
+ sensu_client.close
181
+ end
182
+ end
183
+
184
+ # Adds an attribute to the payload if present.
185
+ private
186
+ def add_attribute_if_present(payload, name, value)
187
+ payload[name] = value if value
188
+ end
189
+
190
+ # Determines "name" attribute of a check.
191
+ private
192
+ def determine_check_name(tag, record)
193
+ # Field specified by check_name_field option
194
+ if @check_name_field
195
+ check_name = record[@check_name_field]
196
+ return check_name if check_name =~ CHECK_NAME_PATTERN
197
+ log.warn('Invalid check name in the field.' +
198
+ ' Fallback to check_name option, tag,' +
199
+ ' or constant "fluent-plugin-sensu".',
200
+ :tag => tag,
201
+ :check_name_field => @check_name_field,
202
+ :value => check_name)
203
+ # Fall through
204
+ end
205
+ # check_name option
206
+ return @check_name if @check_name
207
+ # Tag
208
+ return tag if tag =~ CHECK_NAME_PATTERN
209
+ # Default value
210
+ log.warn('Invalid check name in the tag.' +
211
+ 'Fallback to the constant "fluent-plugin-sensu".',
212
+ :tag => tag)
213
+ return 'fluent-plugin-sensu'
214
+ end
215
+
216
+ # Determines "output" attribute of a check.
217
+ private
218
+ def determine_output(tag, record)
219
+ # Read from the field
220
+ if @check_output_field
221
+ check_output = record[@check_output_field]
222
+ return check_output if check_output
223
+ log.warn('the field for "output" attribute is absent',
224
+ :tag => tag, :check_output_field => @check_output_field)
225
+ # Fall through
226
+ end
227
+ # Returns the option value
228
+ return @check_output if @check_output
229
+ # Default to JSON notation of the record
230
+ return record.to_json
231
+ end
232
+
233
+ # Determines "status" attribute of a check.
234
+ private
235
+ def determine_status(tag, record)
236
+ # Read from the field
237
+ if @check_status_field
238
+ status_field_val = record[@check_status_field]
239
+ if status_field_val
240
+ check_status = SensuOutput.normalize_status(status_field_val)
241
+ return check_status if check_status
242
+ end
243
+ log.warn('the field for "status" attribute is invalid',
244
+ :tag => tag, :check_status_field => @check_status_field,
245
+ :value => status_field_val)
246
+ end
247
+ # Returns the default
248
+ return @check_status
249
+ end
250
+
251
+ # Converts variations of status values to an integer.
252
+ private
253
+ def self.normalize_status(status_val)
254
+ status_str = status_val.to_s
255
+ return 0 if status_str =~ OK_PATTERN
256
+ return 1 if status_str =~ WARNING_PATTERN
257
+ return 2 if status_str =~ CRITICAL_PATTERN
258
+ return 3 if status_str =~ UNKNOWN_PATTERN
259
+ return nil
260
+ end
261
+
262
+ # Determines "source" attribute of a check.
263
+ private
264
+ def determine_source(tag, record)
265
+ if @check_source_field
266
+ source = record[@check_source_field]
267
+ return source if source
268
+ log.warn('the field for "source" attribute is absent',
269
+ :tag => tag, :check_source_field => @check_source_field)
270
+ end
271
+ return @check_source
272
+ end
273
+
274
+ # Determines "executed" attribute of a check.
275
+ private
276
+ def determine_executed_time(tag, time, record)
277
+ if @check_executed_field
278
+ executed = record[@check_executed_field]
279
+ return executed if executed.is_a?(Integer)
280
+ log.warn('the field for "executed" attribute is absent',
281
+ :tag => tag, :check_executed_field => @check_executed_field)
282
+ end
283
+ return time
284
+ end
285
+
286
+ end
287
+
288
+ end