syslog_protocol 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/README.md +84 -0
- data/Rakefile +150 -0
- data/lib/syslog_protocol.rb +8 -0
- data/lib/syslog_protocol/common.rb +79 -0
- data/lib/syslog_protocol/logger.rb +21 -0
- data/lib/syslog_protocol/packet.rb +122 -0
- data/lib/syslog_protocol/parser.rb +57 -0
- data/syslog_protocol.gemspec +81 -0
- data/test/helper.rb +13 -0
- data/test/test_logger.rb +28 -0
- data/test/test_packet.rb +92 -0
- data/test/test_parser.rb +51 -0
- metadata +99 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Syslog protocol
|
2
|
+
|
3
|
+
roughly conforms to the murky shade of grey known as http://www.faqs.org/rfcs/rfc3164.html
|
4
|
+
|
5
|
+
## Examples
|
6
|
+
|
7
|
+
### Manipulate packets manually
|
8
|
+
|
9
|
+
require 'syslog_protocol'
|
10
|
+
|
11
|
+
p = SyslogProtocol::Packet.new
|
12
|
+
p.hostname = "space_station"
|
13
|
+
p.facility = "kern"
|
14
|
+
p.severity = "warn"
|
15
|
+
p.tag = "test"
|
16
|
+
p.content = "flight control broken"
|
17
|
+
p.to_s
|
18
|
+
# => "<4>Aug 1 14:01:17 space_station flight control broken"
|
19
|
+
p.pri
|
20
|
+
# => 4
|
21
|
+
p.facility
|
22
|
+
# => 0
|
23
|
+
p.facility_name
|
24
|
+
# => "kern"
|
25
|
+
p.severity_name
|
26
|
+
# => "warn"
|
27
|
+
p.warn?
|
28
|
+
# => true
|
29
|
+
p.info?
|
30
|
+
# => false
|
31
|
+
|
32
|
+
|
33
|
+
### Use a Logger to generate packets
|
34
|
+
|
35
|
+
require 'syslog_protocol'
|
36
|
+
|
37
|
+
logger = SyslogProtocol::Logger.new("space_station", "uucp")
|
38
|
+
logger.debug("looking for uucp on board the space station")
|
39
|
+
# => "<67>Aug 1 14:02:29 space_station looking for uucp on board the space station"
|
40
|
+
logger.emerg("omg we cant find uucp on the space station")
|
41
|
+
# => "<64>Aug 1 14:03:56 space_station omg we cant find uucp on the space station"
|
42
|
+
|
43
|
+
|
44
|
+
### Parse packets
|
45
|
+
|
46
|
+
require 'syslog_protocol'
|
47
|
+
|
48
|
+
p = SyslogProtocol.parse("<34>Oct 11 22:14:15 space_station space is really getting to me")
|
49
|
+
p.facility
|
50
|
+
# => 4
|
51
|
+
p.severity_name
|
52
|
+
# => "crit"
|
53
|
+
p.time
|
54
|
+
# => Sun Oct 11 22:14:15 -0700 2009
|
55
|
+
p.content
|
56
|
+
# => "space is really getting to me"
|
57
|
+
|
58
|
+
|
59
|
+
### It yells at you for trying to abuse the protocol
|
60
|
+
|
61
|
+
p = SyslogProtocol::Packet.new
|
62
|
+
p.facility = 34534534
|
63
|
+
# => ArgumentError: Facility must be within 0-23
|
64
|
+
p.hostname = "my host"
|
65
|
+
# => ArgumentError: Hostname may not contain spaces
|
66
|
+
p.hostname = "h\000stname"
|
67
|
+
# => ArgumentError: Hostname may only contain ASCII characters 33-126
|
68
|
+
# ...etc.
|
69
|
+
# It will also unintelligently truncate messages > 1024 bytes so beware.
|
70
|
+
|
71
|
+
|
72
|
+
## Caveats
|
73
|
+
|
74
|
+
Syslog is a terrible and loosely defined protocol. Many devices and programs do not
|
75
|
+
conform to it and so their packets may not be parsed correctly by this interpretation,
|
76
|
+
nor may the packets generated by this necessarily be recognized by other devices or programs ;)
|
77
|
+
|
78
|
+
This is probably wrong and buggy, and i know the code is ugly, thanks.
|
79
|
+
|
80
|
+
Good luck.
|
81
|
+
|
82
|
+
## TODO
|
83
|
+
|
84
|
+
* Update to more closely map to the ruby `syslog` API where possible
|
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
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module SyslogProtocol
|
2
|
+
# These hashes stolen from Syslog.pm
|
3
|
+
|
4
|
+
FACILITIES = {
|
5
|
+
'kern' => 0,
|
6
|
+
'user' => 1,
|
7
|
+
'mail' => 2,
|
8
|
+
'daemon' => 3,
|
9
|
+
'auth' => 4,
|
10
|
+
'syslog' => 5,
|
11
|
+
'lpr' => 6,
|
12
|
+
'news' => 7,
|
13
|
+
'uucp' => 8,
|
14
|
+
'cron' => 9,
|
15
|
+
'authpriv' => 10,
|
16
|
+
'ftp' => 11,
|
17
|
+
'ntp' => 12,
|
18
|
+
'audit' => 13,
|
19
|
+
'alert' => 14,
|
20
|
+
'at' => 15,
|
21
|
+
'local0' => 16,
|
22
|
+
'local1' => 17,
|
23
|
+
'local2' => 18,
|
24
|
+
'local3' => 19,
|
25
|
+
'local4' => 20,
|
26
|
+
'local5' => 21,
|
27
|
+
'local6' => 22,
|
28
|
+
'local7' => 23
|
29
|
+
}
|
30
|
+
|
31
|
+
FACILITY_INDEX = {
|
32
|
+
0 => 'kern',
|
33
|
+
1 => 'user',
|
34
|
+
2 => 'mail',
|
35
|
+
3 => 'daemon',
|
36
|
+
4 => 'auth',
|
37
|
+
5 => 'syslog',
|
38
|
+
6 => 'lpr',
|
39
|
+
7 => 'news',
|
40
|
+
8 => 'uucp',
|
41
|
+
9 => 'cron',
|
42
|
+
10 => 'authpriv',
|
43
|
+
11 => 'ftp',
|
44
|
+
12 => 'ntp',
|
45
|
+
13 => 'audit',
|
46
|
+
14 => 'alert',
|
47
|
+
15 => 'at',
|
48
|
+
16 => 'local0',
|
49
|
+
17 => 'local1',
|
50
|
+
18 => 'local2',
|
51
|
+
19 => 'local3',
|
52
|
+
20 => 'local4',
|
53
|
+
21 => 'local5',
|
54
|
+
22 => 'local6',
|
55
|
+
23 => 'local7'
|
56
|
+
}
|
57
|
+
|
58
|
+
SEVERITIES = {
|
59
|
+
'emerg' => 0,
|
60
|
+
'alert' => 1,
|
61
|
+
'crit' => 2,
|
62
|
+
'err' => 3,
|
63
|
+
'warn' => 4,
|
64
|
+
'notice' => 5,
|
65
|
+
'info' => 6,
|
66
|
+
'debug' => 7
|
67
|
+
}
|
68
|
+
|
69
|
+
SEVERITY_INDEX = {
|
70
|
+
0 => 'emerg',
|
71
|
+
1 => 'alert',
|
72
|
+
2 => 'crit',
|
73
|
+
3 => 'err',
|
74
|
+
4 => 'warn',
|
75
|
+
5 => 'notice',
|
76
|
+
6 => 'info',
|
77
|
+
7 => 'debug'
|
78
|
+
}
|
79
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SyslogProtocol
|
2
|
+
class Logger
|
3
|
+
def initialize(hostname, tag, facility)
|
4
|
+
@packet = Packet.new
|
5
|
+
@packet.hostname = hostname
|
6
|
+
@packet.tag = tag
|
7
|
+
@packet.facility = facility
|
8
|
+
end
|
9
|
+
|
10
|
+
SEVERITIES.each do |k,v|
|
11
|
+
define_method(k) do |content|
|
12
|
+
raise ArgumentError.new("Message may not be omitted") unless content and content.length > 0
|
13
|
+
|
14
|
+
p = @packet.dup
|
15
|
+
p.severity = k
|
16
|
+
p.content = content
|
17
|
+
p.assemble
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module SyslogProtocol
|
2
|
+
class Packet
|
3
|
+
attr_reader :facility, :severity, :hostname, :tag
|
4
|
+
attr_accessor :time, :content
|
5
|
+
|
6
|
+
def to_s
|
7
|
+
assemble
|
8
|
+
end
|
9
|
+
|
10
|
+
def assemble
|
11
|
+
unless @hostname and @facility and @severity and @tag
|
12
|
+
raise "Could not assemble packet without hostname, tag, facility, and severity"
|
13
|
+
end
|
14
|
+
data = "<#{pri}>#{generate_timestamp} #{@hostname} #{@tag}: #{@content}"
|
15
|
+
while data.bytesize > 1024
|
16
|
+
data = data[0..(data.length-2)]
|
17
|
+
end
|
18
|
+
data
|
19
|
+
end
|
20
|
+
|
21
|
+
def facility=(f)
|
22
|
+
if f.is_a? Integer
|
23
|
+
if (0..23).include?(f)
|
24
|
+
@facility = f
|
25
|
+
else
|
26
|
+
raise ArgumentError.new "Facility must be within 0-23"
|
27
|
+
end
|
28
|
+
elsif f.is_a? String
|
29
|
+
if facility = FACILITIES[f]
|
30
|
+
@facility = facility
|
31
|
+
else
|
32
|
+
raise ArgumentError.new "'#{f}' is not a designated facility"
|
33
|
+
end
|
34
|
+
else
|
35
|
+
raise ArgumentError.new "Facility must be a designated number or string"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def tag=(t)
|
40
|
+
unless t && t.is_a?(String) && t.length > 0
|
41
|
+
raise ArgumentError, "Tag must not be omitted"
|
42
|
+
end
|
43
|
+
if t.length > 32
|
44
|
+
raise ArgumentError, "Tag must not be longer than 32 characters"
|
45
|
+
end
|
46
|
+
if t =~ /\s/
|
47
|
+
raise ArgumentError, "Tag may not contain spaces"
|
48
|
+
end
|
49
|
+
if t =~ /[^\x21-\x7E]/
|
50
|
+
raise ArgumentError, "Tag may only contain ASCII characters 33-126"
|
51
|
+
end
|
52
|
+
|
53
|
+
@tag = t
|
54
|
+
end
|
55
|
+
|
56
|
+
def severity=(s)
|
57
|
+
if s.is_a? Integer
|
58
|
+
if (0..7).include?(s)
|
59
|
+
@severity = s
|
60
|
+
else
|
61
|
+
raise ArgumentError.new "Severity must be within 0-7"
|
62
|
+
end
|
63
|
+
elsif s.is_a? String
|
64
|
+
if severity = SEVERITIES[s]
|
65
|
+
@severity = severity
|
66
|
+
else
|
67
|
+
raise ArgumentError.new "'#{s}' is not a designated severity"
|
68
|
+
end
|
69
|
+
else
|
70
|
+
raise ArgumentError.new "Severity must be a designated number or string"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def hostname=(h)
|
75
|
+
unless h and h.is_a? String and h.length > 0
|
76
|
+
raise ArgumentError.new("Hostname may not be omitted")
|
77
|
+
end
|
78
|
+
if h =~ /\s/
|
79
|
+
raise ArgumentError.new("Hostname may not contain spaces")
|
80
|
+
end
|
81
|
+
if h =~ /[^\x21-\x7E]/
|
82
|
+
raise ArgumentError.new("Hostname may only contain ASCII characters 33-126")
|
83
|
+
end
|
84
|
+
@hostname = h
|
85
|
+
end
|
86
|
+
|
87
|
+
def facility_name
|
88
|
+
FACILITY_INDEX[@facility]
|
89
|
+
end
|
90
|
+
|
91
|
+
def severity_name
|
92
|
+
SEVERITY_INDEX[@severity]
|
93
|
+
end
|
94
|
+
|
95
|
+
def pri
|
96
|
+
(@facility * 8) + @severity
|
97
|
+
end
|
98
|
+
|
99
|
+
def pri=(p)
|
100
|
+
unless p.is_a? Integer and (0..191).include?(p)
|
101
|
+
raise ArgumentError.new "PRI must be a number between 0 and 191"
|
102
|
+
end
|
103
|
+
@facility = p / 8
|
104
|
+
@severity = p - (@facility * 8)
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate_timestamp
|
108
|
+
time = @time || Time.now
|
109
|
+
# The timestamp format requires that a day with fewer than 2 digits have
|
110
|
+
# what would normally be a preceding zero, be instead an extra space.
|
111
|
+
day = time.strftime("%d")
|
112
|
+
day = day.sub(/^0/, ' ') if day =~ /^0\d/
|
113
|
+
time.strftime("%b #{day} %H:%M:%S")
|
114
|
+
end
|
115
|
+
|
116
|
+
SEVERITIES.each do |k,v|
|
117
|
+
define_method("#{k}?") {SEVERITIES[k] == @severity}
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module SyslogProtocol
|
4
|
+
|
5
|
+
def self.parse(msg, origin=nil)
|
6
|
+
packet = Packet.new
|
7
|
+
original_msg = msg.dup
|
8
|
+
pri = parse_pri(msg)
|
9
|
+
if pri and (pri = pri.to_i).is_a? Integer and (0..191).include?(pri)
|
10
|
+
packet.pri = pri
|
11
|
+
else
|
12
|
+
# If there isn't a valid PRI, treat the entire message as content
|
13
|
+
packet.pri = 13
|
14
|
+
packet.time = Time.now
|
15
|
+
packet.hostname = origin || 'unknown'
|
16
|
+
packet.content = original_msg
|
17
|
+
return packet
|
18
|
+
end
|
19
|
+
time = parse_time(msg)
|
20
|
+
if time
|
21
|
+
packet.time = Time.parse(time)
|
22
|
+
else
|
23
|
+
packet.time = Time.now
|
24
|
+
end
|
25
|
+
hostname = parse_hostname(msg)
|
26
|
+
packet.hostname = hostname || origin
|
27
|
+
if m = msg.match(/^(\w+)(: | )(.*)$/)
|
28
|
+
packet.tag = m[1]
|
29
|
+
packet.content = m[3]
|
30
|
+
else
|
31
|
+
packet.tag = 'unknown'
|
32
|
+
packet.content = msg
|
33
|
+
end
|
34
|
+
packet
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def self.parse_pri(msg)
|
40
|
+
pri = msg.slice!(/<(\d\d?\d?)>/)
|
41
|
+
pri = pri.slice(/\d\d?\d?/) if pri
|
42
|
+
if !pri or (pri =~ /^0/ and pri !~ /^0$/)
|
43
|
+
return nil
|
44
|
+
else
|
45
|
+
return pri
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.parse_time(msg)
|
50
|
+
msg.slice!(/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\s|[1-9])\d\s\d\d:\d\d:\d\d\s/)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.parse_hostname(msg)
|
54
|
+
msg.slice!(/^[\x21-\x7E]+\s/).rstrip
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
## This is the rakegem gemspec template. Make sure you read and understand
|
2
|
+
## all of the comments. Some sections require modification, and others can
|
3
|
+
## be deleted if you don't need them. Once you understand the contents of
|
4
|
+
## this file, feel free to delete any comments that begin with two hash marks.
|
5
|
+
## You can find comprehensive Gem::Specification documentation, at
|
6
|
+
## http://docs.rubygems.org/read/chapter/20
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
+
s.rubygems_version = '1.3.5'
|
11
|
+
|
12
|
+
## Leave these as is they will be modified for you by the rake gemspec task.
|
13
|
+
## If your rubyforge_project name is different, then edit it and comment out
|
14
|
+
## the sub! line in the Rakefile
|
15
|
+
s.name = 'syslog_protocol'
|
16
|
+
s.version = '0.9.0'
|
17
|
+
s.date = "2009-08-01"
|
18
|
+
# s.rubyforge_project = 'syslog_protocol'
|
19
|
+
|
20
|
+
## Make sure your summary is short. The description may be as long
|
21
|
+
## as you like.
|
22
|
+
s.summary = "Syslog protocol parser and generator"
|
23
|
+
s.description = "Syslog protocol parser and generator"
|
24
|
+
|
25
|
+
## List the primary authors. If there are a bunch of authors, it's probably
|
26
|
+
## better to set the email to an email list or something. If you don't have
|
27
|
+
## a custom homepage, consider using your GitHub URL or the like.
|
28
|
+
s.authors = ["Jake Douglas", 'Eric Lindvall']
|
29
|
+
s.email = [ "jakecdouglas@gmail.com", 'eric@5stops.com' ]
|
30
|
+
s.homepage = 'https://github.com/eric/syslog_protocol'
|
31
|
+
|
32
|
+
## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
|
33
|
+
## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
|
34
|
+
s.require_paths = %w[lib]
|
35
|
+
|
36
|
+
## This sections is only necessary if you have C extensions.
|
37
|
+
# s.require_paths << 'ext'
|
38
|
+
# s.extensions = %w[ext/extconf.rb]
|
39
|
+
|
40
|
+
## If your gem includes any executables, list them here.
|
41
|
+
# s.executables = ["name"]
|
42
|
+
# s.default_executable = 'name'
|
43
|
+
|
44
|
+
## Specify any RDoc options here. You'll want to add your README and
|
45
|
+
## LICENSE files to the extra_rdoc_files list.
|
46
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
47
|
+
s.extra_rdoc_files = %w[README.md]
|
48
|
+
|
49
|
+
## List your runtime dependencies here. Runtime dependencies are those
|
50
|
+
## that are needed for an end user to actually USE your code.
|
51
|
+
# s.add_dependency('DEPNAME', [">= 1.1.0", "< 2.0.0"])
|
52
|
+
|
53
|
+
## List your development dependencies here. Development dependencies are
|
54
|
+
## those that are only needed during development
|
55
|
+
s.add_development_dependency('bacon', [ '~> 1.1.0' ])
|
56
|
+
|
57
|
+
## Leave this section as-is. It will be automatically generated from the
|
58
|
+
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
59
|
+
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
60
|
+
# = MANIFEST =
|
61
|
+
s.files = %w[
|
62
|
+
Gemfile
|
63
|
+
README.md
|
64
|
+
Rakefile
|
65
|
+
lib/syslog_protocol.rb
|
66
|
+
lib/syslog_protocol/common.rb
|
67
|
+
lib/syslog_protocol/logger.rb
|
68
|
+
lib/syslog_protocol/packet.rb
|
69
|
+
lib/syslog_protocol/parser.rb
|
70
|
+
syslog_protocol.gemspec
|
71
|
+
test/helper.rb
|
72
|
+
test/test_logger.rb
|
73
|
+
test/test_packet.rb
|
74
|
+
test/test_parser.rb
|
75
|
+
]
|
76
|
+
# = MANIFEST =
|
77
|
+
|
78
|
+
## Test files will be grabbed from the file list. Make sure the path glob
|
79
|
+
## matches what you actually use.
|
80
|
+
s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
|
81
|
+
end
|
data/test/helper.rb
ADDED
data/test/test_logger.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
describe "syslog logger" do
|
4
|
+
|
5
|
+
it "create a new logger with hostname and facility" do
|
6
|
+
lambda {@logger = SyslogProto::Logger.new("space_station", 'test', "local0")}.should.not.raise
|
7
|
+
end
|
8
|
+
|
9
|
+
it "hostname and facility must conform to the requirements of a Packet" do
|
10
|
+
lambda {SyslogProto::Logger.new("space station", "some shit", 'test test')}.should.raise ArgumentError
|
11
|
+
end
|
12
|
+
|
13
|
+
it "generates packets" do
|
14
|
+
# We have to set a time so we have a consistant timestamp to check against..
|
15
|
+
p = @logger.instance_variable_get("@packet")
|
16
|
+
p.time = Time.now
|
17
|
+
ts = p.generate_timestamp
|
18
|
+
@logger.debug("vacuum tubez are operational").should.equal "<135>#{ts} space_station test: vacuum tubez are operational"
|
19
|
+
@logger.info("firing thrusters at 13 degrees").should.equal "<134>#{ts} space_station test: firing thrusters at 13 degrees"
|
20
|
+
@logger.notice("the hyper drive has been activated").should.equal "<133>#{ts} space_station test: the hyper drive has been activated"
|
21
|
+
@logger.warn("meteorites incoming!").should.equal "<132>#{ts} space_station test: meteorites incoming!"
|
22
|
+
@logger.err("vacuum tube 3 in hyper drive failed").should.equal "<131>#{ts} space_station test: vacuum tube 3 in hyper drive failed"
|
23
|
+
@logger.crit("wing struck by a meteorite!").should.equal "<130>#{ts} space_station test: wing struck by a meteorite!"
|
24
|
+
@logger.alert("LEAKING ATMOSPHERE").should.equal "<129>#{ts} space_station test: LEAKING ATMOSPHERE"
|
25
|
+
@logger.emerg("LEAKING ASTRONAUTS WE ARE DONE").should.equal "<128>#{ts} space_station test: LEAKING ASTRONAUTS WE ARE DONE"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/test/test_packet.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
describe "a syslog packet" do
|
4
|
+
|
5
|
+
@p = SyslogProto::Packet.new
|
6
|
+
|
7
|
+
it "should embarrass a person who does not set the fields" do
|
8
|
+
lambda { @p.to_s }.should.raise RuntimeError
|
9
|
+
end
|
10
|
+
|
11
|
+
it "hostname may not be omitted" do
|
12
|
+
lambda {@p.hostname = ""}.should.raise ArgumentError
|
13
|
+
end
|
14
|
+
|
15
|
+
it "hostname may only contain ASCII characters 33-126 (no spaces!)" do
|
16
|
+
lambda {@p.hostname = "linux box"}.should.raise ArgumentError
|
17
|
+
lambda {@p.hostname = "\000" + "linuxbox"}.should.raise ArgumentError
|
18
|
+
lambda {@p.hostname = "space_station"}.should.not.raise
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'tag may only contain ASCII characters 33-126 (no spaces!)' do
|
22
|
+
lambda {@p.tag = "linux box"}.should.raise ArgumentError
|
23
|
+
lambda {@p.tag = "\000" + "linuxbox"}.should.raise ArgumentError
|
24
|
+
lambda {@p.tag = "test"}.should.not.raise
|
25
|
+
end
|
26
|
+
|
27
|
+
it "facility may only be set within 0-23 or with a proper string name" do
|
28
|
+
lambda {@p.facility = 666}.should.raise ArgumentError
|
29
|
+
lambda {@p.facility = "mir space station"}.should.raise ArgumentError
|
30
|
+
|
31
|
+
lambda {@p.facility = 16}.should.not.raise
|
32
|
+
@p.facility.should.equal 16
|
33
|
+
lambda {@p.facility = 'local0'}.should.not.raise
|
34
|
+
@p.facility.should.equal 16
|
35
|
+
end
|
36
|
+
|
37
|
+
it "severity may only be set within 0-7 or with a proper string name" do
|
38
|
+
lambda {@p.severity = 9876}.should.raise ArgumentError
|
39
|
+
lambda {@p.severity = "omgbroken"}.should.raise ArgumentError
|
40
|
+
|
41
|
+
lambda {@p.severity = 6}.should.not.raise
|
42
|
+
@p.severity.should.equal 6
|
43
|
+
lambda {@p.severity = 'info'}.should.not.raise
|
44
|
+
@p.severity.should.equal 6
|
45
|
+
end
|
46
|
+
|
47
|
+
it "severity can be checked using 'some_severity?' methods" do
|
48
|
+
@p.info?.should.equal true
|
49
|
+
@p.alert?.should.equal false
|
50
|
+
@p.emerg?.should.equal false
|
51
|
+
end
|
52
|
+
|
53
|
+
it "PRI is calculated from the facility and severity" do
|
54
|
+
@p.pri.should.equal 134
|
55
|
+
end
|
56
|
+
|
57
|
+
it "PRI may only be within 0-191" do
|
58
|
+
lambda {@p.pri = 22331}.should.raise ArgumentError
|
59
|
+
lambda {@p.pri = "foo"}.should.raise ArgumentError
|
60
|
+
end
|
61
|
+
|
62
|
+
it "facility and severity are deduced and set from setting a valid PRI" do
|
63
|
+
@p.pri = 165
|
64
|
+
@p.severity.should.equal 5
|
65
|
+
@p.facility.should.equal 20
|
66
|
+
end
|
67
|
+
|
68
|
+
it "return the proper names for facility and severity" do
|
69
|
+
@p.severity_name.should.equal 'notice'
|
70
|
+
@p.facility_name.should.equal 'local4'
|
71
|
+
end
|
72
|
+
|
73
|
+
it "set a message, which apparently can be anything" do
|
74
|
+
@p.content = "exploring ze black hole"
|
75
|
+
@p.content.should.equal "exploring ze black hole"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "timestamp must conform to the retarded format" do
|
79
|
+
@p.generate_timestamp.should.match /(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\s|[1-9])\d\s\d\d:\d\d:\d\d/
|
80
|
+
end
|
81
|
+
|
82
|
+
it "use the current time and assemble the packet" do
|
83
|
+
timestamp = @p.generate_timestamp
|
84
|
+
@p.to_s.should.equal "<165>#{timestamp} space_station test: exploring ze black hole"
|
85
|
+
end
|
86
|
+
|
87
|
+
it "packets larger than 1024 will be truncated" do
|
88
|
+
@p.content = "space warp" * 1000
|
89
|
+
@p.to_s.bytesize.should.equal 1024
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/test/test_parser.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
describe "syslog packet parser" do
|
4
|
+
|
5
|
+
it "parse some valid packets" do
|
6
|
+
p = SyslogProto.parse("<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8")
|
7
|
+
p.facility.should.equal 4
|
8
|
+
p.severity.should.equal 2
|
9
|
+
p.pri.should.equal 34
|
10
|
+
p.hostname.should.equal "mymachine"
|
11
|
+
p.tag.should.equal 'su'
|
12
|
+
p.content.should.equal "'su root' failed for lonvick on /dev/pts/8"
|
13
|
+
p.time.should.equal Time.parse("Oct 11 22:14:15")
|
14
|
+
|
15
|
+
p = SyslogProto.parse("<13>Feb 5 17:32:18 10.0.0.99 test: Use the BFG!")
|
16
|
+
p.facility.should.equal 1
|
17
|
+
p.severity.should.equal 5
|
18
|
+
p.pri.should.equal 13
|
19
|
+
p.hostname.should.equal "10.0.0.99"
|
20
|
+
p.tag.should.equal 'test'
|
21
|
+
p.content.should.equal "Use the BFG!"
|
22
|
+
p.time.should.equal Time.parse("Feb 5 17:32:18")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "treat a packet with no valid PRI as all content, setting defaults" do
|
26
|
+
p = SyslogProto.parse("nomnom")
|
27
|
+
p.facility.should.equal 1
|
28
|
+
p.severity.should.equal 5
|
29
|
+
p.pri.should.equal 13
|
30
|
+
p.hostname.should.equal 'unknown'
|
31
|
+
p.content.should.equal "nomnom"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "PRI with preceding 0's shall be considered invalid" do
|
35
|
+
p = SyslogProto.parse("<045>Oct 11 22:14:15 space_station my PRI is not valid")
|
36
|
+
p.facility.should.equal 1
|
37
|
+
p.severity.should.equal 5
|
38
|
+
p.pri.should.equal 13
|
39
|
+
p.hostname.should.equal 'unknown'
|
40
|
+
p.content.should.equal "<045>Oct 11 22:14:15 space_station my PRI is not valid"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "allow the user to pass an origin to be used as the hostname if packet is invalid" do
|
44
|
+
p = SyslogProto.parse("<045>Oct 11 22:14:15 space_station my PRI is not valid", '127.0.0.1')
|
45
|
+
p.facility.should.equal 1
|
46
|
+
p.severity.should.equal 5
|
47
|
+
p.pri.should.equal 13
|
48
|
+
p.hostname.should.equal '127.0.0.1'
|
49
|
+
p.content.should.equal "<045>Oct 11 22:14:15 space_station my PRI is not valid"
|
50
|
+
end
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: syslog_protocol
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 59
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jake Douglas
|
14
|
+
- Eric Lindvall
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2009-08-01 00:00:00 -07:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: bacon
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 19
|
31
|
+
segments:
|
32
|
+
- 1
|
33
|
+
- 1
|
34
|
+
- 0
|
35
|
+
version: 1.1.0
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id001
|
38
|
+
description: Syslog protocol parser and generator
|
39
|
+
email:
|
40
|
+
- jakecdouglas@gmail.com
|
41
|
+
- eric@5stops.com
|
42
|
+
executables: []
|
43
|
+
|
44
|
+
extensions: []
|
45
|
+
|
46
|
+
extra_rdoc_files:
|
47
|
+
- README.md
|
48
|
+
files:
|
49
|
+
- Gemfile
|
50
|
+
- README.md
|
51
|
+
- Rakefile
|
52
|
+
- lib/syslog_protocol.rb
|
53
|
+
- lib/syslog_protocol/common.rb
|
54
|
+
- lib/syslog_protocol/logger.rb
|
55
|
+
- lib/syslog_protocol/packet.rb
|
56
|
+
- lib/syslog_protocol/parser.rb
|
57
|
+
- syslog_protocol.gemspec
|
58
|
+
- test/helper.rb
|
59
|
+
- test/test_logger.rb
|
60
|
+
- test/test_packet.rb
|
61
|
+
- test/test_parser.rb
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: https://github.com/eric/syslog_protocol
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --charset=UTF-8
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.3.7
|
93
|
+
signing_key:
|
94
|
+
specification_version: 2
|
95
|
+
summary: Syslog protocol parser and generator
|
96
|
+
test_files:
|
97
|
+
- test/test_logger.rb
|
98
|
+
- test/test_packet.rb
|
99
|
+
- test/test_parser.rb
|