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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +344 -0
- data/Rakefile +13 -0
- data/fluent-plugin-sensu.gemspec +28 -0
- data/lib/fluent/plugin/out_sensu.rb +288 -0
- data/test/helper.rb +33 -0
- data/test/plugin/test_out_sensu.rb +671 -0
- metadata +143 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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
|