Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/syslog_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
require 'syslog_protocol/packet'
require 'syslog_protocol/logger'
require 'syslog_protocol/parser'
require 'syslog_protocol/syslogrfc5424packet'
require 'syslog_protocol/syslogrfc5424parser'

module SyslogProtocol
VERSION = '0.9.2'
Expand Down
14 changes: 10 additions & 4 deletions lib/syslog_protocol/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,22 @@ module SyslogProtocol
22 => 'local6',
23 => 'local7'
}

SEVERITIES = {
'emerg' => 0,
'emergency' => 0,
'alert' => 1,
'crit' => 2,
'critical' => 2,
'err' => 3,
'error' => 3,
'warn' => 4,
'warning' => 4,
'notice' => 5,
'info' => 6,
'debug' => 7
'debug' => 7
}

SEVERITY_INDEX = {
0 => 'emerg',
1 => 'alert',
Expand All @@ -76,4 +80,6 @@ module SyslogProtocol
6 => 'info',
7 => 'debug'
}
end

end

23 changes: 20 additions & 3 deletions lib/syslog_protocol/packet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ class Packet
attr_reader :facility, :severity, :hostname, :tag
attr_accessor :time, :content

def initialize()
super()
@hostname = nil
@time = nil
end

def to_s
assemble
end
Expand All @@ -11,7 +17,9 @@ def assemble(max_size = 1024)
unless @hostname and @facility and @severity and @tag
raise "Could not assemble packet without hostname, tag, facility, and severity"
end
data = "<#{pri}>#{generate_timestamp} #{@hostname} #{@tag}: #{@content}"

fmt = "<%s>%s %s %s: %s"
data = fmt % [pri, generate_timestamp, @hostname, @tag, @content]

if string_bytesize(data) > max_size
data = data.slice(0, max_size)
Expand Down Expand Up @@ -98,7 +106,7 @@ def severity_name
end

def pri
(@facility * 8) + @severity
(@facility << 3) | @severity
end

def pri=(p)
Expand All @@ -118,6 +126,15 @@ def generate_timestamp
time.strftime("%b #{day} %H:%M:%S")
end

private
def format_field(text, max_length)
if text
text[0, max_length].gsub(/\s+/, '')
else
'-'
end
end

if "".respond_to?(:bytesize)
def string_bytesize(string)
string.bytesize
Expand All @@ -132,4 +149,4 @@ def string_bytesize(string)
define_method("#{k}?") { SEVERITIES[k] == @severity }
end
end
end
end
109 changes: 109 additions & 0 deletions lib/syslog_protocol/syslogrfc5424packet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
module SyslogProtocol
class SyslogRfc5424Packet < Packet
attr_reader :appname, :procid, :msgid, :structured_data

def initialize(appname = nil, procid = nil, msgid = nil, facility = nil)
super()
@msgid = format_field(msgid, 32)
@procid = format_field(procid, 128)
@appname = format_field(appname, 48)
@structured_data = {}
end

def assemble(max_size = 1024)
unless @hostname and @facility and @severity and @appname
raise "Could not assemble packet without hostname, tag, facility, and severity"
end
sd = '-'
unless @structured_data.empty?
sd = format_sdata(@structured_data)
end
fmt = "<%s>1 %s %s %s %s %s %s %s"
data = fmt % [pri, @time, @hostname,
@appname, format_field(@procid, 128),@msgid, sd, @content]

if string_bytesize(data) > max_size
data = data.slice(0, max_size)
while string_bytesize(data) > max_size
data = data.slice(0, data.length - 1)
end
end

data
end

def generate_timestamp
@time || Time.now.to_datetime.rfc3339(6)
end

def appname=(a)
unless a && a.is_a?(String) && a.length > 0
raise ArgumentError, "Appname must not be omitted"
end
if a.length > 48
raise ArgumentError, "Appname must not be longer than 48 characters"
end
if a =~ /\s/
raise ArgumentError, "Appname may not contain spaces"
end
if a =~ /[^\x21-\x7E]/
raise ArgumentError, "Appname may only contain ASCII characters 33-126"
end

@appname = a
end

def procid=(p)
if p.length > 128
raise ArgumentError.new("Procid can't be bigger than 128")
end
@procid = format_field(p, 128)
end

def msgid=(m)
if m.is_a? Integer
@msgid = format_field(m.to_s, 32)
elsif m.is_a? String
if m.length > 32
raise ArgumentError, "msgid must not be longer than 32 characters"
else
@msgid = format_field(m, 32)
end
else
raise ArgumentError.new "msgid must be a number or string"
end
end

def structured_data=(s)
if s.is_a? Hash
@structured_data = s
else
raise ArgumentError.new "structured_data must be a dict"
end
end

def format_sdata(sdata)
if sdata.empty?
'-'
end
r = []
sdata.each { |sid, hash|
s = []
s.push(sid.to_s.gsub(/[^-@\w]/, ""))
hash.each { |n, v|
# RFC-5424 requires SD-NAME to be 32 length
paramname = format_field(n.to_s.gsub(/[^-@\w]/, ""), 32)
paramvalue = v.to_s.gsub(/[\]"=]/, "")
s.push("#{paramname}=\"#{paramvalue}\"")
}
r.push("["+s.join(" ")+"]")
}
rx = []
r.each { |x|
rx.push("[#{x}]")
}
r.join("")
end

end
end
128 changes: 128 additions & 0 deletions lib/syslog_protocol/syslogrfc5424parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
require 'time'

module SyslogProtocol

def self.syslog5424_parse(msg, origin=nil)
packet = SyslogRfc5424Packet.new
original_msg = msg.dup
pri = syslog5424_parse_pri(msg)
if pri and (pri = pri.to_i).is_a? Integer and (0..191).include?(pri)
packet.pri = pri
else
# If there isn't a valid PRI, treat the entire message as content
packet.pri = 13
packet.time = Time.now
packet.hostname = origin || 'unknown'
packet.content = original_msg

return packet
end
time = syslog5424_parse_time(msg)
if time
packet.time = Time.parse(time)
else
packet.time = Time.now
end
hostname = syslog5424_parse_hostname(msg)
packet.hostname = hostname || origin
appname = syslog5424_parse_appname(msg)
packet.appname = appname
procid = syslog5424_parse_procid(msg)
packet.procid = procid
msgid = syslog5424_parse_msgid(msg)
packet.msgid = msgid
structured_data = syslog5424_parse_structured_data(msg)
packet.structured_data = structured_data
content = syslog5424_parse_content(msg)
packet.content = content

packet
end

private

def self.syslog5424_parse_pri(msg)
pri = msg.slice!(/<(\d\d?\d?)>1/)
pri = pri.slice(/\d\d?\d?/) if pri
if !pri or (pri =~ /^0/ and pri !~ /^0$/)
return nil
else
return pri
end
end

def self.syslog5424_parse_time(msg)
msg.slice!(/(\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\d\d\d\d\+\d\d:\d\d)/)
end

def self.syslog5424_parse_hostname(msg)
m = msg.split(" ")
if m.nil? or m.empty?
raise ArgumentError, "Message format is not correct"
end
return m[0]

end

def self.syslog5424_parse_appname(msg)
m = msg.split(" ")
if m.nil? or m.empty?
raise ArgumentError, "Message format is not correct"
end
return m[1]
end
def self.syslog5424_parse_procid(msg)
m = msg.split(" ")

if m.nil? or m.empty?
raise ArgumentError, "Message format is not correct"
end
return m[2]
end
def self.syslog5424_parse_msgid(msg)
m = msg.split(" ")
if m.nil? or m.empty?
raie ArgumentError, "Message format is not correct"
end
return m[3]
end

def self.syslog5424_parse_content(msg)
m = msg.match(/(.*)\s(.*)\s(.*)\s(.*)\s(-)\s(.*)/)
if m.nil?
s = msg.match(/(.*)\s(\[.*\])\s(.*)/)
if s.nil?
raise ArgumentError, "Message format is not correct"
else
return s[3]
end
else
return m[6]
end
end

def self.syslog5424_parse_structured_data(msg)
s_data = {}
m = msg.match(/(.*)\s(\[.*\])\s/)
s_data = parse_structured_data(m[2]) unless m.nil?
return s_data
end

private
def self.parse_structured_data(sdata)
structured_data = {}
sdata_key = ''
sdata_value = {}
arr = sdata.sub(/^\[/, "").sub(/\]/,"").split(" ")
arr.each { |item|
if item.include?("@")
sdata_key = item
else
key, value = item.split("=")
sdata_value[key] = value
end
}
structured_data[sdata_key] = sdata_value
return structured_data
end
end
4 changes: 4 additions & 0 deletions syslog_protocol.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,15 @@ Gem::Specification.new do |s|
lib/syslog_protocol/logger.rb
lib/syslog_protocol/packet.rb
lib/syslog_protocol/parser.rb
lib/syslogrfc5424packet.rb
lib/syslogrfc5424parser.rb
syslog_protocol.gemspec
test/helper.rb
test/test_logger.rb
test/test_packet.rb
test/test_parser.rb
test/test_syslogrfc5424packet.rb
test/test_syslogrfc5424parser.rb
]
# = MANIFEST =

Expand Down
8 changes: 1 addition & 7 deletions test/test_packet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@
@p.severity.should.equal 6
end

it "severity can be checked using 'some_severity?' methods" do
@p.info?.should.equal true
@p.alert?.should.equal false
@p.emerg?.should.equal false
end

it "PRI is calculated from the facility and severity" do
@p.pri.should.equal 134
end
Expand Down Expand Up @@ -93,4 +87,4 @@
end
end

end
end
Loading