#!/usr/bin/env ruby require 'optparse' require 'yaml' require 'pathname' require 'socket' require 'daemons' require 'eventmachine' require 'eventmachine-tail' require 'remote_syslog' def remote_syslog_daemon(args) options = { :configfile => '/etc/log_files.yml', :dest_host => 'logs.papertrailapp.com', :dest_port => 514 } daemonize_options = { :app_name => File.basename($0) || "remote_syslog", :ARGV => ['start'], :dir_mode => :system, :multiple => false, :ontop => false, :mode => :exec, :backtrace => false, :monitor => false, :strip_color => false } op = OptionParser.new do |opts| opts.banner = "Usage: remote_syslog [options] .. " opts.separator '' opts.separator "Example: remote_syslog -c configs/logs.yml -p 12345 /var/log/mysqld.log" opts.separator '' opts.separator "Options:" opts.on("-c", "--configfile PATH", "Path to config (/etc/log_files.yml)") do |v| options[:configfile] = File.expand_path(v) end opts.on("-d", "--dest-host HOSTNAME", "Destination syslog hostname or IP (logs.papertrailapp.com)") do |v| options[:dest_host] = v end opts.on("-D", "--no-detach", "Don't daemonize and detach from the terminal") do daemonize_options[:ontop] = true daemonize_options[:ARGV] = ['run'] # write PID file in . because /var/run is sometimes only writable by root daemonize_options[:dir_mode] = :script daemonize_options[:dir] = '.' end opts.on("-f", "--facility FACILITY", "Facility (user)") do |v| options[:facility] = v.upcase end opts.on("-p", "--dest-port PORT", "Destination syslog port (514)") do |v| options[:dest_port] = v end opts.on("-P", "--pid-dir DIRECTORY", "Directory to write .pid file in (/var/run/)") do |v| daemonize_options[:dir_mode] = :script daemonize_options[:dir] = v end opts.on("-s", "--severity SEVERITY", "Severity (notice)") do |v| options[:severity] = v.upcase end opts.on("--strip-color", "Strip color codes") do options[:strip_color] = true end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end op.parse! files = ARGV if File.exist?(options[:configfile]) config = open(options[:configfile]) do |f| YAML.load(f) end files += config['files'] if config['destination'] options[:dest_host] = config['destination']['host'] if config['destination']['host'] options[:dest_port] = config['destination']['port'] if config['destination']['port'] end if config['hostname'] options[:hostname] = config['hostname'] end elsif files.empty? puts "No filenames provided and #{options[:configfile]} not found." puts '' puts op exit end # handle relative paths before Daemonize changes the wd to / and expand wildcards files = files.map { |f| Dir.glob(f) }.flatten.map { |f| File.expand_path(f) }.uniq Daemons.run_proc(daemonize_options[:app_name], daemonize_options) do EventMachine.run do socket = EventMachine.open_datagram_socket('0.0.0.0', 0) files.each do |path| begin EventMachine::file_tail(File.expand_path(path), RemoteSyslog::Reader, options[:dest_host], options[:dest_port], :socket => socket, :facility => options[:facility], :severity => options[:severity], :strip_color => options[:strip_color], :hostname => options[:hostname]) rescue Errno::ENOENT => e puts "#{File.expand_path(path)} not found, continuing. (#{e.message})" end end end end end remote_syslog_daemon(ARGV)