-
Notifications
You must be signed in to change notification settings - Fork 0
/
munin-passenger.rb
311 lines (262 loc) · 7.68 KB
/
munin-passenger.rb
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#!/root/.rbenv/shims/ruby
# Install
# wget https://raw.githubusercontent.com/tomlobato/munin-passenger5/master/munin-passenger.rb -O /usr/local/sbin/munin-passenger.rb
# chmod 755 /usr/local/sbin/munin-passenger.rb
# munin-passenger.rb install
# /etc/init.d/munin-node restart
require 'active_support/core_ext/hash/conversions'
require 'msgpack'
require 'ostruct'
class MuninPassenger
CACHE_FILE = '/tmp/passenger_status_cache'
CACHE_EXPIRE = 30
GRAPHS = {
# Global Passenger metrics
global_pool: {
level: :passenger,
vlabel: 'count',
fields: {
max: 'Max pool size',
process_count: 'Process Count',
get_wait_list_size: 'Requests in top-level queue'
},
},
# App metrics
processes: {
level: :app,
vlabel: 'Process count',
fields: {
enabled_process_count: 'Enabled process Count',
disabling_process_count: 'Disabling process Count',
disabled_process_count: 'Disabled process Count',
},
},
# App processes metrics: accounted by summing up metrics of all processes of the specific app
app_queue: {
level: :app_process,
vlabel: 'count',
fields: {
get_wait_list_size: 'App requests queue'
},
},
cpu: {
level: :app_process,
vlabel: '%',
fields: {
cpu: 'CPU'
}
},
sessions: {
level: :app_process,
vlabel: 'Sessions',
fields: {
sessions: 'Sessions'
}
},
processed: {
level: :app_process,
vlabel: 'Processed',
fields: {
processed: 'Processed'
}
},
memory: {
level: :app_process,
factor: 1024,
vlabel: 'Bytes',
fields: {
rss: 'Resident Set Size',
pss: 'Proportional Set Size',
private_dirty: 'Private Dirty',
swap: 'Swap',
real_memory: 'Real Memory',
vmsize: 'Virtual Memory Size',
}
}
}
def initialize argv
@argv = argv
end
def cli
case @argv[0]
when 'config'
@param = get_params
config
when 'install'
install
else
@param = get_params
run
end
end
private
def graph
GRAPHS[@param.graph]
end
def config
puts <<-CONFIG
graph_category Passenger
graph_title #{@param.app_name} #{@param.graph}
graph_vlabel #{graph[:vlabel]}
graph_args --base 1000 -l 0
graph_info GI
CONFIG
graph[:fields].each_pair do |k, v|
puts "#{k}.label #{v}"
end
exit 0
end
def run
data_global, data_app, data_app_processes = fetch
count = {}
factor = graph[:factor]
factor = factor ? factor.to_f : 1
case graph[:level]
when :passenger
fields.each do |field|
count[field] = data_global[field].to_i * factor
end
when :app
fields.each do |field|
count[field] = data_app[field].to_i * factor
end
when :app_process
data_app_processes.each do |process|
fields.each do |k|
count[k] ||= 0
count[k] += process[k].to_i * factor
end
end
end
count.each_pair do |k, v|
puts "#{k}.value #{v}"
end
exit 0
end
# Util
def fields
graph[:fields]
.keys
.map(&:to_s)
end
def get_params
filename = File.basename __FILE__
app_keys = []
passenger_keys = []
GRAPHS.each_pair do |k, v|
if v[:level] == :passenger
passenger_keys << k
else
app_keys << k
end
end
if filename =~ /^passenger_(.*?)_(#{ app_keys.join '|' })$/ or
filename =~ /^passenger_(#{ passenger_keys.join '|' })/
if $2
app_name = $1
graph_key = $2
else
graph_key = $1
end
if app_name
app_root = ENV['app_root']
if !app_root or app_root.empty?
error "env.app_root not defined"
end
end
OpenStruct.new(
graph: graph_key.to_sym,
app_name: app_name,
app_root: app_root
)
else
error "Invalid graph type for filename #{filename}."
end
end
def fetch
data_global = nil
data_app = nil
data_app_processes = nil
# Global
data_global = get_passenger_status
# App
supergroups = data_global['supergroups']['supergroup']
supergroups = [supergroups] unless supergroups.is_a? Array
supergroups.each do |_app|
if _app['group']['app_root'] =~ /^#{@param.app_root}/
data_app = _app['group']
end
end
if !data_app
error "App #{@param.app_name} (#{@param.app_root}) not found."
end
# App processes
data_app_processes = data_app['processes']['process']
case data_app_processes
when Hash
data_app_processes = [data_app_processes]
when nil
data_app_processes = []
end
[data_global, data_app, data_app_processes]
end
def get_passenger_status
if File.exists?(CACHE_FILE) and
(Time.now - File.stat(CACHE_FILE).ctime) < CACHE_EXPIRE
MessagePack.unpack File.open(CACHE_FILE, 'rb').read
else
xml = `passenger-status --show=xml`
pstatus = Hash.from_xml(xml)['info']
File.open(CACHE_FILE, 'wb', 0600).write MessagePack.pack(pstatus)
pstatus
end
end
def get_apps
supergroups = get_passenger_status['supergroups']['supergroup']
supergroups = [supergroups] unless supergroups.is_a? Array
supergroups.map{ |supergroup|
app_root = supergroup['group']['app_root']
app_name = app_root
.sub(/^\/var\/www/, '')
.sub(/\/current$/, '')
.sub(/^\/+|\/+$/, '')
OpenStruct.new(app_name: app_name, app_root: app_root)
}
end
def install
apps = get_apps
# Make Links
links = []
GRAPHS.keys.each do |graph_key|
if GRAPHS[graph_key][:level] == :passenger
links << graph_key
else
apps.each do |app|
links << "#{app.app_name}_#{graph_key}"
end
end
end
target = File.expand_path(__FILE__)
links.each do |link|
system "ln -s #{target} /etc/munin/plugins/passenger_#{link}"
end
# munin_node_conf
munin_node_conf = "[passenger_*]
user root
env.PASSENGER_INSTANCE_REGISTRY_DIR /tmp/aptmp
"
apps.each do |app|
munin_node_conf += "
[passenger_#{app.app_name}_*]
env.app_root #{app.app_root}
"
end
munin_node_conf += "\n"
File.open('/etc/munin/plugin-conf.d/munin-node', 'a').write munin_node_conf
end
def error msg
STDERR.puts msg
exit 1
end
end
MuninPassenger.new(ARGV).cli