remote_syslog 0.1.0
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/Gemfile +3 -0
- data/Gemfile.lock +14 -0
- data/LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +150 -0
- data/bin/remote_syslog +116 -0
- data/examples/log_files.yml.example +4 -0
- data/examples/remote_syslog.init.d +91 -0
- data/lib/remote_syslog/levels.rb +37 -0
- data/lib/remote_syslog/reader.rb +57 -0
- data/lib/remote_syslog.rb +6 -0
- data/remote_syslog.gemspec +78 -0
- metadata +120 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Seven Scale LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# remote_syslog Ruby daemon & sender
|
2
|
+
|
3
|
+
Lightweight Ruby daemon to tail one or more log files and transmit UDP syslog
|
4
|
+
messages to a remote syslog host (centralized log aggregation).
|
5
|
+
|
6
|
+
remote_syslog generates UDP packets itself instead of depending on a system
|
7
|
+
syslog daemon, so its configuration doesn't affect system-wide
|
8
|
+
logging - syslog is just the transport.
|
9
|
+
|
10
|
+
Uses:
|
11
|
+
|
12
|
+
* collecting logs from servers & daemons which don't natively support syslog
|
13
|
+
* when reconfiguring the system logger is less convenient than a
|
14
|
+
purpose-built daemon (e.g., automated app deployments)
|
15
|
+
* aggregating files not generated by daemons (e.g., package manager logs)
|
16
|
+
|
17
|
+
The library can also be used to generate one-off log messages from Ruby code.
|
18
|
+
|
19
|
+
Tested with the hosted log management service [Papertrail] and should work for
|
20
|
+
transmitting to any syslog server.
|
21
|
+
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Install the gem, which includes a binary called "remote_syslog":
|
26
|
+
|
27
|
+
gem install remote_syslog
|
28
|
+
|
29
|
+
Optionally, create a log_files.yml with the log file paths to read and the
|
30
|
+
host/port to log to (see examples/log_files.yml.example). These can also be
|
31
|
+
specified as arguments to the remote_syslog daemon. More below.
|
32
|
+
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
$ remote_syslog -h
|
37
|
+
Usage: remote_syslog [options] <path to add'l log 1> .. <path to add'l log n>
|
38
|
+
|
39
|
+
Example: remote_syslog -c configs/logs.yml -p 12345 /var/log/mysqld.log
|
40
|
+
|
41
|
+
Options:
|
42
|
+
-c, --configfile PATH Path to config (/etc/log_files.yml)
|
43
|
+
-d, --dest-host HOSTNAME Destination syslog hostname or IP (logs.papertrailapp.com)
|
44
|
+
-D, --no-detach Don't daemonize and detach from the terminal
|
45
|
+
-f, --facility FACILITY Facility (user)
|
46
|
+
-p, --dest-port PORT Destination syslog port (514)
|
47
|
+
-P, --pid-dir DIRECTORY Directory to write .pid file in (/var/run/)
|
48
|
+
-s, --severity SEVERITY Severity (notice)
|
49
|
+
-h, --help Show this message
|
50
|
+
|
51
|
+
|
52
|
+
## Example
|
53
|
+
|
54
|
+
Daemonize, collecting from files mentioned in ./config/logs.yml as well as
|
55
|
+
/var/log/mysqld.log:
|
56
|
+
remote_syslog -c configs/logs.yml -p 12345 /var/log/mysqld.log
|
57
|
+
|
58
|
+
Stay attached to the terminal, look for and use /etc/log_files.yml if it exists,
|
59
|
+
write PID to /tmp/remote_syslog.pid, and send with facility local0:
|
60
|
+
remote_syslog -d a.server.com -f local0 -P /tmp /var/log/mysqld.log
|
61
|
+
|
62
|
+
remote_syslog will daemonize by default. A sample init file is in the gem as
|
63
|
+
remote_syslog.init.d. You may be able to:
|
64
|
+
cp examples/remote_syslog.init.d /etc/init.d/remote_syslog
|
65
|
+
|
66
|
+
|
67
|
+
## Configuration
|
68
|
+
|
69
|
+
The gem comes with a sample config. Optionally:
|
70
|
+
|
71
|
+
cp examples/log_files.yml.example /etc/log_files.yml
|
72
|
+
|
73
|
+
log_files.yml has filenames to log from (as an array) and hostname and port
|
74
|
+
to log to (as a hash). Only 1 destination server is supported; the command-line
|
75
|
+
argument wins. Filenames given on the command line are additive to those
|
76
|
+
in the config file.
|
77
|
+
|
78
|
+
|
79
|
+
## Contribute
|
80
|
+
|
81
|
+
Bug report:
|
82
|
+
|
83
|
+
1. See whether the issue has already been reported:
|
84
|
+
http://github.com/papertrail/remote_syslog/issues/
|
85
|
+
2. If you don't find one, create an issue with a repro case.
|
86
|
+
|
87
|
+
Enhancement or fix:
|
88
|
+
|
89
|
+
1. Fork the project:
|
90
|
+
http://github.com/papertrail/remote_syslog
|
91
|
+
2. Make your changes with tests.
|
92
|
+
3. Commit the changes without changing the Rakefile or other files unrelated
|
93
|
+
to your enhancement.
|
94
|
+
4. Send a pull request.
|
95
|
+
|
96
|
+
[Papertrail]: http://papertrailapp.com/
|
data/Rakefile
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
#############################################################################
|
6
|
+
#
|
7
|
+
# Helper functions
|
8
|
+
#
|
9
|
+
#############################################################################
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def date
|
21
|
+
Date.today.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def rubyforge_project
|
25
|
+
name
|
26
|
+
end
|
27
|
+
|
28
|
+
def gemspec_file
|
29
|
+
"#{name}.gemspec"
|
30
|
+
end
|
31
|
+
|
32
|
+
def gem_file
|
33
|
+
"#{name}-#{version}.gem"
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_header(head, header_name)
|
37
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
+
end
|
39
|
+
|
40
|
+
#############################################################################
|
41
|
+
#
|
42
|
+
# Standard tasks
|
43
|
+
#
|
44
|
+
#############################################################################
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
require 'rake/testtask'
|
49
|
+
Rake::TestTask.new(:test) do |test|
|
50
|
+
test.libs << 'lib' << 'test'
|
51
|
+
test.pattern = 'test/**/test_*.rb'
|
52
|
+
test.verbose = true
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Generate RCov test coverage and open in your browser"
|
56
|
+
task :coverage do
|
57
|
+
require 'rcov'
|
58
|
+
sh "rm -fr coverage"
|
59
|
+
sh "rcov test/test_*.rb"
|
60
|
+
sh "open coverage/index.html"
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'rake/rdoctask'
|
64
|
+
Rake::RDocTask.new do |rdoc|
|
65
|
+
rdoc.rdoc_dir = 'rdoc'
|
66
|
+
rdoc.title = "#{name} #{version}"
|
67
|
+
rdoc.rdoc_files.include('README*')
|
68
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Open an irb session preloaded with this library"
|
72
|
+
task :console do
|
73
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
74
|
+
end
|
75
|
+
|
76
|
+
#############################################################################
|
77
|
+
#
|
78
|
+
# Custom tasks (add your own tasks here)
|
79
|
+
#
|
80
|
+
#############################################################################
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
#############################################################################
|
85
|
+
#
|
86
|
+
# Packaging tasks
|
87
|
+
#
|
88
|
+
#############################################################################
|
89
|
+
|
90
|
+
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
91
|
+
task :release => :build do
|
92
|
+
unless `git branch` =~ /^\* master$/
|
93
|
+
puts "You must be on the master branch to release!"
|
94
|
+
exit!
|
95
|
+
end
|
96
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
97
|
+
sh "git tag v#{version}"
|
98
|
+
sh "git push origin master"
|
99
|
+
sh "git push origin v#{version}"
|
100
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
101
|
+
end
|
102
|
+
|
103
|
+
desc "Build #{gem_file} into the pkg directory"
|
104
|
+
task :build => :gemspec do
|
105
|
+
sh "mkdir -p pkg"
|
106
|
+
sh "gem build #{gemspec_file}"
|
107
|
+
sh "mv #{gem_file} pkg"
|
108
|
+
end
|
109
|
+
|
110
|
+
desc "Generate #{gemspec_file}"
|
111
|
+
task :gemspec => :validate do
|
112
|
+
# read spec file and split out manifest section
|
113
|
+
spec = File.read(gemspec_file)
|
114
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
115
|
+
|
116
|
+
# replace name version and date
|
117
|
+
replace_header(head, :name)
|
118
|
+
replace_header(head, :version)
|
119
|
+
replace_header(head, :date)
|
120
|
+
#comment this out if your rubyforge_project has a different name
|
121
|
+
replace_header(head, :rubyforge_project)
|
122
|
+
|
123
|
+
# determine file list from git ls-files
|
124
|
+
files = `git ls-files`.
|
125
|
+
split("\n").
|
126
|
+
sort.
|
127
|
+
reject { |file| file =~ /^\./ }.
|
128
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
129
|
+
map { |file| " #{file}" }.
|
130
|
+
join("\n")
|
131
|
+
|
132
|
+
# piece file back together and write
|
133
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
134
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
135
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
136
|
+
puts "Updated #{gemspec_file}"
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "Validate #{gemspec_file}"
|
140
|
+
task :validate do
|
141
|
+
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
142
|
+
unless libfiles.empty?
|
143
|
+
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
144
|
+
exit!
|
145
|
+
end
|
146
|
+
unless Dir['VERSION*'].empty?
|
147
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
148
|
+
exit!
|
149
|
+
end
|
150
|
+
end
|
data/bin/remote_syslog
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'yaml'
|
8
|
+
require 'pathname'
|
9
|
+
require 'socket'
|
10
|
+
|
11
|
+
require 'daemons'
|
12
|
+
require 'eventmachine'
|
13
|
+
require 'eventmachine-tail'
|
14
|
+
|
15
|
+
require 'remote_syslog'
|
16
|
+
|
17
|
+
def remote_syslog_daemon(args)
|
18
|
+
options = {
|
19
|
+
:configfile => '/etc/log_files.yml',
|
20
|
+
:dest_host => 'logs.papertrailapp.com',
|
21
|
+
:dest_port => 514
|
22
|
+
}
|
23
|
+
daemonize_options = {
|
24
|
+
:app_name => File.basename($0) || "remote_syslog",
|
25
|
+
:ARGV => ['start'],
|
26
|
+
:dir_mode => :system,
|
27
|
+
:multiple => false,
|
28
|
+
:ontop => false,
|
29
|
+
:mode => :exec,
|
30
|
+
:backtrace => false,
|
31
|
+
:monitor => false
|
32
|
+
}
|
33
|
+
|
34
|
+
op = OptionParser.new do |opts|
|
35
|
+
opts.banner = "Usage: remote_syslog [options] <path to add'l log 1> .. <path to add'l log n>"
|
36
|
+
opts.separator ''
|
37
|
+
opts.separator "Example: remote_syslog -c configs/logs.yml -p 12345 /var/log/mysqld.log"
|
38
|
+
opts.separator ''
|
39
|
+
opts.separator "Options:"
|
40
|
+
|
41
|
+
opts.on("-c", "--configfile PATH", "Path to config (/etc/log_files.yml)") do |v|
|
42
|
+
options[:configfile] = File.expand_path(v)
|
43
|
+
end
|
44
|
+
opts.on("-d", "--dest-host HOSTNAME", "Destination syslog hostname or IP (logs.papertrailapp.com)") do |v|
|
45
|
+
options[:dest_host] = v
|
46
|
+
end
|
47
|
+
opts.on("-D", "--no-detach", "Don't daemonize and detach from the terminal") do
|
48
|
+
daemonize_options[:ontop] = true
|
49
|
+
daemonize_options[:ARGV] = ['run']
|
50
|
+
# write PID file in . because /var/run is sometimes only writable by root
|
51
|
+
daemonize_options[:dir_mode] = :script
|
52
|
+
daemonize_options[:dir] = '.'
|
53
|
+
end
|
54
|
+
opts.on("-f", "--facility FACILITY", "Facility (user)") do |v|
|
55
|
+
options[:facility] = v.upcase
|
56
|
+
end
|
57
|
+
opts.on("-p", "--dest-port PORT", "Destination syslog port (514)") do |v|
|
58
|
+
options[:dest_port] = v
|
59
|
+
end
|
60
|
+
opts.on("-P", "--pid-dir DIRECTORY", "Directory to write .pid file in (/var/run/)") do |v|
|
61
|
+
daemonize_options[:dir_mode] = :script
|
62
|
+
daemonize_options[:dir] = v
|
63
|
+
end
|
64
|
+
opts.on("-s", "--severity SEVERITY", "Severity (notice)") do |v|
|
65
|
+
options[:severity] = v.upcase
|
66
|
+
end
|
67
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
68
|
+
puts opts
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
op.parse!
|
74
|
+
|
75
|
+
files = ARGV
|
76
|
+
if File.exist?(options[:configfile])
|
77
|
+
config = open(options[:configfile]) do |f|
|
78
|
+
YAML.load(f)
|
79
|
+
end
|
80
|
+
|
81
|
+
files += config['files']
|
82
|
+
if config['destination']
|
83
|
+
options[:dest_host] = config['destination']['host'] if config['destination']['host']
|
84
|
+
options[:dest_port] = config['destination']['port'] if config['destination']['port']
|
85
|
+
end
|
86
|
+
elsif files.empty?
|
87
|
+
puts "No filenames provided and #{options[:configfile]} not found."
|
88
|
+
puts ''
|
89
|
+
puts op
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
|
93
|
+
# handle relative paths before Daemonize changes the wd to /
|
94
|
+
files.map! { |f| File.expand_path(f) }
|
95
|
+
|
96
|
+
Daemons.run_proc(daemonize_options[:app_name], daemonize_options) do
|
97
|
+
EventMachine.run do
|
98
|
+
socket = EventMachine.open_datagram_socket('0.0.0.0', 0)
|
99
|
+
|
100
|
+
files.each do |path|
|
101
|
+
begin
|
102
|
+
EventMachine::file_tail(File.expand_path(path), RemoteSyslog::Reader,
|
103
|
+
options[:dest_host], options[:dest_port],
|
104
|
+
{ :socket => socket, :facility => options[:facility],
|
105
|
+
:severity => options[:severity] })
|
106
|
+
|
107
|
+
rescue Errno::ENOENT => e
|
108
|
+
puts "#{File.expand_path(path)} not found, continuing. (#{e.message})"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
remote_syslog_daemon(ARGV)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
prog="remote_syslog"
|
4
|
+
config="/etc/log_files.yml"
|
5
|
+
pid_dir="/var/run"
|
6
|
+
|
7
|
+
EXTRAOPTIONS=""
|
8
|
+
|
9
|
+
pid_file="$pid_dir/$prog.pid"
|
10
|
+
|
11
|
+
PATH=/sbin:/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
|
12
|
+
|
13
|
+
RETVAL=0
|
14
|
+
|
15
|
+
is_running(){
|
16
|
+
[ -e $pid_file ]
|
17
|
+
}
|
18
|
+
|
19
|
+
start(){
|
20
|
+
echo -n $"Starting $prog: "
|
21
|
+
|
22
|
+
unset HOME MAIL USER USERNAME
|
23
|
+
$prog -c $config -P $pid_dir "$EXTRAOPTIONS"
|
24
|
+
RETVAL=$?
|
25
|
+
echo
|
26
|
+
return $RETVAL
|
27
|
+
}
|
28
|
+
|
29
|
+
stop(){
|
30
|
+
echo -n $"Stopping $prog: "
|
31
|
+
if (is_running); then
|
32
|
+
kill `cat $pid_file`
|
33
|
+
RETVAL=$?
|
34
|
+
echo
|
35
|
+
return $RETVAL
|
36
|
+
else
|
37
|
+
echo "$pid_file not found"
|
38
|
+
fi
|
39
|
+
}
|
40
|
+
|
41
|
+
status(){
|
42
|
+
echo -n $"Checking for $pid_file: "
|
43
|
+
|
44
|
+
if (is_running); then
|
45
|
+
echo "found"
|
46
|
+
else
|
47
|
+
echo "not found"
|
48
|
+
fi
|
49
|
+
}
|
50
|
+
|
51
|
+
reload(){
|
52
|
+
restart
|
53
|
+
}
|
54
|
+
|
55
|
+
restart(){
|
56
|
+
stop
|
57
|
+
start
|
58
|
+
}
|
59
|
+
|
60
|
+
condrestart(){
|
61
|
+
is_running && restart
|
62
|
+
return 0
|
63
|
+
}
|
64
|
+
|
65
|
+
|
66
|
+
# See how we were called.
|
67
|
+
case "$1" in
|
68
|
+
start)
|
69
|
+
start
|
70
|
+
;;
|
71
|
+
stop)
|
72
|
+
stop
|
73
|
+
;;
|
74
|
+
status)
|
75
|
+
status
|
76
|
+
;;
|
77
|
+
restart)
|
78
|
+
restart
|
79
|
+
;;
|
80
|
+
reload)
|
81
|
+
reload
|
82
|
+
;;
|
83
|
+
condrestart)
|
84
|
+
condrestart
|
85
|
+
;;
|
86
|
+
*)
|
87
|
+
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
|
88
|
+
RETVAL=1
|
89
|
+
esac
|
90
|
+
|
91
|
+
exit $RETVAL
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RemoteSyslog
|
2
|
+
class Levels
|
3
|
+
SEVERITIES = {
|
4
|
+
:emerg => 0,
|
5
|
+
:alert => 1,
|
6
|
+
:crit => 2,
|
7
|
+
:err => 3,
|
8
|
+
:warning => 4,
|
9
|
+
:notice => 5,
|
10
|
+
:info => 6,
|
11
|
+
:debug => 7
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
FACILITIES = {
|
15
|
+
:kern => (0<<3),
|
16
|
+
:user => (1<<3),
|
17
|
+
:mail => (2<<3),
|
18
|
+
:daemon => (3<<3),
|
19
|
+
:auth => (4<<3),
|
20
|
+
:syslog => (5<<3),
|
21
|
+
:lpr => (6<<3),
|
22
|
+
:news => (7<<3),
|
23
|
+
:uucp => (8<<3),
|
24
|
+
:cron => (9<<3),
|
25
|
+
:authpriv => (10<<3),
|
26
|
+
:ftp => (11<<3),
|
27
|
+
:local0 => (16<<3),
|
28
|
+
:local1 => (17<<3),
|
29
|
+
:local2 => (18<<3),
|
30
|
+
:local3 => (19<<3),
|
31
|
+
:local4 => (20<<3),
|
32
|
+
:local5 => (21<<3),
|
33
|
+
:local6 => (22<<3),
|
34
|
+
:local7 => (23<<3)
|
35
|
+
}.freeze
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module RemoteSyslog
|
2
|
+
class Reader < EventMachine::FileTail
|
3
|
+
def initialize(path, dest_addr, dest_port, options = {})
|
4
|
+
@dest_addr = dest_addr
|
5
|
+
@dest_port = dest_port.to_i
|
6
|
+
|
7
|
+
@socket = options[:socket] || EventMachine.open_datagram_socket('0.0.0.0', 0)
|
8
|
+
@program = options[:program] || File.basename(path) || 'remote_syslog'
|
9
|
+
@hostname = options[:hostname] || `hostname`.strip
|
10
|
+
@hostname = 'localhost' unless @hostname && @hostname != ''
|
11
|
+
|
12
|
+
if options[:severity]
|
13
|
+
@severity = severity_value(options[:severity]) || raise(ArgumentError, "Invalid severity: #{options[:severity]} (valid: #{severities.keys.join(', ')})")
|
14
|
+
else
|
15
|
+
@severity = severity_value(:notice)
|
16
|
+
end
|
17
|
+
|
18
|
+
if options[:facility]
|
19
|
+
@facility = facility_value(options[:facility]) || raise(ArgumentError, "Invalid facility: #{options[:facility]} (valid: #{facilities.keys.join(', ')}")
|
20
|
+
else
|
21
|
+
@facility = facility_value(:user)
|
22
|
+
end
|
23
|
+
|
24
|
+
super(path, -1)
|
25
|
+
@buffer = BufferedTokenizer.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def receive_data(data)
|
29
|
+
@buffer.extract(data).each do |line|
|
30
|
+
transmit(line)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def transmit(message)
|
35
|
+
time ||= Time.now
|
36
|
+
day = time.strftime('%b %d').sub(/0(\d)/, ' \\1')
|
37
|
+
|
38
|
+
@socket.send_datagram("<#{(@facility) + @severity}>#{day} #{time.strftime('%T')} #{@hostname} #{@program}: #{message}", @dest_addr, @dest_port)
|
39
|
+
end
|
40
|
+
|
41
|
+
def facility_value(f)
|
42
|
+
f.is_a?(Integer) ? f*8 : facilities[f.to_sym]
|
43
|
+
end
|
44
|
+
|
45
|
+
def severity_value(s)
|
46
|
+
s.is_a?(Integer) ? s : severities[s.to_sym]
|
47
|
+
end
|
48
|
+
|
49
|
+
def facilities
|
50
|
+
Levels::FACILITIES
|
51
|
+
end
|
52
|
+
|
53
|
+
def severities
|
54
|
+
Levels::SEVERITIES
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# https://github.com/mojombo/rakegem
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
4
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
5
|
+
s.rubygems_version = '1.3.5'
|
6
|
+
|
7
|
+
## Leave these as is they will be modified for you by the rake gemspec task.
|
8
|
+
## If your rubyforge_project name is different, then edit it and comment out
|
9
|
+
## the sub! line in the Rakefile
|
10
|
+
s.name = 'remote_syslog'
|
11
|
+
s.version = '0.1.0'
|
12
|
+
s.date = '2010-12-17'
|
13
|
+
s.rubyforge_project = 'remote_syslog'
|
14
|
+
|
15
|
+
## Make sure your summary is short. The description may be as long
|
16
|
+
## as you like.
|
17
|
+
s.summary = 'Monitor plain text log file(s) for new entries and send to remote syslog collector'
|
18
|
+
s.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."
|
19
|
+
|
20
|
+
## List the primary authors. If there are a bunch of authors, it's probably
|
21
|
+
## better to set the email to an email list or something. If you don't have
|
22
|
+
## a custom homepage, consider using your GitHub URL or the like.
|
23
|
+
s.authors = ['Papertrail']
|
24
|
+
s.email = 'troy@sevenscale.com'
|
25
|
+
s.homepage = 'http://github.com/papertrail/remote_syslog'
|
26
|
+
|
27
|
+
## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
|
28
|
+
## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
|
29
|
+
s.require_paths = %w[lib]
|
30
|
+
|
31
|
+
## This sections is only necessary if you have C extensions.
|
32
|
+
#s.require_paths << 'ext'
|
33
|
+
#s.extensions = %w[ext/extconf.rb]
|
34
|
+
|
35
|
+
## If your gem includes any executables, list them here.
|
36
|
+
s.executables = ['remote_syslog']
|
37
|
+
s.default_executable = 'remote_syslog'
|
38
|
+
|
39
|
+
## Specify any RDoc options here. You'll want to add your README and
|
40
|
+
## LICENSE files to the extra_rdoc_files list.
|
41
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
42
|
+
s.extra_rdoc_files = %w[README.md LICENSE]
|
43
|
+
|
44
|
+
## List your runtime dependencies here. Runtime dependencies are those
|
45
|
+
## that are needed for an end user to actually USE your code.
|
46
|
+
#s.add_dependency('DEPNAME', [">= 1.1.0", "< 2.0.0"])
|
47
|
+
s.add_dependency 'daemons'
|
48
|
+
s.add_dependency 'eventmachine'
|
49
|
+
s.add_dependency 'eventmachine-tail'
|
50
|
+
|
51
|
+
## List your development dependencies here. Development dependencies are
|
52
|
+
## those that are only needed during development
|
53
|
+
#s.add_development_dependency('DEVDEPNAME', [">= 1.1.0", "< 2.0.0"])
|
54
|
+
|
55
|
+
## Leave this section as-is. It will be automatically generated from the
|
56
|
+
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
57
|
+
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
58
|
+
# = MANIFEST =
|
59
|
+
s.files = %w[
|
60
|
+
Gemfile
|
61
|
+
Gemfile.lock
|
62
|
+
LICENSE
|
63
|
+
README.md
|
64
|
+
Rakefile
|
65
|
+
bin/remote_syslog
|
66
|
+
examples/log_files.yml.example
|
67
|
+
examples/remote_syslog.init.d
|
68
|
+
lib/remote_syslog.rb
|
69
|
+
lib/remote_syslog/levels.rb
|
70
|
+
lib/remote_syslog/reader.rb
|
71
|
+
remote_syslog.gemspec
|
72
|
+
]
|
73
|
+
# = MANIFEST =
|
74
|
+
|
75
|
+
## Test files will be grabbed from the file list. Make sure the path glob
|
76
|
+
## matches what you actually use.
|
77
|
+
s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: remote_syslog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Papertrail
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-17 00:00:00 -08:00
|
19
|
+
default_executable: remote_syslog
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: daemons
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: eventmachine
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: eventmachine-tail
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
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.
|
64
|
+
email: troy@sevenscale.com
|
65
|
+
executables:
|
66
|
+
- remote_syslog
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files:
|
70
|
+
- README.md
|
71
|
+
- LICENSE
|
72
|
+
files:
|
73
|
+
- Gemfile
|
74
|
+
- Gemfile.lock
|
75
|
+
- LICENSE
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- bin/remote_syslog
|
79
|
+
- examples/log_files.yml.example
|
80
|
+
- examples/remote_syslog.init.d
|
81
|
+
- lib/remote_syslog.rb
|
82
|
+
- lib/remote_syslog/levels.rb
|
83
|
+
- lib/remote_syslog/reader.rb
|
84
|
+
- remote_syslog.gemspec
|
85
|
+
has_rdoc: true
|
86
|
+
homepage: http://github.com/papertrail/remote_syslog
|
87
|
+
licenses: []
|
88
|
+
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options:
|
91
|
+
- --charset=UTF-8
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
requirements: []
|
113
|
+
|
114
|
+
rubyforge_project: remote_syslog
|
115
|
+
rubygems_version: 1.3.7
|
116
|
+
signing_key:
|
117
|
+
specification_version: 2
|
118
|
+
summary: Monitor plain text log file(s) for new entries and send to remote syslog collector
|
119
|
+
test_files: []
|
120
|
+
|