Skip to content

Instantly share code, notes, and snippets.

@Quintus
Created July 30, 2018 17:05
Show Gist options
  • Save Quintus/e1439c54c64fa23b56a7c050e1e7b60d to your computer and use it in GitHub Desktop.
Save Quintus/e1439c54c64fa23b56a7c050e1e7b60d to your computer and use it in GitHub Desktop.
Munin plugin for analysing xferlog (e.g. by vsftpd)
#!/usr/bin/ruby
LOGFILE = ENV["LOGFILE"] || "/var/log/vsftpd.log"
STATEFILE = ENV["STATEFILE"] || "#{ENV["MUNIN_PLUGSTATE"]}/munin-xferlog.state"
ROTLOGFILE = "#{LOGFILE}.1"
LOGFILE.freeze
STATEFILE.freeze
ROTLOGFILE.freeze
$asciitransfers = 0
$bintransfers = 0
def usage
puts "Usage: $0 [config]"
puts "This munin plugin monitors the FTP transfers over time."
end
def parse_logfile(filename, start, fin)
File.open(filename, "rb") do |file|
file.seek(start)
# It is guaranteed that fin is never beyond EOF.
# It can be equal to EOF, but that's okay. But
# if data has been written to the file while this
# programme is running, it will be smaller than EOF.
while file.tell < fin
line = file.gets.chomp.split(/\s+/)
if line[9] == "a"
$asciitransfers += line[7].to_i
elsif line[9] == "b"
$bintransfers += line[7].to_i
end
end
end
end
def get_values
# If the logfile is not there, indicate failure to munin.
unless File.exist?(LOGFILE)
puts "xferlog_ascii.value U"
puts "xferlog_binary.value U"
exit 2
end
# Read values from last run
pos = nil
if File.exist?(STATEFILE)
str = File.read(STATEFILE)
pos, $asciitransfers, $bintransfers = str.split(":").map(&:to_i)
else
# Initial run, start at zero
$asciitransfers = 0
$bintransfers = 0
end
# Determine size of logfile
logsize = File.size(LOGFILE)
# If pos is not set, the statefile did not exist,
# i.e. this is the first run of the plugin. Use
# the current logfile size as the starting point
# (makes the first run a no-op).
pos = logsize unless pos
# If the size of the logfile is smaller than the
# last handled position, the log has been rotated.
# Process the rotated logfile prior to the current
# logfile, if that file exists.
if logsize < pos
if File.exist?(ROTLOGFILE)
parse_logfile ROTLOGFILE, pos, File.size(ROTLOGFILE)
end
# Process the current logfile from the beginning again.
pos = 0
end
# Now parse the current logfile (use determined logsize
# to ignore data written to the logfile while this programme
# is running).
parse_logfile LOGFILE, pos, logsize
pos = logsize
# Tell munin
puts "xferlog_ascii.value #$asciitransfers"
puts "xferlog_binary.value #$bintransfers"
# Remember where we left off
File.open(STATEFILE, "w") do |f|
f.write("#{pos}:#$asciitransfers:#$bintransfers")
end
end
case ARGV.count
when 0
get_values
when 1
if ARGV.first == "config"
puts "graph_title FTP Transfers"
puts "graph_category network"
puts "graph_args --base 1000 -l 0"
puts "graph_vlabel Bytes/\${graph_period}"
puts "xferlog_ascii.label ASCII Transfers"
puts "xferlog_ascii.type DERIVE"
puts "xferlog_ascii.draw AREA"
puts "xferlog_ascii.min 0"
puts "xferlog_binary.label Binary Transfers"
puts "xferlog_binary.type DERIVE"
puts "xferlog_binary.draw STACK"
puts "xferlog_binary.min 0"
else
usage
exit 1
end
else
usage
exit 1
end
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment