From: Mike Wilson Date: 2005-10-21T03:16:21+09:00 Subject: Re: Seeking Contributions for O'Reilly's Ruby Cookbook > We definitely want more GUI recipes, but I don't know what techniques > they should illustrate. PDF creation, SQLite integration, XML->YAML > mapping, specific web service integrations, etc. are all great ideas. > I'd like to have a section on system administration tasks, but I'm not > a sysadmin, so I'd like to hear from Ruby-using sysadmins what recipes > they'd like to see or share. > I'm a sysadmin who uses Ruby :O) How about a logfile reader. Here's mine, although I'm sure the turd could be polished quite heavily! ------ cut config -------- mailhost: mailhost tmout: 3600 grab: 1 patterns: - !ruby/struct:LoganStruct pattern: "correctable error detected" grab: 5 email: me@here.com post: "-r CRITICAL -m 'Logan(thishost): correctable error detected - Contac t Unix Admin Team' hostname=thishost general_use SCRIPTS" - !ruby/struct:LoganStruct pattern: "Error Level: Retryable" except: QUANTUM grab: 5 email: me@here.com post: "-r CRITICAL -m 'Logan(thishost): Retryable disk error detected - Con tact Unix Admin Team' hostname=thishost general_use SCRIPTS" - !ruby/struct:LoganStruct pattern: "NFS getattr failed for server" email: unix.admin@customs.treas.gov #post: "-r CRITICAL -m 'Logan(thishost): NFS getattr failed - Contact Unix A dmin Team' hostname=thishost general_use SCRIPTS" - !ruby/struct:LoganStruct pattern: "logan.* Config file error" email: unix.admin@customs.treas.gov tmout: 0 ------------- end config --------------- ------------- start script ---------------- #!/usr/local/bin/ruby # @(#)logan 1.4 06/15/05 # # Program Name: logan # Date Created: 06/01/05 # Creator: Mike Wilson # # SYNOPSIS # logan [-d] -c config_file -l logfile # # DESCRIPTION # Pattern matches patterns specified in config_file (YAML) against logfile. # require "yaml" require "socket" require "syslog" require "net/smtp" require "file/tail" require "getoptlong" $config = {} $progname = File.basename($0) thishost = Socket.gethostname tivenv = "/etc/Tivoli/setup_env.sh" usetiv = false daemon = false logfile = nil config_file = nil opts = GetoptLong.new( ["--config-file", "-c", GetoptLong::REQUIRED_ARGUMENT], ["--daemon", "-d", GetoptLong::NO_ARGUMENT], ["--logfile", "-l", GetoptLong::REQUIRED_ARGUMENT] ) logan_struct = Struct::new("LoganStruct", :pattern, :tmout, :grab, :except, :mailhost, :email, :post, :expire) def daemonize() fork and exit Process.setsid fork and exit File.open("/dev/null", "r+") do |devnull| $stdin.reopen(devnull) $stdout.reopen(devnull) $stderr.reopen(devnull) end Dir.chdir("/") end def source(file) response_match = Regexp.new(/^([^=]*)=(.*)/) `#{ENV['SHELL']} -c ". #{file}; set"`.each_line do |line| if response_match.match(line) k, v = line.gsub(/^([^=]*)=(.*)/, '\1 \2').split ENV[k] = v end end end def logConfigLoadError(config_file) Syslog.open($progname, Syslog::LOG_PID, Syslog::LOG_DAEMON) do |syslog| syslog.log(Syslog::LOG_ALERT, "Config file error in #{config_file}") end end def parseYamlConfig(file) tmpconfig = {} begin tmpconfig = YAML::load(File.open(file)) rescue => err if $config.size == 0 raise err else logConfigLoadError(file) end else $config = tmpconfig end p $config if $DEBUG $config end def convertToRegexp(topat) pat = nil case topat when String pat = Regexp.new(topat) when Regexp pat = topat end pat end def setDefault(check, master, lastchance) if not check if master check = master else check = lastchance end end check end def setDefaultConfigVals(tmout = 3600, mailhost = "mailhost", grab = 1) $config["patterns"].each do |pat| pat.expire = 0 pat.pattern = convertToRegexp(pat.pattern) pat.except = convertToRegexp(pat.except) pat.mailhost = setDefault(pat.mailhost, $config["mailhost"], mailhost) pat.tmout = setDefault(pat.tmout, $config["tmout"], tmout) pat.grab = setDefault(pat.grab, $config["grab"], grab) end end def lookForPatternsInLogfile(file) if File.exists?(file) and File.stat(file).readable? File::Tail::Logfile.open(file, :rewind=>0) do |log| lines = [] pattern = nil grab = 0 log.tail do |line| if grab > 1 lines << line grab -= 1 if grab == 1 yield pattern, lines lines = [] end end $config["patterns"].each do |pat| if pat.pattern.match(line) if pat.grab > 1 lines << line pattern = pat grab = pat.grab else yield pat, line end end end end end end end def sendMail(email, alert, host, mailhost = "mailhost") message = < /dev/null 2>&1") end opts.each do |opt, arg| case opt when "--daemon" daemon = true when "--logfile" logfile = File.expand_path(arg) when "--config-file" config_file = File.expand_path(arg) end end if not logfile or not config_file fail "Usage: #{$progname} [-d] -c config_file -l logfile" end hup_proc = proc { |*a| puts "Received signal to reload config" parseYamlConfig(config_file) or fail $! setDefaultConfigVals trap("HUP", hup_proc) } trap("HUP", hup_proc) trap("INT") { exit } trap("TERM") { exit } parseYamlConfig(config_file) or fail $! setDefaultConfigVals daemonize if daemon if File.exists?(tivenv) source(tivenv) usetiv = true end lookForPatternsInLogfile(logfile) do |pat, msg| if pat.expire <= Time.now.to_i pat.expire = 0 end if not pat.except or not pat.except.match(msg.to_s) if pat.expire == 0 pat.expire = Time.now.to_i + pat.tmout if pat.email pat.email.each do |e| sendMail(e, msg, thishost, pat.mailhost) end end if pat.post postMessage(pat.post) if usetiv end puts msg end end end ------------------ end script ------------