Module: MuninPassenger::Collect

Defined in:
lib/munin_passenger/collect.rb

Class Method Summary collapse

Class Method Details

.default_state_fileObject



8
9
10
11
12
# File 'lib/munin_passenger/collect.rb', line 8

def self.default_state_file
  {
    'pses' => []
  }
end

.get_group_stats(doc) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/munin_passenger/collect.rb', line 49

def self.get_group_stats(doc)
  ret = []
  doc.xpath('//info/supergroups/supergroup').each do |x_supergroup|
    x_supergroup.xpath('./group').each do |x_group|
      g = OpenStruct.new
      g.name = x_group.xpath('name').first.text
      g.queue = x_group.xpath('get_wait_list_size').first.text
      ret << g
    end
  end
  ret
end

.get_ps_stats(doc) ⇒ Object



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
# File 'lib/munin_passenger/collect.rb', line 62

def self.get_ps_stats(doc)
  state = read_state_file
  slots_by_pid = {}
  state['pses'].each_with_index do |pid, i|
    slots_by_pid[pid] = i if pid
  end
  ret = [nil] * state['pses'].size  # Need at least as many workers as the most we've seen.
  curr_pses = []
  now = Time.now
  doc.xpath('//info/supergroups/supergroup').each do |x_supergroup|
    x_supergroup.xpath('./group').each do |x_group|
      x_group.xpath('./processes/process').each do |x_ps|
        pid = x_ps.xpath('pid').first.text
        ps = OpenStruct.new
        ps.active      = true
        ps.pid         = pid
        ps.sessions    = x_ps.xpath('sessions').first.text.to_i
        ps.last_used   = (now - Time.at(x_ps.xpath('last_used').first.text.to_i / 1000 / 1000).to_i).to_i # in seconds
        ps.ram         = x_ps.xpath('real_memory').first.text.to_i   # in KB
        ps.cpu         = x_ps.xpath('cpu').first.text.to_i           # in %
        ps.uptime      = (now - Time.at(x_ps.xpath('spawn_start_time').first.text.to_i / 1000 / 1000).to_i).to_i # in seconds
        ps.processed   = x_ps.xpath('processed').first.text.to_i     # in requests
        ps.last_seen   = now.to_i
        curr_pses << ps
      end
    end
  end
  # This is all a little tricky,
  # but if we name each metric after the pid,
  # munin will throw away the history when passenger is restarted and the pids change.
  # So instead we have "worker1", "worker2", etc.,
  # and we show the current pid in parentheses.
  # Each time we collect info, we keep a pid in the same slot as before.
  # If a pid goes away, we free that slot.
  # If it's the first we've seen a pid,
  # we give it the first free slot.
  new_pses = []
  curr_pses.each do |ps|
    i = slots_by_pid[ps.pid]
    if i
      ret[i] = ps
    else
      new_pses << ps
    end
  end
  new_pses.each do |ps|
    i = ret.index{|x| x == nil}
    if i
      ret[i] = ps
    else
      ret << ps
    end
  end
  state['pses'] = ret.map do |ps|
    if ps
      {
        'pid'       => ps.pid,
        'last_seen' => ps.last_seen,
      }
    else
      nil
    end
  end
  write_state_file(state)
  # Now if there are still nils,
  # it means we are running than fewer workers than before.
  # Preserve those slots,
  # but the graphing code will have to watch out
  # and note that they have no process currently.
  ret
end

.parse_stats(f) ⇒ Object



41
42
43
44
45
46
47
# File 'lib/munin_passenger/collect.rb', line 41

def self.parse_stats(f)
  Nokogiri::XML(f) do |config|
    config.strict.
           noblanks.  # Exclude blank nodes
           nonet      # Don't make any network connections
  end
end

.read_state_fileObject



14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/munin_passenger/collect.rb', line 14

def self.read_state_file
  filename = ENV['MUNIN_STATEFILE']
  if filename and File.exist?(filename)
    begin
      JSON.parse(File.open(filename, 'r') {|f| f.read})
    rescue
      $stderr.puts "WARN: Couldn't open munin statefile at #{filename}: #{$!.message}"
      default_state_file
    end
  else
    default_state_file
  end
end

.run_statusObject



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/munin_passenger/collect.rb', line 134

def self.run_status
  dir = if ENV['PASSENGER_ROOT']
          "#{ENV['PASSENGER_ROOT']}/"
        else
          ''
        end

  sudo = if ENV['WITHOUT_SUDO']
           ''
         else
           'sudo '
         end

  status = `#{sudo}#{dir}passenger-status --show=xml`
  if not $?.success?
    $stderr.puts 'Error running passenger-status'
    exit 1
  end
  status
end

.write_state_file(state) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/munin_passenger/collect.rb', line 28

def self.write_state_file(state)
  filename = ENV['MUNIN_STATEFILE']
  if filename
    begin
      File.open(filename, 'w') do |f|
        f.write state.to_json
      end
    rescue
      $stderr.puts "WARN: Error writing to statefile at #{filename}: #{$!.message}"
    end
  end
end