remote_syslog 1.6.5 → 1.6.6.rc1
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.
- data/README.md +18 -6
- data/bin/remote_syslog +0 -4
- data/lib/remote_syslog/agent.rb +103 -0
- data/lib/remote_syslog/cli.rb +155 -97
- data/lib/remote_syslog/eventmachine_reader.rb +43 -0
- data/lib/remote_syslog/file_tail_reader.rb +38 -0
- data/lib/remote_syslog/glob_watch.rb +18 -0
- data/lib/remote_syslog/message_generator.rb +54 -0
- data/lib/remote_syslog/tls_endpoint.rb +13 -1
- data/lib/remote_syslog/udp_endpoint.rb +7 -2
- data/lib/remote_syslog.rb +1 -4
- data/remote_syslog.gemspec +9 -4
- metadata +117 -77
- data/lib/remote_syslog/reader.rb +0 -87
data/README.md
CHANGED
|
@@ -71,6 +71,12 @@ to `a.server.com:514`:
|
|
|
71
71
|
|
|
72
72
|
$ remote_syslog -D -d a.server.com -f local0 -P /tmp /var/log/mysqld.log
|
|
73
73
|
|
|
74
|
+
### Windows
|
|
75
|
+
|
|
76
|
+
To run in Windows, start in a DOS Prompt or batch file and do not daemonize:
|
|
77
|
+
|
|
78
|
+
C:\> remote_syslog -D
|
|
79
|
+
|
|
74
80
|
## Auto-starting at boot
|
|
75
81
|
|
|
76
82
|
The gem includes sample [init files] such as [remote_syslog.init.d]. remote_syslog will
|
|
@@ -141,13 +147,19 @@ Provide a client certificate when connecting via TLS:
|
|
|
141
147
|
|
|
142
148
|
### Detecting new files
|
|
143
149
|
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
remote_syslog automatically detects and activates new log files that match
|
|
151
|
+
its file specifiers. For example, `*.log` may be provided as a file specifier,
|
|
152
|
+
and remote_syslog will detect a `some.log` file created after it was started.
|
|
153
|
+
Globs are re-checked every 60 seconds. Ruby's `Dir.glob` is used.
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
155
|
+
Also, explicitly-provided filenames need not exist when `remote_syslog` is
|
|
156
|
+
started. `remote_syslog` can be pre-configured to monitor log files which are
|
|
157
|
+
created later (or may never be created).
|
|
158
|
+
|
|
159
|
+
If globs are specified on the command-line, enclose each one in single-quotes
|
|
160
|
+
(`'*.log'`) so the shell passes the raw glob string to remote_syslog (rather
|
|
161
|
+
than the current set of matches). This is not necessary for globs defined in
|
|
162
|
+
the config file.
|
|
151
163
|
|
|
152
164
|
|
|
153
165
|
### Multiple instances
|
data/bin/remote_syslog
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
|
|
11
|
+
module RemoteSyslog
|
|
12
|
+
class Agent < Servolux::Server
|
|
13
|
+
# Who should we connect to?
|
|
14
|
+
attr_accessor :destination_host, :destination_port
|
|
15
|
+
|
|
16
|
+
# Should use TLS?
|
|
17
|
+
attr_accessor :tls
|
|
18
|
+
|
|
19
|
+
# TLS settings
|
|
20
|
+
attr_accessor :client_cert_chain, :client_private_key, :server_cert
|
|
21
|
+
|
|
22
|
+
# syslog defaults
|
|
23
|
+
attr_accessor :facility, :severity, :hostname
|
|
24
|
+
|
|
25
|
+
# Other settings
|
|
26
|
+
attr_accessor :strip_color, :parse_fields, :exclude_pattern
|
|
27
|
+
|
|
28
|
+
# Files
|
|
29
|
+
attr_reader :files
|
|
30
|
+
|
|
31
|
+
# How often should we check for new files?
|
|
32
|
+
attr_accessor :glob_check_interval
|
|
33
|
+
|
|
34
|
+
# Should we use eventmachine to tail?
|
|
35
|
+
attr_accessor :eventmachine_tail
|
|
36
|
+
|
|
37
|
+
def initialize(options = {})
|
|
38
|
+
@files = []
|
|
39
|
+
@glob_check_interval = 60
|
|
40
|
+
@eventmachine_tail = options.fetch(:eventmachine_tail, true)
|
|
41
|
+
|
|
42
|
+
unless logger = options[:logger]
|
|
43
|
+
logger = Logger.new(STDERR)
|
|
44
|
+
logger.level = Logger::ERROR
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
super('remote_syslog', :logger => logger, :pid_file => options[:pid_file])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def files=(files)
|
|
51
|
+
@files = [ @files, files ].flatten.compact.uniq
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def watch_file(file)
|
|
55
|
+
if eventmachine_tail
|
|
56
|
+
RemoteSyslog::EventMachineReader.new(file,
|
|
57
|
+
:callback => @message_generator.method(:transmit),
|
|
58
|
+
:logger => logger)
|
|
59
|
+
else
|
|
60
|
+
RemoteSyslog::FileTailReader.new(file,
|
|
61
|
+
:callback => @message_generator.method(:transmit),
|
|
62
|
+
:logger => logger)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def run
|
|
67
|
+
EventMachine.run do
|
|
68
|
+
EM.error_handler do |e|
|
|
69
|
+
logger.error "Unhandled EventMachine Exception: #{e.class}: #{e.message}:\n\t#{e.backtrace.join("\n\t")}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if @tls
|
|
73
|
+
max_message_size = 10240
|
|
74
|
+
|
|
75
|
+
connection = TlsEndpoint.new(@destination_host, @destination_port,
|
|
76
|
+
:client_cert_chain => @client_cert_chain,
|
|
77
|
+
:client_private_key => @client_private_key,
|
|
78
|
+
:server_cert => @server_cert,
|
|
79
|
+
:logger => logger)
|
|
80
|
+
else
|
|
81
|
+
max_message_size = 1024
|
|
82
|
+
connection = UdpEndpoint.new(@destination_host, @destination_port,
|
|
83
|
+
:logger => logger)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
@message_generator = RemoteSyslog::MessageGenerator.new(connection,
|
|
87
|
+
:facility => @facility, :severity => @severity,
|
|
88
|
+
:strip_color => @strip_color, :hostname => @hostname,
|
|
89
|
+
:parse_fields => @parse_fields, :exclude_pattern => @exclude_pattern,
|
|
90
|
+
:max_message_size => max_message_size)
|
|
91
|
+
|
|
92
|
+
files.each do |file|
|
|
93
|
+
RemoteSyslog::GlobWatch.new(file, @glob_check_interval,
|
|
94
|
+
method(:watch_file))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def before_stopping
|
|
100
|
+
EM.stop
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/remote_syslog/cli.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
require 'optparse'
|
|
2
2
|
require 'yaml'
|
|
3
3
|
require 'pathname'
|
|
4
|
-
require '
|
|
4
|
+
require 'servolux'
|
|
5
5
|
|
|
6
|
+
require 'remote_syslog/agent'
|
|
6
7
|
|
|
7
8
|
module RemoteSyslog
|
|
8
9
|
class Cli
|
|
@@ -11,18 +12,29 @@ module RemoteSyslog
|
|
|
11
12
|
'rfc3339' => /^(\S+) (\S+) ([^: ]+):? (.*)$/
|
|
12
13
|
}
|
|
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
|
+
|
|
14
26
|
def self.process!(argv)
|
|
15
27
|
c = new(argv)
|
|
16
28
|
c.parse
|
|
17
29
|
c.run
|
|
18
30
|
end
|
|
19
31
|
|
|
32
|
+
attr_reader :program_name
|
|
33
|
+
|
|
20
34
|
def initialize(argv)
|
|
21
35
|
@argv = argv
|
|
36
|
+
@program_name = File.basename($0)
|
|
22
37
|
|
|
23
|
-
@app_name = File.basename($0) || 'remote_syslog'
|
|
24
|
-
|
|
25
|
-
@configfile = '/etc/log_files.yml'
|
|
26
38
|
@strip_color = false
|
|
27
39
|
@exclude_pattern = nil
|
|
28
40
|
|
|
@@ -32,168 +44,214 @@ module RemoteSyslog
|
|
|
32
44
|
:backtrace => false,
|
|
33
45
|
:monitor => false,
|
|
34
46
|
}
|
|
47
|
+
|
|
48
|
+
@agent = RemoteSyslog::Agent.new(:pid_file => default_pid_file)
|
|
35
49
|
end
|
|
36
50
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
51
|
+
def is_file_writable?(file)
|
|
52
|
+
directory = File.dirname(file)
|
|
53
|
+
|
|
54
|
+
(File.directory?(directory) && File.writable?(directory) && !File.exists?(file)) || File.writable?(file)
|
|
55
|
+
end
|
|
43
56
|
|
|
44
|
-
|
|
57
|
+
def default_pid_file
|
|
58
|
+
DEFAULT_PID_FILES.each do |file|
|
|
59
|
+
return file if is_file_writable?(file)
|
|
60
|
+
end
|
|
45
61
|
end
|
|
46
62
|
|
|
47
63
|
def parse
|
|
48
64
|
op = OptionParser.new do |opts|
|
|
49
|
-
opts.banner = "Usage:
|
|
50
|
-
opts.separator ''
|
|
51
|
-
opts.separator "Example: remote_syslog -c configs/logs.yml -p 12345 /var/log/mysqld.log"
|
|
65
|
+
opts.banner = "Usage: #{program_name} [OPTION]... <FILE>..."
|
|
52
66
|
opts.separator ''
|
|
67
|
+
|
|
53
68
|
opts.separator "Options:"
|
|
54
69
|
|
|
55
70
|
opts.on("-c", "--configfile PATH", "Path to config (/etc/log_files.yml)") do |v|
|
|
56
|
-
@configfile =
|
|
71
|
+
@configfile = v
|
|
57
72
|
end
|
|
58
73
|
opts.on("-d", "--dest-host HOSTNAME", "Destination syslog hostname or IP (logs.papertrailapp.com)") do |v|
|
|
59
|
-
@
|
|
74
|
+
@agent.destination_host = v
|
|
60
75
|
end
|
|
61
76
|
opts.on("-p", "--dest-port PORT", "Destination syslog port (514)") do |v|
|
|
62
|
-
@
|
|
77
|
+
@agent.destination_port = v
|
|
63
78
|
end
|
|
64
79
|
opts.on("-D", "--no-detach", "Don't daemonize and detach from the terminal") do
|
|
65
80
|
@no_detach = true
|
|
66
81
|
end
|
|
67
82
|
opts.on("-f", "--facility FACILITY", "Facility (user)") do |v|
|
|
68
|
-
@facility = v
|
|
83
|
+
@agent.facility = v
|
|
69
84
|
end
|
|
70
85
|
opts.on("--hostname HOST", "Local hostname to send from") do |v|
|
|
71
|
-
@hostname = v
|
|
86
|
+
@agent.hostname = v
|
|
72
87
|
end
|
|
73
|
-
opts.on("-P", "--pid-dir DIRECTORY", "Directory to write .pid file in
|
|
74
|
-
|
|
75
|
-
@
|
|
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
|
|
76
91
|
end
|
|
77
|
-
opts.on("--pid-file FILENAME", "PID
|
|
78
|
-
|
|
92
|
+
opts.on("--pid-file FILENAME", "Location of the PID file (default #{@agent.pid_file})") do |v|
|
|
93
|
+
@agent.pid_file = v
|
|
79
94
|
end
|
|
80
95
|
opts.on("--parse-syslog", "Parse file as syslog-formatted file") do
|
|
81
|
-
@parse_fields = FIELD_REGEXES['syslog']
|
|
96
|
+
@agent.parse_fields = FIELD_REGEXES['syslog']
|
|
82
97
|
end
|
|
83
98
|
opts.on("-s", "--severity SEVERITY", "Severity (notice)") do |v|
|
|
84
|
-
@severity = v
|
|
99
|
+
@agent.severity = v
|
|
85
100
|
end
|
|
86
101
|
opts.on("--strip-color", "Strip color codes") do
|
|
87
|
-
@strip_color = true
|
|
102
|
+
@agent.strip_color = true
|
|
88
103
|
end
|
|
89
104
|
opts.on("--tls", "Connect via TCP with TLS") do
|
|
90
|
-
@tls = true
|
|
105
|
+
@agent.tls = true
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
opts.on("--new-file-check-interval INTERVAL", OptionParser::DecimalInteger,
|
|
110
|
+
"Time between checks for new files") do |v|
|
|
111
|
+
@agent.glob_check_interval = v
|
|
91
112
|
end
|
|
92
|
-
|
|
113
|
+
|
|
114
|
+
opts.separator ''
|
|
115
|
+
opts.separator 'Advanced options:'
|
|
116
|
+
|
|
117
|
+
opts.on("--[no-]eventmachine-tail", "Enable or disable using eventmachine-tail") do |v|
|
|
118
|
+
@agent.eventmachine_tail = v
|
|
119
|
+
end
|
|
120
|
+
opts.on("--debug-log FILE", "Log internal debug messages") do |v|
|
|
121
|
+
level = @agent.logger.level
|
|
122
|
+
@agent.logger = Logger.new(v)
|
|
123
|
+
@agent.logger.level = level
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
severities = Logger::Severity.constants + Logger::Severity.constants.map { |s| s.downcase }
|
|
127
|
+
opts.on("--debug-level LEVEL", severities, "Log internal debug messages at level") do |v|
|
|
128
|
+
@agent.logger.level = Logger::Severity.const_get(v.upcase)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
opts.separator ""
|
|
132
|
+
opts.separator "Common options:"
|
|
133
|
+
|
|
134
|
+
opts.on("-h", "--help", "Show this message") do
|
|
93
135
|
puts opts
|
|
94
136
|
exit
|
|
95
137
|
end
|
|
138
|
+
|
|
139
|
+
opts.on("--version", "Show version") do
|
|
140
|
+
puts RemoteSyslog::VERSION
|
|
141
|
+
exit(0)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
opts.separator ''
|
|
145
|
+
opts.separator "Example:"
|
|
146
|
+
opts.separator " $ #{program_name} -c configs/logs.yml -p 12345 /var/log/mysqld.log"
|
|
96
147
|
end
|
|
97
148
|
|
|
98
149
|
op.parse!(@argv)
|
|
99
150
|
|
|
100
151
|
@files = @argv.dup.delete_if { |a| a.empty? }
|
|
101
152
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
153
|
+
if @configfile
|
|
154
|
+
if File.exists?(@configfile)
|
|
155
|
+
parse_config(@configfile)
|
|
156
|
+
else
|
|
157
|
+
error "The config file specified could not be found: #{@configfile}"
|
|
158
|
+
end
|
|
159
|
+
elsif File.exists?(DEFAULT_CONFIG_FILE)
|
|
160
|
+
parse_config(DEFAULT_CONFIG_FILE)
|
|
161
|
+
end
|
|
106
162
|
|
|
107
163
|
if @files.empty?
|
|
108
|
-
|
|
109
|
-
puts ''
|
|
110
|
-
puts op
|
|
111
|
-
exit
|
|
164
|
+
error "You must specify at least one file to watch"
|
|
112
165
|
end
|
|
113
166
|
|
|
167
|
+
@agent.destination_host ||= 'logs.papertrailapp.com'
|
|
168
|
+
@agent.destination_port ||= 514
|
|
169
|
+
|
|
114
170
|
# handle relative paths before Daemonize changes the wd to / and expand wildcards
|
|
115
171
|
@files = @files.flatten.map { |f| File.expand_path(f) }.uniq
|
|
116
172
|
|
|
173
|
+
@agent.files = @files
|
|
174
|
+
|
|
175
|
+
if @pid_directory
|
|
176
|
+
if @agent.pid_file
|
|
177
|
+
@agent.pid_file = File.expand_path("#{@pid_directory}/#{@agent.pid_file}")
|
|
178
|
+
else
|
|
179
|
+
@agent.pid_file = File.expand_path("#{@pid_directory}/remote_syslog.pid")
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
@agent.pid_file ||= default_pid_file
|
|
184
|
+
|
|
185
|
+
if !@no_detach && !::Servolux.fork?
|
|
186
|
+
@no_detach = true
|
|
187
|
+
|
|
188
|
+
puts "Fork is not supported in this Ruby environment. Running in foreground."
|
|
189
|
+
end
|
|
190
|
+
rescue OptionParser::ParseError => e
|
|
191
|
+
error e.message, true
|
|
117
192
|
end
|
|
118
193
|
|
|
119
|
-
def parse_config
|
|
120
|
-
|
|
121
|
-
config = YAML.load_file(@configfile)
|
|
194
|
+
def parse_config(file)
|
|
195
|
+
config = YAML.load_file(file)
|
|
122
196
|
|
|
123
|
-
|
|
197
|
+
@files += Array(config['files'])
|
|
124
198
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
199
|
+
if config['destination'] && config['destination']['host']
|
|
200
|
+
@agent.destination_host ||= config['destination']['host']
|
|
201
|
+
end
|
|
128
202
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
203
|
+
if config['destination'] && config['destination']['port']
|
|
204
|
+
@agent.destination_port ||= config['destination']['port']
|
|
205
|
+
end
|
|
132
206
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
207
|
+
if config['hostname']
|
|
208
|
+
@agent.hostname = config['hostname']
|
|
209
|
+
end
|
|
136
210
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
211
|
+
@agent.server_cert = config['ssl_server_cert']
|
|
212
|
+
@agent.client_cert_chain = config['ssl_client_cert_chain']
|
|
213
|
+
@agent.client_private_key = config['ssl_client_private_key']
|
|
140
214
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
215
|
+
if config['parse_fields']
|
|
216
|
+
@agent.parse_fields = FIELD_REGEXES[config['parse_fields']] || Regexp.new(config['parse_fields'])
|
|
217
|
+
end
|
|
144
218
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
end
|
|
219
|
+
if config['exclude_patterns']
|
|
220
|
+
@agent.exclude_pattern = Regexp.new(config['exclude_patterns'].map { |r| "(#{r})" }.join('|'))
|
|
148
221
|
end
|
|
149
222
|
end
|
|
150
223
|
|
|
151
224
|
def run
|
|
152
|
-
puts "Watching #{@files.length} files/paths. Sending to #{@dest_host}:#{@dest_port} (#{@tls ? 'TCP/TLS' : 'UDP'})."
|
|
153
|
-
|
|
154
225
|
if @no_detach
|
|
155
|
-
|
|
226
|
+
puts "Watching #{@agent.files.length} files/paths. Sending to #{@agent.destination_host}:#{@agent.destination_port} (#{@agent.tls ? 'TCP/TLS' : 'UDP'})."
|
|
227
|
+
@agent.run
|
|
156
228
|
else
|
|
157
|
-
|
|
158
|
-
|
|
229
|
+
daemon = Servolux::Daemon.new(:server => @agent)
|
|
230
|
+
|
|
231
|
+
if daemon.alive?
|
|
232
|
+
error "Already running at #{@agent.pid_file}. To run another instance, specify a different `--pid-file`.", true
|
|
159
233
|
end
|
|
234
|
+
|
|
235
|
+
puts "Watching #{@agent.files.length} files/paths. Sending to #{@agent.destination_host}:#{@agent.destination_port} (#{@agent.tls ? 'TCP/TLS' : 'UDP'})."
|
|
236
|
+
daemon.startup
|
|
160
237
|
end
|
|
238
|
+
rescue Servolux::Daemon::StartupError => e
|
|
239
|
+
case message = e.message[/^(Child raised error: )?(.*)$/, 2]
|
|
240
|
+
when /#<Errno::EACCES: (.*)>$/
|
|
241
|
+
error $1
|
|
242
|
+
else
|
|
243
|
+
error message
|
|
244
|
+
end
|
|
245
|
+
rescue Interrupt
|
|
246
|
+
exit(0)
|
|
161
247
|
end
|
|
162
248
|
|
|
163
|
-
def
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
:client_cert_chain => @client_cert_chain,
|
|
168
|
-
:client_private_key => @client_private_key,
|
|
169
|
-
:server_cert => @server_cert)
|
|
170
|
-
else
|
|
171
|
-
connection = UdpEndpoint.new(@dest_host, @dest_port)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
@files.each do |path|
|
|
175
|
-
begin
|
|
176
|
-
glob_check_interval = 60
|
|
177
|
-
exclude_files = []
|
|
178
|
-
max_message_size = 1024
|
|
179
|
-
|
|
180
|
-
if @tls
|
|
181
|
-
max_message_size = 10240
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
EventMachine::FileGlobWatchTail.new(path, RemoteSyslog::Reader,
|
|
185
|
-
glob_check_interval, exclude_files,
|
|
186
|
-
@dest_host, @dest_port,
|
|
187
|
-
:socket => connection, :facility => @facility,
|
|
188
|
-
:severity => @severity, :strip_color => @strip_color,
|
|
189
|
-
:hostname => @hostname, :parse_fields => @parse_fields,
|
|
190
|
-
:exclude_pattern => @exclude_pattern,
|
|
191
|
-
:max_message_size => max_message_size)
|
|
192
|
-
rescue Errno::ENOENT => e
|
|
193
|
-
puts "#{path} not found, continuing. (#{e.message})"
|
|
194
|
-
end
|
|
195
|
-
end
|
|
249
|
+
def error(message, try_help = false)
|
|
250
|
+
puts "#{program_name}: #{message}"
|
|
251
|
+
if try_help
|
|
252
|
+
puts "Try `#{program_name} --help' for more information."
|
|
196
253
|
end
|
|
254
|
+
exit(1)
|
|
197
255
|
end
|
|
198
256
|
end
|
|
199
257
|
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,38 @@
|
|
|
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
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'eventmachine-tail'
|
|
2
|
+
|
|
3
|
+
module RemoteSyslog
|
|
4
|
+
class GlobWatch < EventMachine::FileGlobWatch
|
|
5
|
+
def initialize(path, interval, callback)
|
|
6
|
+
super(path, interval)
|
|
7
|
+
@callback = callback
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def file_found(path)
|
|
11
|
+
@callback.call(path)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def file_deleted(path)
|
|
15
|
+
# Nothing to do
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
require 'syslog_protocol'
|
|
3
|
+
|
|
4
|
+
module RemoteSyslog
|
|
5
|
+
class MessageGenerator
|
|
6
|
+
COLORED_REGEXP = /\e\[(?:(?:[0-9]{1,3});){0,2}(?:[0-9]{1,3})m/
|
|
7
|
+
|
|
8
|
+
def initialize(socket, options = {})
|
|
9
|
+
@socket = socket
|
|
10
|
+
|
|
11
|
+
@parse_fields = options[:parse_fields]
|
|
12
|
+
@strip_color = options[:strip_color]
|
|
13
|
+
@exclude_pattern = options[:exclude_pattern]
|
|
14
|
+
@max_message_size = options[:max_message_size] || 1024
|
|
15
|
+
|
|
16
|
+
@packet = SyslogProtocol::Packet.new
|
|
17
|
+
|
|
18
|
+
if options[:hostname] && options[:hostname] != ''
|
|
19
|
+
local_hostname = options[:hostname]
|
|
20
|
+
else
|
|
21
|
+
local_hostname = (Socket.gethostname rescue `hostname`.chomp)[/^([^\.]+)/, 1]
|
|
22
|
+
|
|
23
|
+
if local_hostname.nil? || local_hostname == ''
|
|
24
|
+
local_hostname = 'localhost'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@packet.hostname = local_hostname
|
|
29
|
+
@packet.facility = options[:facility] || 'user'
|
|
30
|
+
@packet.severity = options[:severity] || 'notice'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def transmit(tag, message)
|
|
34
|
+
return if @exclude_pattern && message =~ @exclude_pattern
|
|
35
|
+
|
|
36
|
+
message = message.gsub(COLORED_REGEXP, '') if @strip_color
|
|
37
|
+
|
|
38
|
+
packet = @packet.dup
|
|
39
|
+
packet.content = message
|
|
40
|
+
|
|
41
|
+
if @parse_fields && message =~ @parse_fields
|
|
42
|
+
packet.hostname = $2 if $2 && $2 != ''
|
|
43
|
+
packet.tag = $3 if $3 && $3 != ''
|
|
44
|
+
packet.content = $4 if $4 && $4 != ''
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
unless packet.tag
|
|
48
|
+
packet.tag = tag
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@socket.write(packet.assemble(@max_message_size))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'eventmachine'
|
|
2
|
+
|
|
1
3
|
module RemoteSyslog
|
|
2
4
|
class TlsEndpoint
|
|
3
5
|
class Handler < EventMachine::Connection
|
|
@@ -29,12 +31,15 @@ module RemoteSyslog
|
|
|
29
31
|
attr_accessor :connection
|
|
30
32
|
attr_reader :server_cert, :client_cert_chain, :client_private_key
|
|
31
33
|
|
|
34
|
+
attr_reader :logger
|
|
35
|
+
|
|
32
36
|
def initialize(address, port, options = {})
|
|
33
37
|
@address = address
|
|
34
38
|
@port = port.to_i
|
|
35
39
|
@client_cert_chain = options[:client_cert_chain]
|
|
36
40
|
@client_private_key = options[:client_private_key]
|
|
37
41
|
@queue_limit = options[:queue_limit] || 10_000
|
|
42
|
+
@logger = options[:logger] || Logger.new(STDERR)
|
|
38
43
|
|
|
39
44
|
if options[:server_cert]
|
|
40
45
|
@server_cert = OpenSSL::X509::Certificate.new(File.read(options[:server_cert]))
|
|
@@ -51,6 +56,12 @@ module RemoteSyslog
|
|
|
51
56
|
connect
|
|
52
57
|
end
|
|
53
58
|
|
|
59
|
+
def connection=(conn)
|
|
60
|
+
port, ip = Socket.unpack_sockaddr_in(conn.get_peername)
|
|
61
|
+
logger.debug "Connected to #{ip}:#{port}"
|
|
62
|
+
@connection = conn
|
|
63
|
+
end
|
|
64
|
+
|
|
54
65
|
def resolve_address
|
|
55
66
|
request = EventMachine::DnsResolver.resolve(@address)
|
|
56
67
|
request.callback do |addrs|
|
|
@@ -63,6 +74,7 @@ module RemoteSyslog
|
|
|
63
74
|
end
|
|
64
75
|
|
|
65
76
|
def connect
|
|
77
|
+
logger.debug "Connecting to #{address}:#{@port}"
|
|
66
78
|
EventMachine.connect(address, @port, TlsEndpoint::Handler, self)
|
|
67
79
|
end
|
|
68
80
|
|
|
@@ -86,4 +98,4 @@ module RemoteSyslog
|
|
|
86
98
|
end
|
|
87
99
|
end
|
|
88
100
|
end
|
|
89
|
-
end
|
|
101
|
+
end
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
require 'eventmachine'
|
|
2
|
+
|
|
1
3
|
module RemoteSyslog
|
|
2
4
|
class UdpEndpoint
|
|
3
|
-
|
|
5
|
+
attr_reader :logger
|
|
6
|
+
|
|
7
|
+
def initialize(address, port, options = {})
|
|
4
8
|
@address = address
|
|
5
9
|
@port = port.to_i
|
|
6
10
|
@socket = EventMachine.open_datagram_socket('0.0.0.0', 0)
|
|
11
|
+
@logger = options[:logger] || Logger.new(STDERR)
|
|
7
12
|
|
|
8
13
|
# Try to resolve the address
|
|
9
14
|
resolve_address
|
|
@@ -29,4 +34,4 @@ module RemoteSyslog
|
|
|
29
34
|
@socket.send_datagram(value, address, @port)
|
|
30
35
|
end
|
|
31
36
|
end
|
|
32
|
-
end
|
|
37
|
+
end
|
data/lib/remote_syslog.rb
CHANGED
data/remote_syslog.gemspec
CHANGED
|
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
## If your rubyforge_project name is different, then edit it and comment out
|
|
9
9
|
## the sub! line in the Rakefile
|
|
10
10
|
s.name = 'remote_syslog'
|
|
11
|
-
s.version = '1.6.
|
|
12
|
-
s.date = '2012-
|
|
11
|
+
s.version = '1.6.6.rc1'
|
|
12
|
+
s.date = '2012-08-13'
|
|
13
13
|
s.rubyforge_project = 'remote_syslog'
|
|
14
14
|
|
|
15
15
|
## Make sure your summary is short. The description may be as long
|
|
@@ -40,7 +40,8 @@ Gem::Specification.new do |s|
|
|
|
40
40
|
## List your runtime dependencies here. Runtime dependencies are those
|
|
41
41
|
## that are needed for an end user to actually USE your code.
|
|
42
42
|
#s.add_dependency('DEPNAME', [">= 1.1.0", "< 2.0.0"])
|
|
43
|
-
s.add_dependency '
|
|
43
|
+
s.add_dependency 'servolux'
|
|
44
|
+
s.add_dependency 'file-tail'
|
|
44
45
|
s.add_dependency 'eventmachine', [ '>= 0.12.10', '< 1.1' ]
|
|
45
46
|
s.add_dependency 'eventmachine-tail'
|
|
46
47
|
s.add_dependency 'syslog_protocol', [ '~> 0.9.2' ]
|
|
@@ -67,8 +68,12 @@ Gem::Specification.new do |s|
|
|
|
67
68
|
examples/remote_syslog.supervisor.conf
|
|
68
69
|
examples/remote_syslog.upstart.conf
|
|
69
70
|
lib/remote_syslog.rb
|
|
71
|
+
lib/remote_syslog/agent.rb
|
|
70
72
|
lib/remote_syslog/cli.rb
|
|
71
|
-
lib/remote_syslog/
|
|
73
|
+
lib/remote_syslog/eventmachine_reader.rb
|
|
74
|
+
lib/remote_syslog/file_tail_reader.rb
|
|
75
|
+
lib/remote_syslog/glob_watch.rb
|
|
76
|
+
lib/remote_syslog/message_generator.rb
|
|
72
77
|
lib/remote_syslog/tls_endpoint.rb
|
|
73
78
|
lib/remote_syslog/udp_endpoint.rb
|
|
74
79
|
remote_syslog.gemspec
|
metadata
CHANGED
|
@@ -1,89 +1,118 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: remote_syslog
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease: true
|
|
5
|
+
segments:
|
|
6
|
+
- 1
|
|
7
|
+
- 6
|
|
8
|
+
- 6
|
|
9
|
+
- rc1
|
|
10
|
+
version: 1.6.6.rc1
|
|
6
11
|
platform: ruby
|
|
7
|
-
authors:
|
|
12
|
+
authors:
|
|
8
13
|
- Troy Davis
|
|
9
14
|
- Eric Lindvall
|
|
10
15
|
autorequire:
|
|
11
16
|
bindir: bin
|
|
12
17
|
cert_chain: []
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
|
|
19
|
+
date: 2012-08-13 00:00:00 -07:00
|
|
20
|
+
default_executable: remote_syslog
|
|
21
|
+
dependencies:
|
|
22
|
+
- !ruby/object:Gem::Dependency
|
|
23
|
+
name: servolux
|
|
24
|
+
prerelease: false
|
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
segments:
|
|
30
|
+
- 0
|
|
31
|
+
version: "0"
|
|
23
32
|
type: :runtime
|
|
33
|
+
version_requirements: *id001
|
|
34
|
+
- !ruby/object:Gem::Dependency
|
|
35
|
+
name: file-tail
|
|
24
36
|
prerelease: false
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
segments:
|
|
42
|
+
- 0
|
|
43
|
+
version: "0"
|
|
44
|
+
type: :runtime
|
|
45
|
+
version_requirements: *id002
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
27
47
|
name: eventmachine
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
requirements:
|
|
31
|
-
- -
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
48
|
+
prerelease: false
|
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
segments:
|
|
54
|
+
- 0
|
|
55
|
+
- 12
|
|
56
|
+
- 10
|
|
33
57
|
version: 0.12.10
|
|
34
58
|
- - <
|
|
35
|
-
- !ruby/object:Gem::Version
|
|
36
|
-
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
segments:
|
|
61
|
+
- 1
|
|
62
|
+
- 1
|
|
63
|
+
version: "1.1"
|
|
37
64
|
type: :runtime
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
65
|
+
version_requirements: *id003
|
|
66
|
+
- !ruby/object:Gem::Dependency
|
|
41
67
|
name: eventmachine-tail
|
|
42
|
-
requirement: &70356082563580 !ruby/object:Gem::Requirement
|
|
43
|
-
none: false
|
|
44
|
-
requirements:
|
|
45
|
-
- - ! '>='
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :runtime
|
|
49
68
|
prerelease: false
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
segments:
|
|
74
|
+
- 0
|
|
75
|
+
version: "0"
|
|
76
|
+
type: :runtime
|
|
77
|
+
version_requirements: *id004
|
|
78
|
+
- !ruby/object:Gem::Dependency
|
|
52
79
|
name: syslog_protocol
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
requirements:
|
|
80
|
+
prerelease: false
|
|
81
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
56
83
|
- - ~>
|
|
57
|
-
- !ruby/object:Gem::Version
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
segments:
|
|
86
|
+
- 0
|
|
87
|
+
- 9
|
|
88
|
+
- 2
|
|
58
89
|
version: 0.9.2
|
|
59
90
|
type: :runtime
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- !ruby/object:Gem::Dependency
|
|
91
|
+
version_requirements: *id005
|
|
92
|
+
- !ruby/object:Gem::Dependency
|
|
63
93
|
name: em-resolv-replace
|
|
64
|
-
requirement: &70356082562600 !ruby/object:Gem::Requirement
|
|
65
|
-
none: false
|
|
66
|
-
requirements:
|
|
67
|
-
- - ! '>='
|
|
68
|
-
- !ruby/object:Gem::Version
|
|
69
|
-
version: '0'
|
|
70
|
-
type: :runtime
|
|
71
94
|
prerelease: false
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
segments:
|
|
100
|
+
- 0
|
|
101
|
+
version: "0"
|
|
102
|
+
type: :runtime
|
|
103
|
+
version_requirements: *id006
|
|
104
|
+
description: Lightweight daemon to tail one or more log files and transmit UDP syslog messages to a remote syslog host (centralized log aggregation). Generates UDP packets itself instead of depending on a system syslog daemon, so it doesn't affect system-wide logging configuration.
|
|
105
|
+
email:
|
|
78
106
|
- troy@sevenscale.com
|
|
79
107
|
- eric@sevenscale.com
|
|
80
|
-
executables:
|
|
108
|
+
executables:
|
|
81
109
|
- remote_syslog
|
|
82
110
|
extensions: []
|
|
83
|
-
|
|
111
|
+
|
|
112
|
+
extra_rdoc_files:
|
|
84
113
|
- README.md
|
|
85
114
|
- LICENSE
|
|
86
|
-
files:
|
|
115
|
+
files:
|
|
87
116
|
- Gemfile
|
|
88
117
|
- LICENSE
|
|
89
118
|
- README.md
|
|
@@ -96,35 +125,46 @@ files:
|
|
|
96
125
|
- examples/remote_syslog.supervisor.conf
|
|
97
126
|
- examples/remote_syslog.upstart.conf
|
|
98
127
|
- lib/remote_syslog.rb
|
|
128
|
+
- lib/remote_syslog/agent.rb
|
|
99
129
|
- lib/remote_syslog/cli.rb
|
|
100
|
-
- lib/remote_syslog/
|
|
130
|
+
- lib/remote_syslog/eventmachine_reader.rb
|
|
131
|
+
- lib/remote_syslog/file_tail_reader.rb
|
|
132
|
+
- lib/remote_syslog/glob_watch.rb
|
|
133
|
+
- lib/remote_syslog/message_generator.rb
|
|
101
134
|
- lib/remote_syslog/tls_endpoint.rb
|
|
102
135
|
- lib/remote_syslog/udp_endpoint.rb
|
|
103
136
|
- remote_syslog.gemspec
|
|
137
|
+
has_rdoc: true
|
|
104
138
|
homepage: http://github.com/papertrail/remote_syslog
|
|
105
139
|
licenses: []
|
|
140
|
+
|
|
106
141
|
post_install_message:
|
|
107
|
-
rdoc_options:
|
|
142
|
+
rdoc_options:
|
|
108
143
|
- --charset=UTF-8
|
|
109
|
-
require_paths:
|
|
144
|
+
require_paths:
|
|
110
145
|
- lib
|
|
111
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
requirements:
|
|
120
|
-
- -
|
|
121
|
-
- !ruby/object:Gem::Version
|
|
122
|
-
|
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
147
|
+
requirements:
|
|
148
|
+
- - ">="
|
|
149
|
+
- !ruby/object:Gem::Version
|
|
150
|
+
segments:
|
|
151
|
+
- 0
|
|
152
|
+
version: "0"
|
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
|
+
requirements:
|
|
155
|
+
- - ">"
|
|
156
|
+
- !ruby/object:Gem::Version
|
|
157
|
+
segments:
|
|
158
|
+
- 1
|
|
159
|
+
- 3
|
|
160
|
+
- 1
|
|
161
|
+
version: 1.3.1
|
|
123
162
|
requirements: []
|
|
163
|
+
|
|
124
164
|
rubyforge_project: remote_syslog
|
|
125
|
-
rubygems_version: 1.
|
|
165
|
+
rubygems_version: 1.3.6
|
|
126
166
|
signing_key:
|
|
127
167
|
specification_version: 2
|
|
128
|
-
summary: Monitor plain text log file(s) for new entries and send to remote syslog
|
|
129
|
-
collector
|
|
168
|
+
summary: Monitor plain text log file(s) for new entries and send to remote syslog collector
|
|
130
169
|
test_files: []
|
|
170
|
+
|
data/lib/remote_syslog/reader.rb
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
require 'socket'
|
|
2
|
-
require 'eventmachine'
|
|
3
|
-
require 'eventmachine-tail'
|
|
4
|
-
require 'em-dns-resolver'
|
|
5
|
-
require 'syslog_protocol'
|
|
6
|
-
|
|
7
|
-
# Force eventmachine-tail not to change the encoding
|
|
8
|
-
# This will allow ruby 1.9 to deal with any file data
|
|
9
|
-
old_verbose, $VERBOSE = $VERBOSE, nil
|
|
10
|
-
EventMachine::FileTail::FORCE_ENCODING = false
|
|
11
|
-
$VERBOSE = old_verbose
|
|
12
|
-
|
|
13
|
-
module RemoteSyslog
|
|
14
|
-
class Reader < EventMachine::FileTail
|
|
15
|
-
COLORED_REGEXP = /\e\[(?:(?:[0-9]{1,3});){0,2}(?:[0-9]{1,3})m/
|
|
16
|
-
|
|
17
|
-
def initialize(path, destination_address, destination_port, options = {})
|
|
18
|
-
super(path, -1)
|
|
19
|
-
|
|
20
|
-
@parse_fields = options[:parse_fields]
|
|
21
|
-
@strip_color = options[:strip_color]
|
|
22
|
-
@exclude_pattern = options[:exclude_pattern]
|
|
23
|
-
@max_message_size = options[:max_message_size] || 1024
|
|
24
|
-
|
|
25
|
-
@socket = options[:socket] || UdpEndpoint.new(destination_address, destination_port)
|
|
26
|
-
|
|
27
|
-
@buffer = BufferedTokenizer.new
|
|
28
|
-
|
|
29
|
-
@packet = SyslogProtocol::Packet.new
|
|
30
|
-
|
|
31
|
-
if options[:hostname] && options[:hostname] != ''
|
|
32
|
-
local_hostname = options[:hostname]
|
|
33
|
-
else
|
|
34
|
-
local_hostname = (Socket.gethostname rescue `hostname`.chomp)[/^([^\.]+)/, 1]
|
|
35
|
-
|
|
36
|
-
if local_hostname.nil? || local_hostname.empty?
|
|
37
|
-
local_hostname = 'localhost'
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
@packet.hostname = local_hostname
|
|
42
|
-
@packet.facility = options[:facility] || 'user'
|
|
43
|
-
@packet.severity = options[:severity] || 'notice'
|
|
44
|
-
|
|
45
|
-
tag = options[:program] || File.basename(path) || File.basename($0)
|
|
46
|
-
|
|
47
|
-
# Remove characters that can't be in a tag
|
|
48
|
-
tag = tag.gsub(%r{[: \]\[\\]+}, '-')
|
|
49
|
-
|
|
50
|
-
# Make sure the tag isn't too long
|
|
51
|
-
if tag.length > 32
|
|
52
|
-
tag = tag[0..31]
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
@packet.tag = tag
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def receive_data(data)
|
|
59
|
-
@buffer.extract(data).each do |line|
|
|
60
|
-
transmit(line)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def transmit(message)
|
|
65
|
-
return if @exclude_pattern && message =~ @exclude_pattern
|
|
66
|
-
|
|
67
|
-
message = message.gsub(COLORED_REGEXP, '') if @strip_color
|
|
68
|
-
|
|
69
|
-
packet = @packet.dup
|
|
70
|
-
packet.content = message
|
|
71
|
-
|
|
72
|
-
if @parse_fields
|
|
73
|
-
if message =~ @parse_fields
|
|
74
|
-
packet.hostname = $2 if $2 && $2 != ''
|
|
75
|
-
packet.tag = $3 if $3 && $3 != ''
|
|
76
|
-
packet.content = $4 if $4 && $4 != ''
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
@socket.write(packet.assemble(@max_message_size))
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def on_exception(exception)
|
|
84
|
-
puts "Exception: #{exception.class}: #{exception.message}\n\t#{exception.backtrace.join("\n\t")}"
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|