remote_syslog-gitlab 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +488 -0
- data/Rakefile +150 -0
- data/bin/remote_syslog +6 -0
- data/examples/com.papertrailapp.remote_syslog.plist +20 -0
- data/examples/log_files.yml.example +9 -0
- data/examples/log_files.yml.example.advanced +21 -0
- data/examples/remote_syslog.init.d +110 -0
- data/examples/remote_syslog.supervisor.conf +9 -0
- data/examples/remote_syslog.upstart.conf +9 -0
- data/lib/remote_syslog/agent.rb +144 -0
- data/lib/remote_syslog/cli.rb +292 -0
- data/lib/remote_syslog/eventmachine_reader.rb +43 -0
- data/lib/remote_syslog/file_tail_reader.rb +40 -0
- data/lib/remote_syslog/glob_watch.rb +25 -0
- data/lib/remote_syslog/message_generator.rb +60 -0
- data/lib/remote_syslog/tcp_endpoint.rb +87 -0
- data/lib/remote_syslog/tls_endpoint.rb +104 -0
- data/lib/remote_syslog/udp_endpoint.rb +37 -0
- data/lib/remote_syslog.rb +4 -0
- data/remote_syslog.gemspec +88 -0
- data/test/unit/message_generator_test.rb +14 -0
- metadata +165 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# see README - demonstrates all optional arguments and more glob formats
|
2
|
+
files:
|
3
|
+
- /var/log/httpd/access_log
|
4
|
+
- /var/log/httpd/error_log
|
5
|
+
- /opt/misc/*.log
|
6
|
+
- /home/**/*.log
|
7
|
+
- /var/log/mysqld.log
|
8
|
+
- /var/run/mysqld/mysqld-slow.log
|
9
|
+
exclude_files:
|
10
|
+
- old
|
11
|
+
- 200\d
|
12
|
+
hostname: www42 # override OS hostname
|
13
|
+
parse_fields: syslog # predefined regex name or double-quoted regex
|
14
|
+
prepend: '0xDEADBEEF: ' # prepend this before every log message
|
15
|
+
exclude_patterns:
|
16
|
+
- exclude this
|
17
|
+
- \d+ things
|
18
|
+
destination:
|
19
|
+
host: logs.papertrailapp.com
|
20
|
+
port: 12345 # NOTE: change this to YOUR papertrail port!
|
21
|
+
new_file_check_interval: 5 # Check every 5 seconds
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
### BEGIN INIT INFO
|
4
|
+
# Provides: remote_syslog
|
5
|
+
# Required-Start: $remote_fs $syslog
|
6
|
+
# Required-Stop: $remote_fs $syslog
|
7
|
+
# Default-Start: 2 3 4 5
|
8
|
+
# Default-Stop: 0 1 6
|
9
|
+
# Short-Description: Start and Stop
|
10
|
+
# Description: Runs remote_syslog
|
11
|
+
### END INIT INFO
|
12
|
+
|
13
|
+
# /etc/init.d/remote_syslog
|
14
|
+
#
|
15
|
+
# Starts the remote_syslog daemon
|
16
|
+
#
|
17
|
+
# chkconfig: 345 90 5
|
18
|
+
# description: Runs remote_syslog
|
19
|
+
#
|
20
|
+
# processname: remote_syslog
|
21
|
+
|
22
|
+
prog="remote_syslog"
|
23
|
+
config="/etc/log_files.yml"
|
24
|
+
pid_dir="/var/run"
|
25
|
+
|
26
|
+
EXTRAOPTIONS=""
|
27
|
+
|
28
|
+
pid_file="$pid_dir/$prog.pid"
|
29
|
+
|
30
|
+
PATH=/sbin:/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
|
31
|
+
|
32
|
+
RETVAL=0
|
33
|
+
|
34
|
+
is_running(){
|
35
|
+
[ -e $pid_file ]
|
36
|
+
}
|
37
|
+
|
38
|
+
start(){
|
39
|
+
echo -n $"Starting $prog: "
|
40
|
+
|
41
|
+
unset HOME MAIL USER USERNAME
|
42
|
+
$prog -c $config --pid-file=$pid_file $EXTRAOPTIONS
|
43
|
+
RETVAL=$?
|
44
|
+
echo
|
45
|
+
return $RETVAL
|
46
|
+
}
|
47
|
+
|
48
|
+
stop(){
|
49
|
+
echo -n $"Stopping $prog: "
|
50
|
+
if (is_running); then
|
51
|
+
kill `cat $pid_file`
|
52
|
+
RETVAL=$?
|
53
|
+
echo
|
54
|
+
return $RETVAL
|
55
|
+
else
|
56
|
+
echo "$pid_file not found"
|
57
|
+
fi
|
58
|
+
}
|
59
|
+
|
60
|
+
status(){
|
61
|
+
echo -n $"Checking for $pid_file: "
|
62
|
+
|
63
|
+
if (is_running); then
|
64
|
+
echo "found"
|
65
|
+
else
|
66
|
+
echo "not found"
|
67
|
+
fi
|
68
|
+
}
|
69
|
+
|
70
|
+
reload(){
|
71
|
+
restart
|
72
|
+
}
|
73
|
+
|
74
|
+
restart(){
|
75
|
+
stop
|
76
|
+
start
|
77
|
+
}
|
78
|
+
|
79
|
+
condrestart(){
|
80
|
+
is_running && restart
|
81
|
+
return 0
|
82
|
+
}
|
83
|
+
|
84
|
+
|
85
|
+
# See how we were called.
|
86
|
+
case "$1" in
|
87
|
+
start)
|
88
|
+
start
|
89
|
+
;;
|
90
|
+
stop)
|
91
|
+
stop
|
92
|
+
;;
|
93
|
+
status)
|
94
|
+
status
|
95
|
+
;;
|
96
|
+
restart)
|
97
|
+
restart
|
98
|
+
;;
|
99
|
+
reload)
|
100
|
+
reload
|
101
|
+
;;
|
102
|
+
condrestart)
|
103
|
+
condrestart
|
104
|
+
;;
|
105
|
+
*)
|
106
|
+
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
|
107
|
+
RETVAL=1
|
108
|
+
esac
|
109
|
+
|
110
|
+
exit $RETVAL
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'servolux'
|
3
|
+
|
4
|
+
require 'remote_syslog/eventmachine_reader'
|
5
|
+
require 'remote_syslog/file_tail_reader'
|
6
|
+
require 'remote_syslog/glob_watch'
|
7
|
+
require 'remote_syslog/message_generator'
|
8
|
+
require 'remote_syslog/udp_endpoint'
|
9
|
+
require 'remote_syslog/tls_endpoint'
|
10
|
+
require 'remote_syslog/tcp_endpoint'
|
11
|
+
|
12
|
+
module RemoteSyslog
|
13
|
+
class Agent < Servolux::Server
|
14
|
+
# Who should we connect to?
|
15
|
+
attr_accessor :destination_host, :destination_port
|
16
|
+
|
17
|
+
# Should use TCP?
|
18
|
+
attr_accessor :tcp
|
19
|
+
|
20
|
+
# Should use TLS?
|
21
|
+
attr_accessor :tls
|
22
|
+
|
23
|
+
# TLS settings
|
24
|
+
attr_accessor :client_cert_chain, :client_private_key, :server_cert
|
25
|
+
|
26
|
+
# syslog defaults
|
27
|
+
attr_accessor :facility, :severity, :hostname
|
28
|
+
|
29
|
+
# Other settings
|
30
|
+
attr_accessor :strip_color, :parse_fields, :prepend
|
31
|
+
|
32
|
+
# Exclude messages matching pattern
|
33
|
+
attr_accessor :exclude_pattern
|
34
|
+
|
35
|
+
# Files (can be globs)
|
36
|
+
attr_reader :files
|
37
|
+
|
38
|
+
# Exclude files matching pattern
|
39
|
+
attr_accessor :exclude_file_pattern
|
40
|
+
|
41
|
+
# How often should we check for new files?
|
42
|
+
attr_accessor :glob_check_interval
|
43
|
+
|
44
|
+
# Should we use eventmachine to tail?
|
45
|
+
attr_accessor :eventmachine_tail
|
46
|
+
|
47
|
+
def initialize(options = {})
|
48
|
+
@files = []
|
49
|
+
@glob_check_interval = 10
|
50
|
+
@eventmachine_tail = options.fetch(:eventmachine_tail, true)
|
51
|
+
|
52
|
+
unless logger = options[:logger]
|
53
|
+
logger = Logger.new(STDERR)
|
54
|
+
logger.level = Logger::ERROR
|
55
|
+
end
|
56
|
+
|
57
|
+
super('remote_syslog', :logger => logger, :pid_file => options[:pid_file])
|
58
|
+
end
|
59
|
+
|
60
|
+
def log_file=(file)
|
61
|
+
@log_file = File.expand_path(file)
|
62
|
+
|
63
|
+
level = self.logger.level
|
64
|
+
self.logger = Logger.new(file)
|
65
|
+
self.logger.level = level
|
66
|
+
end
|
67
|
+
|
68
|
+
def redirect_io!
|
69
|
+
if @log_file
|
70
|
+
STDOUT.reopen(@log_file, 'a')
|
71
|
+
STDERR.reopen(@log_file, 'a')
|
72
|
+
STDERR.sync = STDOUT.sync = true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def files=(files)
|
77
|
+
@files = [ @files, files ].flatten.compact.uniq
|
78
|
+
end
|
79
|
+
|
80
|
+
def watch_file(file)
|
81
|
+
if eventmachine_tail
|
82
|
+
RemoteSyslog::EventMachineReader.new(file,
|
83
|
+
:callback => @message_generator.method(:transmit),
|
84
|
+
:logger => logger)
|
85
|
+
else
|
86
|
+
RemoteSyslog::FileTailReader.new(file,
|
87
|
+
:callback => @message_generator.method(:transmit),
|
88
|
+
:logger => logger)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def run
|
93
|
+
EventMachine.run do
|
94
|
+
EM.error_handler do |e|
|
95
|
+
logger.error "Unhandled EventMachine Exception: #{e.class}: #{e.message}:\n\t#{e.backtrace.join("\n\t")}"
|
96
|
+
end
|
97
|
+
|
98
|
+
if @tls
|
99
|
+
max_message_size = 10240
|
100
|
+
|
101
|
+
connection = TlsEndpoint.new(@destination_host, @destination_port,
|
102
|
+
:client_cert_chain => @client_cert_chain,
|
103
|
+
:client_private_key => @client_private_key,
|
104
|
+
:server_cert => @server_cert,
|
105
|
+
:logger => logger)
|
106
|
+
elsif @tcp
|
107
|
+
max_message_size = 20480
|
108
|
+
|
109
|
+
connection = TcpEndpoint.new(@destination_host, @destination_port,
|
110
|
+
:logger => logger)
|
111
|
+
else
|
112
|
+
max_message_size = 1024
|
113
|
+
connection = UdpEndpoint.new(@destination_host, @destination_port,
|
114
|
+
:logger => logger)
|
115
|
+
end
|
116
|
+
|
117
|
+
@message_generator = RemoteSyslog::MessageGenerator.new(connection,
|
118
|
+
:facility => @facility, :severity => @severity,
|
119
|
+
:strip_color => @strip_color, :hostname => @hostname,
|
120
|
+
:parse_fields => @parse_fields, :exclude_pattern => @exclude_pattern,
|
121
|
+
:prepend => @prepend, :max_message_size => max_message_size)
|
122
|
+
|
123
|
+
files.each do |file|
|
124
|
+
RemoteSyslog::GlobWatch.new(file, @glob_check_interval,
|
125
|
+
@exclude_file_pattern, method(:watch_file))
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def endpoint_mode
|
131
|
+
@endpoint_mode ||= if @tls
|
132
|
+
'TCP/TLS'
|
133
|
+
elsif @tcp
|
134
|
+
'TCP'
|
135
|
+
else
|
136
|
+
'UDP'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def before_stopping
|
141
|
+
EM.stop
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,292 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'yaml'
|
3
|
+
require 'pathname'
|
4
|
+
require 'servolux'
|
5
|
+
|
6
|
+
require 'remote_syslog/agent'
|
7
|
+
|
8
|
+
module RemoteSyslog
|
9
|
+
class Cli
|
10
|
+
FIELD_REGEXES = {
|
11
|
+
'syslog' => /^(\w+ +\d+ \S+) (\S+) ([^: ]+):? (.*)$/,
|
12
|
+
'rfc3339' => /^(\S+) (\S+) ([^: ]+):? (.*)$/
|
13
|
+
}
|
14
|
+
|
15
|
+
DEFAULT_PID_FILES = [
|
16
|
+
"/var/run/remote_syslog.pid",
|
17
|
+
"#{ENV['HOME']}/run/remote_syslog.pid",
|
18
|
+
"#{ENV['HOME']}/tmp/remote_syslog.pid",
|
19
|
+
"#{ENV['HOME']}/remote_syslog.pid",
|
20
|
+
"#{ENV['TMPDIR']}/remote_syslog.pid",
|
21
|
+
"/tmp/remote_syslog.pid"
|
22
|
+
]
|
23
|
+
|
24
|
+
DEFAULT_CONFIG_FILE = '/etc/log_files.yml'
|
25
|
+
|
26
|
+
def self.process!(argv)
|
27
|
+
c = new(argv)
|
28
|
+
c.parse
|
29
|
+
c.run
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :program_name
|
33
|
+
|
34
|
+
def initialize(argv)
|
35
|
+
@argv = argv
|
36
|
+
@program_name = File.basename($0)
|
37
|
+
|
38
|
+
@strip_color = false
|
39
|
+
@exclude_pattern = nil
|
40
|
+
|
41
|
+
@daemonize_options = {
|
42
|
+
:ARGV => %w(start),
|
43
|
+
:dir_mode => :system,
|
44
|
+
:backtrace => false,
|
45
|
+
:monitor => false,
|
46
|
+
}
|
47
|
+
|
48
|
+
@agent = RemoteSyslog::Agent.new(:pid_file => default_pid_file)
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_file_writable?(file)
|
52
|
+
directory = File.dirname(file)
|
53
|
+
|
54
|
+
(File.directory?(directory) && File.writable?(directory) && !File.exist?(file)) || File.writable?(file)
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_pid_file
|
58
|
+
DEFAULT_PID_FILES.each do |file|
|
59
|
+
return file if is_file_writable?(file)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse
|
64
|
+
op = OptionParser.new do |opts|
|
65
|
+
opts.banner = "Usage: #{program_name} [OPTION]... <FILE>..."
|
66
|
+
opts.separator ''
|
67
|
+
|
68
|
+
opts.separator "Options:"
|
69
|
+
|
70
|
+
opts.on("-c", "--configfile PATH", "Path to config (/etc/log_files.yml)") do |v|
|
71
|
+
@configfile = v
|
72
|
+
end
|
73
|
+
opts.on("-d", "--dest-host HOSTNAME", "Destination syslog hostname or IP (logs.papertrailapp.com)") do |v|
|
74
|
+
@agent.destination_host = v
|
75
|
+
end
|
76
|
+
opts.on("-p", "--dest-port PORT", "Destination syslog port (514)") do |v|
|
77
|
+
@agent.destination_port = v
|
78
|
+
end
|
79
|
+
opts.on("-D", "--no-detach", "Don't daemonize and detach from the terminal") do
|
80
|
+
@no_detach = true
|
81
|
+
end
|
82
|
+
opts.on("-f", "--facility FACILITY", "Facility (user)") do |v|
|
83
|
+
@agent.facility = v
|
84
|
+
end
|
85
|
+
opts.on("--hostname HOST", "Local hostname to send from") do |v|
|
86
|
+
@agent.hostname = v
|
87
|
+
end
|
88
|
+
opts.on("-P", "--pid-dir DIRECTORY", "DEPRECATED: Directory to write .pid file in") do |v|
|
89
|
+
puts "Warning: --pid-dir is deprecated. Please use --pid-file FILENAME instead"
|
90
|
+
@pid_directory = v
|
91
|
+
end
|
92
|
+
opts.on("--pid-file FILENAME", "Location of the PID file (default #{@agent.pid_file})") do |v|
|
93
|
+
@agent.pid_file = v
|
94
|
+
end
|
95
|
+
opts.on("--parse-syslog", "Parse file as syslog-formatted file") do
|
96
|
+
@agent.parse_fields = FIELD_REGEXES['syslog']
|
97
|
+
end
|
98
|
+
opts.on("-s", "--severity SEVERITY", "Severity (notice)") do |v|
|
99
|
+
@agent.severity = v
|
100
|
+
end
|
101
|
+
opts.on("--strip-color", "Strip color codes") do
|
102
|
+
@agent.strip_color = true
|
103
|
+
end
|
104
|
+
opts.on("--tcp", "Connect via TCP (no TLS)") do
|
105
|
+
@agent.tcp = true
|
106
|
+
end
|
107
|
+
opts.on("--tls", "Connect via TCP with TLS") do
|
108
|
+
@agent.tls = true
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
opts.on("--new-file-check-interval INTERVAL", OptionParser::DecimalInteger,
|
113
|
+
"Time between checks for new files") do |v|
|
114
|
+
@agent.glob_check_interval = v
|
115
|
+
end
|
116
|
+
|
117
|
+
opts.separator ''
|
118
|
+
opts.separator 'Advanced options:'
|
119
|
+
|
120
|
+
opts.on("--[no-]eventmachine-tail", "Enable or disable using eventmachine-tail") do |v|
|
121
|
+
@agent.eventmachine_tail = v
|
122
|
+
end
|
123
|
+
opts.on("--debug-log FILE", "Log internal debug messages") do |v|
|
124
|
+
@agent.log_file = v
|
125
|
+
end
|
126
|
+
|
127
|
+
severities = Logger::Severity.constants + Logger::Severity.constants.map { |s| s.downcase }
|
128
|
+
opts.on("--debug-level LEVEL", severities, "Log internal debug messages at level") do |v|
|
129
|
+
@agent.logger.level = Logger::Severity.const_get(v.upcase)
|
130
|
+
end
|
131
|
+
|
132
|
+
opts.separator ""
|
133
|
+
opts.separator "Common options:"
|
134
|
+
|
135
|
+
opts.on("-h", "--help", "Show this message") do
|
136
|
+
puts opts
|
137
|
+
exit
|
138
|
+
end
|
139
|
+
|
140
|
+
opts.on("--version", "Show version") do
|
141
|
+
puts RemoteSyslog::VERSION
|
142
|
+
exit(0)
|
143
|
+
end
|
144
|
+
|
145
|
+
opts.separator ''
|
146
|
+
opts.separator "Example:"
|
147
|
+
opts.separator " $ #{program_name} -c configs/logs.yml -p 12345 /var/log/mysqld.log"
|
148
|
+
end
|
149
|
+
|
150
|
+
op.parse!(@argv)
|
151
|
+
|
152
|
+
@files = @argv.dup.delete_if { |a| a.empty? }
|
153
|
+
|
154
|
+
if @configfile
|
155
|
+
if File.exist?(@configfile)
|
156
|
+
parse_config(@configfile)
|
157
|
+
else
|
158
|
+
error "The config file specified could not be found: #{@configfile}"
|
159
|
+
end
|
160
|
+
elsif File.exist?(DEFAULT_CONFIG_FILE)
|
161
|
+
parse_config(DEFAULT_CONFIG_FILE)
|
162
|
+
end
|
163
|
+
|
164
|
+
if @files.empty?
|
165
|
+
error "You must specify at least one file to watch"
|
166
|
+
end
|
167
|
+
|
168
|
+
@agent.destination_host ||= 'logs.papertrailapp.com'
|
169
|
+
@agent.destination_port ||= 514
|
170
|
+
|
171
|
+
# handle relative paths before Daemonize changes the wd to / and expand wildcards
|
172
|
+
@files = @files.flatten.map { |f| File.expand_path(f) }.uniq
|
173
|
+
|
174
|
+
@agent.files = @files
|
175
|
+
|
176
|
+
if @pid_directory
|
177
|
+
if @agent.pid_file
|
178
|
+
@agent.pid_file = File.expand_path("#{@pid_directory}/#{File.basename(@agent.pid_file)}")
|
179
|
+
else
|
180
|
+
@agent.pid_file = File.expand_path("#{@pid_directory}/remote_syslog.pid")
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# We're dealing with an old-style pid_file
|
185
|
+
if @agent.pid_file && File.basename(@agent.pid_file) == @agent.pid_file
|
186
|
+
default_pid_dir = File.dirname(default_pid_file)
|
187
|
+
|
188
|
+
@agent.pid_file = File.join(default_pid_dir, @agent.pid_file)
|
189
|
+
|
190
|
+
if File.extname(@agent.pid_file) == ''
|
191
|
+
@agent.pid_file << '.pid'
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
@agent.pid_file ||= default_pid_file
|
196
|
+
|
197
|
+
if !@no_detach && !::Servolux.fork?
|
198
|
+
@no_detach = true
|
199
|
+
|
200
|
+
puts "Fork is not supported in this Ruby environment. Running in foreground."
|
201
|
+
end
|
202
|
+
rescue OptionParser::ParseError => e
|
203
|
+
error e.message, true
|
204
|
+
end
|
205
|
+
|
206
|
+
def parse_config(file)
|
207
|
+
config = YAML.load_file(file)
|
208
|
+
|
209
|
+
@files += Array(config['files'])
|
210
|
+
|
211
|
+
if config['destination'] && config['destination']['host']
|
212
|
+
@agent.destination_host ||= config['destination']['host']
|
213
|
+
end
|
214
|
+
|
215
|
+
if config['destination'] && config['destination']['port']
|
216
|
+
@agent.destination_port ||= config['destination']['port']
|
217
|
+
end
|
218
|
+
|
219
|
+
if config['hostname']
|
220
|
+
@agent.hostname = config['hostname']
|
221
|
+
end
|
222
|
+
|
223
|
+
@agent.server_cert = config['ssl_server_cert']
|
224
|
+
@agent.client_cert_chain = config['ssl_client_cert_chain']
|
225
|
+
@agent.client_private_key = config['ssl_client_private_key']
|
226
|
+
|
227
|
+
if config['parse_fields']
|
228
|
+
@agent.parse_fields = FIELD_REGEXES[config['parse_fields']] || Regexp.new(config['parse_fields'])
|
229
|
+
end
|
230
|
+
|
231
|
+
if config['exclude_patterns']
|
232
|
+
@agent.exclude_pattern = Regexp.new(config['exclude_patterns'].map { |r| "(#{r})" }.join('|'))
|
233
|
+
end
|
234
|
+
|
235
|
+
if config['exclude_files']
|
236
|
+
@agent.exclude_file_pattern = Regexp.new(config['exclude_files'].map { |r| "(#{r})" }.join('|'))
|
237
|
+
end
|
238
|
+
|
239
|
+
if config['new_file_check_interval']
|
240
|
+
@agent.glob_check_interval = config['new_file_check_interval']
|
241
|
+
end
|
242
|
+
|
243
|
+
if config['prepend']
|
244
|
+
@agent.prepend = config['prepend']
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def run
|
249
|
+
Thread.abort_on_exception = true
|
250
|
+
|
251
|
+
if @agent.tls && !EventMachine.ssl?
|
252
|
+
error "TLS is not supported by eventmachine installed on this system.\nThe openssl-devel/openssl-dev package must be installed before installing eventmachine."
|
253
|
+
end
|
254
|
+
|
255
|
+
if @no_detach
|
256
|
+
puts "Watching #{@agent.files.length} files/globs. Sending to #{@agent.destination_host}:#{@agent.destination_port} (#{@agent.endpoint_mode})."
|
257
|
+
@agent.run
|
258
|
+
else
|
259
|
+
daemon = Servolux::Daemon.new(:server => @agent, :after_fork => method(:redirect_io))
|
260
|
+
|
261
|
+
if daemon.alive?
|
262
|
+
error "Already running at #{@agent.pid_file}. To run another instance, specify a different `--pid-file`.", true
|
263
|
+
end
|
264
|
+
|
265
|
+
puts "Watching #{@agent.files.length} files/globs. Sending to #{@agent.destination_host}:#{@agent.destination_port} (#{@agent.endpoint_mode})."
|
266
|
+
daemon.startup
|
267
|
+
end
|
268
|
+
rescue Servolux::Daemon::StartupError => e
|
269
|
+
case message = e.message[/^(Child raised error: )?(.*)$/, 2]
|
270
|
+
when /#<Errno::EACCES: (.*)>$/
|
271
|
+
error $1
|
272
|
+
else
|
273
|
+
error message
|
274
|
+
end
|
275
|
+
rescue Interrupt
|
276
|
+
exit(0)
|
277
|
+
end
|
278
|
+
|
279
|
+
def redirect_io
|
280
|
+
@agent.redirect_io!
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
def error(message, try_help = false)
|
285
|
+
puts "#{program_name}: #{message}"
|
286
|
+
if try_help
|
287
|
+
puts "Try `#{program_name} --help' for more information."
|
288
|
+
end
|
289
|
+
exit(1)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'eventmachine-tail'
|
3
|
+
require 'em-dns-resolver'
|
4
|
+
|
5
|
+
# Force eventmachine-tail not to change the encoding
|
6
|
+
# This will allow ruby 1.9 to deal with any file data
|
7
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
8
|
+
EventMachine::FileTail::FORCE_ENCODING = false
|
9
|
+
$VERBOSE = old_verbose
|
10
|
+
|
11
|
+
module RemoteSyslog
|
12
|
+
class EventMachineReader < EventMachine::FileTail
|
13
|
+
def initialize(path, options = {}, &block)
|
14
|
+
@callback = options[:callback] || block
|
15
|
+
@buffer = BufferedTokenizer.new
|
16
|
+
@logger = options[:logger] || Logger.new(STDERR)
|
17
|
+
|
18
|
+
@tag = options[:program] || File.basename(path)
|
19
|
+
|
20
|
+
# Remove characters that can't be in a tag
|
21
|
+
@tag = @tag.gsub(%r{[: \]\[\\]+}, '-')
|
22
|
+
|
23
|
+
# Make sure the tag isn't too long
|
24
|
+
if @tag.length > 32
|
25
|
+
@tag = @tag[0..31]
|
26
|
+
end
|
27
|
+
|
28
|
+
@logger.debug "Watching #{path} with EventMachineReader"
|
29
|
+
|
30
|
+
super(path, -1)
|
31
|
+
end
|
32
|
+
|
33
|
+
def receive_data(data)
|
34
|
+
@buffer.extract(data).each do |line|
|
35
|
+
@callback.call(@tag, line)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_exception(exception)
|
40
|
+
@logger.error "Exception: #{exception.class}: #{exception.message}\n\t#{exception.backtrace.join("\n\t")}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'file/tail'
|
2
|
+
|
3
|
+
module RemoteSyslog
|
4
|
+
class FileTailReader
|
5
|
+
def initialize(path, options = {}, &block)
|
6
|
+
@path = path
|
7
|
+
@callback = options[:callback] || block
|
8
|
+
@logger = options[:logger] || Logger.new(STDERR)
|
9
|
+
@tag = options[:program] || File.basename(path)
|
10
|
+
|
11
|
+
# Remove characters that can't be in a tag
|
12
|
+
@tag = @tag.gsub(%r{[: \]\[\\]+}, '-')
|
13
|
+
|
14
|
+
# Make sure the tag isn't too long
|
15
|
+
if @tag.length > 32
|
16
|
+
@tag = @tag[0..31]
|
17
|
+
end
|
18
|
+
|
19
|
+
@logger.debug "Watching #{path} with FileTailReader"
|
20
|
+
|
21
|
+
start
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
@thread = Thread.new do
|
26
|
+
run
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
File::Tail::Logfile.tail(@path) do |line|
|
32
|
+
EventMachine.schedule do
|
33
|
+
@callback.call(@tag, line)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
rescue => e
|
37
|
+
@logger.error "Unhandled FileTailReader Exception: #{e.class}: #{e.message}:\n\t#{e.backtrace.join("\n\t")}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'eventmachine-tail'
|
2
|
+
|
3
|
+
module RemoteSyslog
|
4
|
+
class GlobWatch < EventMachine::FileGlobWatch
|
5
|
+
def initialize(path, interval, exclude_files, callback)
|
6
|
+
@exclude_files = exclude_files
|
7
|
+
@callback = callback
|
8
|
+
|
9
|
+
super(path, interval)
|
10
|
+
end
|
11
|
+
|
12
|
+
def file_found(path)
|
13
|
+
# Check if we should exclude this file
|
14
|
+
if @exclude_files && @exclude_files =~ path
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
@callback.call(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def file_deleted(path)
|
22
|
+
# Nothing to do
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|