syslog_protocol 0.9.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 ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -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
@@ -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,8 @@
1
+ require 'syslog_protocol/common'
2
+ require 'syslog_protocol/packet'
3
+ require 'syslog_protocol/logger'
4
+ require 'syslog_protocol/parser'
5
+
6
+ module SyslogProtocol
7
+ VERSION = '0.9.0'
8
+ 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
@@ -0,0 +1,13 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ unless ENV['BUNDLE_GEMFILE']
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ Bundler.setup
7
+ Bundler.require
8
+ end
9
+
10
+ require 'bacon'
11
+ Bacon.summary_at_exit
12
+
13
+ require 'syslog_proto'
@@ -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
@@ -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
@@ -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