sensu-plugins-graphite-boutetnico 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,158 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-stats
4
+ #
5
+ # DESCRIPTION:
6
+ # Checks metrics in Graphite, averaged over a period of time.
7
+ #
8
+ # The fired Sensu event will only be critical if a stat is
9
+ # above the critical threshold. Otherwise, the event will be warning,
10
+ # if a stat is above the warning threshold.
11
+ #
12
+ # Multiple stats will be checked if * are used
13
+ # in the "target" query.
14
+ #
15
+ # OUTPUT:
16
+ # plain text
17
+ #
18
+ # PLATFORMS:
19
+ # Linux
20
+ #
21
+ # DEPENDENCIES:
22
+ # gem: sensu-plugin
23
+ #
24
+ # USAGE:
25
+ # example commands
26
+ #
27
+ # NOTES:
28
+ #
29
+ # LICENSE:
30
+ # Alan Smith (alan@asmith.me)
31
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
32
+ # for details.
33
+ #
34
+
35
+ require 'json'
36
+ require 'net/http'
37
+ require 'sensu-plugin/check/cli'
38
+
39
+ class CheckGraphiteStat < Sensu::Plugin::Check::CLI
40
+ option :host,
41
+ short: '-h HOST',
42
+ long: '--host HOST',
43
+ description: 'Graphite hostname',
44
+ proc: proc(&:to_s),
45
+ default: 'graphite'
46
+
47
+ option :period,
48
+ short: '-p PERIOD',
49
+ long: '--period PERIOD',
50
+ description: 'The period back in time to extract from Graphite. Use -24hours, -2days, -15mins, etc, same format as in Graphite',
51
+ proc: proc(&:to_s),
52
+ required: true
53
+
54
+ option :target,
55
+ short: '-t TARGET',
56
+ long: '--target TARGET',
57
+ description: 'The Graphite metric name. Can include * to query multiple metrics',
58
+ proc: proc(&:to_s),
59
+ required: true
60
+
61
+ option :warn,
62
+ short: '-w WARN',
63
+ long: '--warn WARN',
64
+ description: 'Warning level',
65
+ proc: proc(&:to_f),
66
+ required: false
67
+
68
+ option :crit,
69
+ short: '-c CRIT',
70
+ long: '--crit CRIT',
71
+ description: 'Critical level',
72
+ proc: proc(&:to_f),
73
+ required: false
74
+
75
+ option :unknown_ignore,
76
+ short: '-u',
77
+ long: '--unknown-ignore',
78
+ description: "Do nothing for UNKNOWN status (when you wildcard-match a ton of metrics at once and you don't care about a few missing data)",
79
+ boolean: true,
80
+ default: false
81
+
82
+ option :reverse_scale,
83
+ short: '-r',
84
+ long: '--reverse-scale',
85
+ description: 'Reverse the warn/crit scale (if value is less than instead of greater than)',
86
+ boolean: true,
87
+ default: false
88
+
89
+ def average(datapoints)
90
+ total = 0
91
+ datapoints.to_a.each { |i| total += i.to_f }
92
+
93
+ total / datapoints.length
94
+ end
95
+
96
+ def danger(metric)
97
+ datapoints = metric['datapoints'].map(&:first).compact
98
+
99
+ # #YELLOW
100
+ unless datapoints.empty? # rubocop:disable Style/UnlessElse
101
+ avg = average(datapoints)
102
+ if config[:reverse_scale] == false
103
+ if !config[:crit].nil? && avg > config[:crit]
104
+ return [2, "#{metric['target']} is #{avg}"]
105
+ elsif !config[:warn].nil? && avg > config[:warn]
106
+ return [1, "#{metric['target']} is #{avg}"]
107
+ end
108
+ else
109
+ if !config[:crit].nil? && avg < config[:crit] # rubocop:disable Style/IfInsideElse
110
+ return [2, "#{metric['target']} is #{avg}"]
111
+ elsif !config[:warn].nil? && avg < config[:warn]
112
+ return [1, "#{metric['target']} is #{avg}"]
113
+ end
114
+ end
115
+ else
116
+ return [3, "#{metric['target']} has no datapoints"] unless config[:unknown_ignore]
117
+ end
118
+ [0, nil]
119
+ end
120
+
121
+ def run
122
+ body =
123
+ begin
124
+ uri = URI.parse(CGI.escape("http://#{config[:host]}/render?format=json&target=#{config[:target]}&from=#{config[:period]}"))
125
+ res = Net::HTTP.get_response(uri)
126
+ res.body
127
+ rescue => e
128
+ warning "Failed to query Graphite: #{e.inspect}"
129
+ end
130
+
131
+ status = 0
132
+ message = ''
133
+ data =
134
+ begin
135
+ JSON.parse(body)
136
+ rescue
137
+ []
138
+ end
139
+
140
+ unknown 'No data from Graphite' if data.empty?
141
+
142
+ data.each do |metric|
143
+ s, msg = danger(metric)
144
+
145
+ message += "#{msg} " unless s.zero?
146
+ status = s unless s < status
147
+ end
148
+
149
+ if status == 2
150
+ critical message
151
+ elsif status == 1
152
+ warning message
153
+ elsif status == 3
154
+ unknown message
155
+ end
156
+ ok
157
+ end
158
+ end
@@ -0,0 +1,542 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # <script name>
4
+ #
5
+ # DESCRIPTION:
6
+ # Get time series values from Graphite and create events based on values
7
+ #
8
+ # OUTPUT:
9
+ # plain text
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: sensu-plugin
16
+ # gem: array_stats
17
+ #
18
+ # USAGE:
19
+ # #YELLOW
20
+ #
21
+ # NOTES:
22
+ #
23
+ # LICENSE:
24
+ # Copyright 2012 Ulf Mansson @ Recorded Future
25
+ # Modifications by Chris Jansen to support wildcard targets
26
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
27
+ # for details.
28
+ #
29
+
30
+ require 'sensu-plugin/check/cli'
31
+ require 'json'
32
+ require 'net/http'
33
+ require 'net/https'
34
+ require 'socket'
35
+ require 'array_stats'
36
+
37
+ class Graphite < Sensu::Plugin::Check::CLI
38
+ option :host,
39
+ short: '-h HOST',
40
+ long: '--host HOST',
41
+ description: 'Graphite host to connect to, include port',
42
+ required: true
43
+
44
+ option :target,
45
+ description: 'The graphite metric name. Could be a comma separated list of metric names.',
46
+ short: '-t TARGET',
47
+ long: '--target TARGET',
48
+ required: true
49
+
50
+ option :complex_target,
51
+ description: 'Allows complex targets which contain functions. Disables splitting on comma.',
52
+ short: '-x',
53
+ long: '--complex_target',
54
+ default: false
55
+
56
+ option :period,
57
+ description: 'The period back in time to extract from Graphite and compare with. Use 24hours,2days etc, same format as in Graphite',
58
+ short: '-p PERIOD',
59
+ long: '--period PERIOD',
60
+ default: '2hours'
61
+
62
+ option :updated_since,
63
+ description: 'The graphite value should have been updated within UPDATED_SINCE seconds, default to 600 seconds',
64
+ short: '-u UPDATED_SINCE',
65
+ long: '--updated_since UPDATED_SINCE',
66
+ default: 600
67
+
68
+ option :acceptable_diff_percentage,
69
+ description: 'The acceptable diff from max values in percentage, used in check_function_increasing',
70
+ short: '-D ACCEPTABLE_DIFF_PERCENTAGE',
71
+ long: '--acceptable_diff_percentage ACCEPTABLE_DIFF_PERCENTAGE',
72
+ default: 0
73
+
74
+ option :check_function_increasing,
75
+ description: 'Check that value is increasing or equal over time (use acceptable_diff_percentage if it should allow to be lower)',
76
+ short: '-i',
77
+ long: '--check_function_increasing',
78
+ default: false,
79
+ boolean: true
80
+
81
+ option :greater_than,
82
+ description: 'Change whether value is greater than or less than check',
83
+ short: '-g',
84
+ long: '--greater_than',
85
+ default: false
86
+
87
+ option :check_last,
88
+ description: 'Check that the last value in GRAPHITE is greater/less than VALUE',
89
+ short: '-l VALUE',
90
+ long: '--last VALUE',
91
+ default: nil
92
+
93
+ option :ignore_nulls,
94
+ description: 'Do not error on null values, used in check_function_increasing',
95
+ short: '-n',
96
+ long: '--ignore_nulls',
97
+ default: false,
98
+ boolean: true
99
+
100
+ option :concat_output,
101
+ description: 'Include warning messages in output even if overall status is critical',
102
+ short: '-c',
103
+ long: '--concat_output',
104
+ default: false,
105
+ boolean: true
106
+
107
+ option :short_output,
108
+ description: 'Report only the highest status per series in output',
109
+ short: '-s',
110
+ long: '--short_output',
111
+ default: false,
112
+ boolean: true
113
+
114
+ option :check_average,
115
+ description: 'MAX_VALUE should be greater than the average of Graphite values from PERIOD',
116
+ short: '-a MAX_VALUE',
117
+ long: '--average_value MAX_VALUE'
118
+
119
+ option :data_points,
120
+ description: 'Number of data points to include in average check (smooths out spikes)',
121
+ short: '-d VALUE',
122
+ long: '--data_points VALUE',
123
+ default: 1
124
+
125
+ option :check_average_percent,
126
+ description: 'MAX_VALUE% should be greater than the average of Graphite values from PERIOD',
127
+ short: '-b MAX_VALUE',
128
+ long: '--average_percent_value MAX_VALUE'
129
+
130
+ option :percentile,
131
+ description: 'Percentile value, should be used in conjunction with percentile_value, defaults to 90',
132
+ long: '--percentile PERCENTILE',
133
+ default: 90
134
+
135
+ option :check_percentile,
136
+ description: 'Values should not be greater than the VALUE of Graphite values from PERIOD',
137
+ long: '--percentile_value VALUE'
138
+
139
+ option :http_user,
140
+ description: 'Basic HTTP authentication user',
141
+ short: '-U USER',
142
+ long: '--http-user USER',
143
+ default: nil
144
+
145
+ option :http_password,
146
+ description: 'Basic HTTP authentication password',
147
+ short: '-P PASSWORD',
148
+ long: '--http-password USER',
149
+ default: nil
150
+
151
+ def initialize
152
+ super
153
+ @graphite_cache = {}
154
+ end
155
+
156
+ def graphite_cache(target = nil)
157
+ # #YELLOW
158
+ if @graphite_cache.key?(target)
159
+ graphite_value = @graphite_cache[target].select { |value| value[:period] == @period }
160
+ graphite_value if graphite_value.size > 0
161
+ end
162
+ end
163
+
164
+ # Create a graphite url from params
165
+ #
166
+ #
167
+ def graphite_url(target = nil)
168
+ url = "#{config[:host]}/render/"
169
+ url = 'http://' + url unless url[0..3] == 'http'
170
+ # #YELLOW
171
+ url = url + "?target=#{target}" if target # rubocop:disable Style/SelfAssignment
172
+ URI.parse(url)
173
+ end
174
+
175
+ def get_levels(config_param)
176
+ values = config_param.split(',')
177
+ i = 0
178
+ levels = {}
179
+ %w[warning error fatal].each do |type|
180
+ levels[type] = values[i] if values[i]
181
+ i += 1
182
+ end
183
+ levels
184
+ end
185
+
186
+ def get_graphite_values(target)
187
+ cache_value = graphite_cache target
188
+ return cache_value if cache_value
189
+
190
+ params = {
191
+ target: target,
192
+ from: "-#{@period}",
193
+ format: 'json'
194
+ }
195
+
196
+ req = Net::HTTP::Post.new(graphite_url.path)
197
+
198
+ # If the basic http authentication credentials have been provided, then use them
199
+ if !config[:http_user].nil? && !config[:http_password].nil?
200
+ req.basic_auth(config[:http_user], config[:http_password])
201
+ end
202
+
203
+ req.set_form_data(params)
204
+ nethttp = Net::HTTP.new(graphite_url.host, graphite_url.port)
205
+ if graphite_url.scheme == 'https'
206
+ nethttp.use_ssl = true
207
+ end
208
+ resp = nethttp.start { |http| http.request(req) }
209
+
210
+ data = JSON.parse(resp.body)
211
+ @graphite_cache[target] = []
212
+ if data.size > 0
213
+ data.each { |d| @graphite_cache[target] << { target: d['target'], period: @period, datapoints: d['datapoints'] } }
214
+ graphite_cache target
215
+ end
216
+ end
217
+
218
+ # Will give max values for [0..-2]
219
+ def max_graphite_value(target)
220
+ max_values = {}
221
+ values = get_graphite_values target
222
+ if values
223
+ values.each do |val|
224
+ max = get_max_value(val[:datapoints])
225
+ max_values[val[:target]] = max
226
+ end
227
+ end
228
+ max_values
229
+ end
230
+
231
+ def get_max_value(values)
232
+ if values
233
+ values.map { |i| i[0] || 0 }[0..-2].max
234
+ end
235
+ end
236
+
237
+ def last_graphite_metric(target, count = 1)
238
+ last_values = {}
239
+ values = get_graphite_values target
240
+ if values
241
+ values.each do |val|
242
+ last = get_last_metric(val[:datapoints], count)
243
+ last_values[val[:target]] = last
244
+ end
245
+ end
246
+ last_values
247
+ end
248
+
249
+ def get_last_metric(values, count = 1)
250
+ if values
251
+ ret = []
252
+ values_size = values.size
253
+ count = values_size if count > values_size
254
+ while count > 0
255
+ values_size -= 1
256
+ break if values[values_size].nil?
257
+
258
+ count -= 1 if values[values_size][0]
259
+ ret.push(values[values_size]) if values[values_size][0]
260
+ end
261
+ ret
262
+ end
263
+ end
264
+
265
+ def last_graphite_value(target, count = 1)
266
+ last_metrics = last_graphite_metric(target, count)
267
+ last_values = {}
268
+ if last_metrics
269
+ last_metrics.each do |target_name, metrics|
270
+ last_values[target_name] = metrics.map { |metric| metric[0] }.mean
271
+ end
272
+ end
273
+ last_values
274
+ end
275
+
276
+ def been_updated_since(target, time, updated_since)
277
+ last_time_stamp = last_graphite_metric target
278
+ warnings = []
279
+ if last_time_stamp
280
+ last_time_stamp.each do |target_name, value|
281
+ last_time_stamp_bool = value[1] > time.to_i
282
+ warnings << "The metric #{target_name} has not been updated in #{updated_since} seconds" unless last_time_stamp_bool
283
+ end
284
+ end
285
+ warnings
286
+ end
287
+
288
+ def greater_less
289
+ return 'greater' if config[:greater_than]
290
+ return 'less' unless config[:greater_than]
291
+ end
292
+
293
+ def check_increasing(target)
294
+ updated_since = config[:updated_since].to_i
295
+ time_to_be_updated_since = Time.now - updated_since
296
+ critical_errors = []
297
+ warnings = []
298
+ max_gv = max_graphite_value target
299
+ last_gv = last_graphite_value target
300
+ if last_gv.is_a?(Hash) && max_gv.is_a?(Hash)
301
+ # #YELLOW
302
+ last_gv.each do |target_name, value|
303
+ if value && max_gv[target_name]
304
+ last = value
305
+ max = max_gv[target_name]
306
+ if max > last * (1 + config[:acceptable_diff_percentage].to_f / 100)
307
+ msg = "The metric #{target} with last value #{last} is less than max value #{max} during #{config[:period]} period"
308
+ critical_errors << msg
309
+ end
310
+ end
311
+ end
312
+ else
313
+ warnings << "Could not found any value in Graphite for metric #{target}, see #{graphite_url(target)}"
314
+ end
315
+ unless config[:ignore_nulls]
316
+ warnings.concat(been_updated_since(target, time_to_be_updated_since, updated_since))
317
+ end
318
+ [warnings, critical_errors, []]
319
+ end
320
+
321
+ def check_average_percent(target, max_values, data_points = 1)
322
+ values = get_graphite_values target
323
+ last_values = last_graphite_value(target, data_points)
324
+ return [[], [], []] unless values
325
+
326
+ warnings = []
327
+ criticals = []
328
+ fatal = []
329
+ values.each do |data| # rubocop:disable Metrics/BlockLength
330
+ target = data[:target]
331
+ values_pair = data[:datapoints]
332
+ values_array = values_pair.select(&:first).map { |v| v.first unless v.first.nil? }
333
+ # #YELLOW
334
+ avg_value = values_array.reduce { |sum, el| sum + el if el }.to_f / values_array.size
335
+ last_value = last_values[target]
336
+ percent = last_value / avg_value unless last_value.nil? || avg_value.nil?
337
+ # #YELLOW
338
+ %w[fatal error warning].each do |type|
339
+ next unless max_values.key?(type)
340
+
341
+ max_value = max_values[type]
342
+ var1 = config[:greater_than] ? percent : max_value.to_f
343
+ var2 = config[:greater_than] ? max_value.to_f : percent
344
+ if !percent.nil? && var1 > var2 && (values_array.size > 0 || !config[:ignore_nulls])
345
+ text = "The last value of metric #{target} is #{percent}% #{greater_less} than allowed #{max_value}% of the average value #{avg_value}"
346
+ case type
347
+ when 'warning'
348
+ warnings << text
349
+ when 'error'
350
+ criticals << text
351
+ when 'fatal'
352
+ fatal << text
353
+ else
354
+ raise "Unknown type #{type}"
355
+ end
356
+ break if config[:short_output]
357
+ end
358
+ end
359
+ end
360
+ [warnings, criticals, fatal]
361
+ end
362
+
363
+ def check_average(target, max_values)
364
+ values = get_graphite_values target
365
+ return [[], [], []] unless values
366
+
367
+ warnings = []
368
+ criticals = []
369
+ fatal = []
370
+ values.each do |data|
371
+ target = data[:target]
372
+ values_pair = data[:datapoints]
373
+ values_array = values_pair.select(&:first).map { |v| v.first unless v.first.nil? }
374
+ # #YELLOW
375
+ avg_value = values_array.reduce { |sum, el| sum + el if el }.to_f / values_array.size
376
+ # YELLOW
377
+ %w[fatal error warning].each do |type|
378
+ next unless max_values.key?(type)
379
+
380
+ max_value = max_values[type]
381
+ var1 = config[:greater_than] ? avg_value : max_value.to_f
382
+ var2 = config[:greater_than] ? max_value.to_f : avg_value
383
+ if var1 > var2 && (values_array.size > 0 || !config[:ignore_nulls])
384
+ text = "The average value of metric #{target} is #{avg_value} that is #{greater_less} than allowed average of #{max_value}"
385
+ case type
386
+ when 'warning'
387
+ warnings << text
388
+ when 'error'
389
+ criticals << text
390
+ when 'fatal'
391
+ fatal << text
392
+ else
393
+ raise "Unknown type #{type}"
394
+ end
395
+ break if config[:short_output]
396
+ end
397
+ end
398
+ end
399
+ [warnings, criticals, fatal]
400
+ end
401
+
402
+ def check_percentile(target, max_values, percentile, data_points = 1)
403
+ values = get_graphite_values target
404
+ last_values = last_graphite_value(target, data_points)
405
+ return [[], [], []] unless values
406
+
407
+ warnings = []
408
+ criticals = []
409
+ fatal = []
410
+ values.each do |data| # rubocop:disable Metrics/BlockLength
411
+ target = data[:target]
412
+ values_pair = data[:datapoints]
413
+ values_array = values_pair.select(&:first).map { |v| v.first unless v.first.nil? }
414
+ percentile_value = values_array.percentile(percentile)
415
+ last_value = last_values[target]
416
+ percent = last_value / percentile_value unless last_value.nil? || percentile_value.nil?
417
+ # #YELLOW
418
+ %w[fatal error warning].each do |type|
419
+ next unless max_values.key?(type)
420
+
421
+ max_value = max_values[type]
422
+ var1 = config[:greater_than] ? percent : max_value.to_f
423
+ var2 = config[:greater_than] ? max_value.to_f : percent
424
+ if !percentile_value.nil? && var1 > var2
425
+ text = "The percentile value of metric #{target} (#{last_value}) is #{greater_less} than the
426
+ #{percentile}th percentile (#{percentile_value}) by more than #{max_value}%"
427
+ case type
428
+ when 'warning'
429
+ warnings << text
430
+ when 'error'
431
+ criticals << text
432
+ when 'fatal'
433
+ fatal << text
434
+ else
435
+ raise "Unknown type #{type}"
436
+ end
437
+ break if config[:short_output]
438
+ end
439
+ end
440
+ end
441
+ [warnings, criticals, fatal]
442
+ end
443
+
444
+ def check_last(target, max_values)
445
+ last_targets = last_graphite_value target
446
+ return [[], [], []] unless last_targets
447
+
448
+ warnings = []
449
+ criticals = []
450
+ fatal = []
451
+ # #YELLOW
452
+ last_targets.each do |target_name, last_value|
453
+ unless last_value.nil?
454
+ # #YELLOW
455
+ %w[fatal error warning].each do |type|
456
+ next unless max_values.key?(type)
457
+
458
+ max_value = max_values[type]
459
+ var1 = config[:greater_than] ? last_value : max_value.to_f
460
+ var2 = config[:greater_than] ? max_value.to_f : last_value
461
+ if var1 > var2
462
+ text = "The metric #{target_name} is #{last_value} that is #{greater_less} than last allowed #{max_value}"
463
+ case type
464
+ when 'warning'
465
+ warnings << text
466
+ when 'error'
467
+ criticals << text
468
+ when 'fatal'
469
+ fatal << text
470
+ else
471
+ raise "Unknown type #{type}"
472
+ end
473
+ break if config[:short_output]
474
+ end
475
+ end
476
+ end
477
+ end
478
+ [warnings, criticals, fatal]
479
+ end
480
+
481
+ def run
482
+ targets = config[:complex_target] ? [config[:target]] : config[:target].split(',')
483
+ @period = config[:period]
484
+ critical_errors = []
485
+ warnings = []
486
+ fatals = []
487
+ # #YELLOW
488
+ targets.each do |target| # rubocop:disable Metrics/BlockLength
489
+ if config[:check_function_increasing]
490
+ inc_warnings, inc_critical, inc_fatal = check_increasing target
491
+ warnings += inc_warnings
492
+ critical_errors += inc_critical
493
+ fatals += inc_fatal
494
+ end
495
+ if config[:check_last]
496
+ max_values = get_levels config[:check_last]
497
+ lt_warnings, lt_critical, lt_fatal = check_last(target, max_values)
498
+ warnings += lt_warnings
499
+ critical_errors += lt_critical
500
+ fatals += lt_fatal
501
+ end
502
+ if config[:check_average]
503
+ max_values = get_levels config[:check_average]
504
+ avg_warnings, avg_critical, avg_fatal = check_average(target, max_values)
505
+ warnings += avg_warnings
506
+ critical_errors += avg_critical
507
+ fatals += avg_fatal
508
+ end
509
+ if config[:check_average_percent]
510
+ max_values = get_levels config[:check_average_percent]
511
+ avg_warnings, avg_critical, avg_fatal = check_average_percent(target, max_values, config[:data_points].to_i)
512
+ warnings += avg_warnings
513
+ critical_errors += avg_critical
514
+ fatals += avg_fatal
515
+ end
516
+ if config[:check_percentile]
517
+ max_values = get_levels config[:check_percentile]
518
+ pct_warnings, pct_critical, pct_fatal = check_percentile(target, max_values, config[:percentile].to_i, config[:data_points].to_i)
519
+ warnings += pct_warnings
520
+ critical_errors += pct_critical
521
+ fatals += pct_fatal
522
+ end
523
+ end
524
+ fatals_string = fatals.size > 0 ? fatals.join("\n") : ''
525
+ criticals_string = critical_errors.size > 0 ? critical_errors.join("\n") : ''
526
+ warnings_string = warnings.size > 0 ? warnings.join("\n") : ''
527
+
528
+ if config[:concat_output]
529
+ fatals_string = fatals_string + "\n" + criticals_string if critical_errors.size > 0
530
+ fatals_string = fatals_string + "\nGraphite WARNING: " + warnings_string if warnings.size > 0
531
+ criticals_string = criticals_string + "\nGraphite WARNING: " + warnings_string if warnings.size > 0
532
+ critical fatals_string if fatals.size > 0
533
+ critical criticals_string if critical_errors.size > 0
534
+ warning warnings_string if warnings.size > 0 # rubocop:disable Style/IdenticalConditionalBranches
535
+ else
536
+ critical fatals_string if fatals.size > 0
537
+ critical criticals_string if critical_errors.size > 0
538
+ warning warnings_string if warnings.size > 0 # rubocop:disable Style/IdenticalConditionalBranches
539
+ end
540
+ ok
541
+ end
542
+ end