fluent-plugin-sensu 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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