-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathmetrics-process-status.rb
More file actions
executable file
·143 lines (124 loc) · 3.56 KB
/
Copy pathmetrics-process-status.rb
File metadata and controls
executable file
·143 lines (124 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#! /usr/bin/env ruby
#
# proc-status-metrics
#
# DESCRIPTION:
# For all processes owned by a user AND/OR matching a provided process
# name substring, return selected memory metrics from /proc/[PID]/status
#
# OUTPUT:
# metric data
#
# PLATFORMS:
# Linux
#
# DEPENDENCIES:
# gem: sensu-plugin
#
# USAGE:
#
# NOTES:
# - This check names the metrics after the full process name
# string as found in /proc/[PID]/cmdline, so data will be lost if
# you have multiple processes with identical cmdlines.
# - A process that changes its cmdline but still matches the search
# query will be duplicated as multiple discovered processes.
# - We make some assumptions in parsing /proc/[PID]/status, this will
# mostly only work with the memory-related stats.
#
# LICENSE:
# Copyright (c) 2014 Cozy Services Ltd. opensource@cozy.co
# Matt Greensmith mgreensmith@cozy.co
# Released under the same terms as Sensu (the MIT license); see LICENSE
# for details.
#
require 'sensu-plugin/metric/cli'
require 'socket'
#
# String
# monkeypatch for helping validate PID strings
#
class String
# if its and integer, set it to a string
def integer?
to_i.to_s == self
end
end
#
# Proc Status
#
# /proc/[PID]/status memory metrics plugin
#
class ProcStatus < Sensu::Plugin::Metric::CLI::Graphite
option :user,
description: 'Query processes owned by a user',
short: '-u USER',
long: '--user USER'
option :processname,
description: 'Process name substring to match against, not a regex.',
short: '-p PROCESSNAME',
long: '--process-name PROCESSNAME'
option :scheme,
description: 'Metric naming scheme',
long: '--scheme SCHEME',
default: "#{Socket.gethostname}.proc"
option :metrics,
description: 'Memory metrics to collect from /proc/[PID]/status, comma-separated',
short: '-m METRICS',
long: '--metrics METRICS',
default: 'VmSize,VmRSS,VmSwap'
# Build search command
#
def pgrep_command
pgrep_command = 'pgrep '
pgrep_command << "-u #{config[:user]} " if config[:user]
pgrep_command << "-f #{config[:processname]} " if config[:processname]
pgrep_command << '2<&1'
end
# Acquire process_pids
#
# @param pgrep_output [String]
#
def acquire_valid_pids(pgrep_output)
res = pgrep_output.split("\n").map(&:strip)
pids = res.select(&:integer?)
pids
end
# Acquire the sate for the supplied PID
#
# @param pid [String]
#
def acquire_stats_for_pid(pid)
return nil unless ::File.exist?(::File.join('/proc', pid, 'cmdline'))
cmdline_raw = `cat /proc/#{pid}/cmdline`
cmdline = cmdline_raw.strip.gsub(/[^[:alnum:]]/, '_')
metric_names = config[:metrics].split(',')
proc_status_lines = `cat /proc/#{pid}/status`.split("\n")
out = { cmdline.to_s => {} }
metric_names.each do |m|
line = proc_status_lines.find { |x| /^#{m}/.match(x) }
val = line ? line.split("\t")[1].to_i : nil
out[cmdline.to_s][m] = val
end
out
end
# Main functino
#
def run
raise 'You must supply -u USER or -p PROCESSNAME' unless config[:user] || config[:processname]
metrics = {}
pgrep_output = `#{pgrep_command}`
pids = acquire_valid_pids(pgrep_output)
pids.each do |p|
data = acquire_stats_for_pid(p)
metrics.merge!(data) unless data.nil?
end
timestamp = Time.now.to_i
metrics.each do |proc_name, stats|
stats.each do |stat_name, value|
output [config[:scheme], proc_name, stat_name].join('.'), value, timestamp
end
end
ok
end
end