syslog-parser 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0122b53ddbdfa55a0d0f82651086678004bdb65a
4
+ data.tar.gz: 3ae2068c28677f19eacdc2d85735040ec35f8045
5
+ SHA512:
6
+ metadata.gz: 360e9c187eb3735165e24c1838a88779fbf5897c1e6cc0d1908f649f3c608227db1492b2b74b6d22874f5c2b2a18a1840ebb300196e532b00caac30dbbd2fce0
7
+ data.tar.gz: e145c75a5f8fb905e920ed56ae14303d0cbc620f149413df3c543ae23be88829cdedf0ce4298bbbdd6d29445ceeb30c0636e0ba9e61708ef1497a1a850f733e0
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1 @@
1
+ 2.2.2
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Calle Erlandsson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,93 @@
1
+ # Syslog::Parser
2
+
3
+ Parse RFC5424 Syslog messages
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem "syslog-parser"
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```sh
16
+ bundle
17
+ ```
18
+
19
+ Or install it using gem(1):
20
+
21
+ ```sh
22
+ gem install syslog-parser
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```ruby
28
+ require "syslog/parser"
29
+
30
+ parser = Syslog::Parser.new
31
+
32
+ line = '<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 '\
33
+ [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] '\
34
+ 'An application event log entry...'
35
+
36
+ message = parser.parse(line)
37
+
38
+ message.prival #=> 165
39
+ message.facility #=> 20
40
+ message.severity #=> 5
41
+ message.version #=> 1
42
+ message.timestamp #=> 2003-10-11 22:14:15 UTC
43
+ message.timestamp.class #=> Time
44
+ message.hostname #=> "mymachine.example.com"
45
+ message.app_name #=> "evntslog"
46
+ message.procid #=> nil
47
+ message.structured_data #=> [#<struct StructuredDataElement
48
+ # id="exampleSDID@32473"@71, params={"iut"=>"3", "eventSource"=>"Application",
49
+ # "eventID"=>"1011"}>]
50
+ message.msg #=> "An application event log entry..."
51
+
52
+ parser.parse("malformed") #=> "Syslog::Parser::Error: Failed to match sequence
53
+ # (HEADER SP STRUCTURED_DATA (SP MSG)?) at line 1 char 1."
54
+ ```
55
+
56
+ ### Parsing messages received via Heroku HTTPS log drains
57
+
58
+ The cloud application platform [Heroku][heroku] allows it's users to register
59
+ log drains that receive Syslog formatted application log messages over HTTPS. As
60
+ outlined in [Heroku's documentation on HTTPS Log Drains][drains], these messages
61
+ do not fully conform to RFC5424:
62
+
63
+ > “application/logplex-1” does not conform to RFC5424. It leaves out
64
+ > STRUCTURED-DATA but does not replace it with a NILVALUE.
65
+
66
+ RFC5424 requires STRUCTURED-DATA to consist of either one NILVALUE or one or
67
+ more SD-ELEMENTs.
68
+
69
+ [heroku]: https://heroku.com
70
+ [drains]: https://devcenter.heroku.com/articles/log-drains#https-drains
71
+
72
+ In order to support parsing log messages received via Heroku HTTPS log drains,
73
+ the parser support an option that can be used to allow missing STRUCTURED-DATA:
74
+
75
+ ```ruby
76
+ parser = Syslog::Parser.new(allow_missing_structured_data: true)
77
+
78
+ line = "<40>1 2012-11-30T06:45:29+00:00 host app web.3 - State changed from "\
79
+ "starting to up"
80
+
81
+ message = parser.parse(line)
82
+
83
+ message.prival #=> 40
84
+ message.facility #=> 5
85
+ message.severity #=> 0
86
+ message.version #=> 1
87
+ message.timestamp #=> 2012-11-30 06:45:29 UTC
88
+ message.hostname #=> "host"
89
+ message.app_name #=> "app"
90
+ message.procid #=> "web.3"
91
+ message.structured_data #=> nil
92
+ message.msg #=> "State changed from starting to up"
93
+ ```
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/*.rb']
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,19 @@
1
+ require "syslog/parser/internal_parser"
2
+ require "syslog/parser/transform"
3
+
4
+ module Syslog
5
+ class Parser
6
+ class Error < StandardError; end
7
+
8
+ def initialize(options={})
9
+ @transform = Transform.new
10
+ @parser = InternalParser.new(options)
11
+ end
12
+
13
+ def parse(line)
14
+ @transform.apply @parser.parse(line)
15
+ rescue Parslet::ParseFailed => parse_failed
16
+ raise Error, parse_failed.message
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,177 @@
1
+ require "parslet"
2
+
3
+ module Syslog
4
+ class Parser
5
+ class InternalParser < Parslet::Parser
6
+ def initialize(options={})
7
+ super()
8
+
9
+ @allow_missing_structured_data =
10
+ options.fetch(:allow_missing_structured_data, false)
11
+ end
12
+
13
+ root :syslog_msg
14
+
15
+ rule :syslog_msg do
16
+ if @allow_missing_structured_data
17
+ (header >> (sp >> structured_data).maybe >> (sp >> msg).maybe)
18
+ .as(:syslog_msg)
19
+ else
20
+ (header >> sp >> structured_data >> (sp >> msg).maybe).as(:syslog_msg)
21
+ end
22
+ end
23
+
24
+ rule :header do
25
+ pri >> version >> sp >> timestamp >> sp >> hostname >> sp >>
26
+ app_name >> sp >> procid >> sp >> msgid
27
+ end
28
+
29
+ rule :pri do
30
+ str("<") >> prival >> str(">")
31
+ end
32
+
33
+ rule :prival do
34
+ (digit.repeat(1, 3)).as(:prival) # range 0..191
35
+ end
36
+
37
+ rule :version do
38
+ (nonzero_digit >> digit.repeat(0, 2)).as(:version)
39
+ end
40
+
41
+ rule :hostname do
42
+ (nilvalue | printusascii.repeat(1, 255)).as(:hostname)
43
+ end
44
+
45
+ rule :app_name do
46
+ (nilvalue | printusascii.repeat(1, 48)).as(:app_name)
47
+ end
48
+
49
+ rule :procid do
50
+ (nilvalue | printusascii.repeat(1, 128)).as(:procid)
51
+ end
52
+
53
+ rule :msgid do
54
+ (nilvalue | printusascii.repeat(1, 32)).as(:msgid)
55
+ end
56
+
57
+ rule :timestamp do
58
+ (nilvalue | (full_date >> str("T") >> full_time)).as(:timestamp)
59
+ end
60
+
61
+ rule :full_date do
62
+ date_fullyear >> str("-") >> date_month >> str("-") >> date_mday
63
+ end
64
+
65
+ rule :date_fullyear do
66
+ digit.repeat(4, 4)
67
+ end
68
+
69
+ rule :date_month do
70
+ digit.repeat(2, 2) # 01-12
71
+ end
72
+
73
+ rule :date_mday do
74
+ digit.repeat(2, 2) # 01-28, 01-29, 01-30, 01-31 based on month/year
75
+ end
76
+
77
+ rule :full_time do
78
+ partial_time >> time_offset
79
+ end
80
+
81
+ rule :partial_time do
82
+ time_hour >> str(":") >> time_minute >> str(":") >> time_second >>
83
+ time_secfrac.maybe
84
+ end
85
+
86
+ rule :time_hour do
87
+ digit.repeat(2, 2) # 00-23
88
+ end
89
+
90
+ rule :time_minute do
91
+ digit.repeat(2, 2) # 00-59
92
+ end
93
+
94
+ rule :time_second do
95
+ digit.repeat(2, 2) # 00-59
96
+ end
97
+
98
+ rule :time_secfrac do
99
+ str(".") >> digit.repeat(1, 6)
100
+ end
101
+
102
+ rule :time_offset do
103
+ str("Z") | time_numoffset
104
+ end
105
+
106
+ rule :time_numoffset do
107
+ (str("+") | str("-")) >> time_hour >> str(":") >> time_minute
108
+ end
109
+
110
+ rule :structured_data do
111
+ (nilvalue | sd_element.repeat(1)).as(:structured_data)
112
+ end
113
+
114
+ rule :sd_element do
115
+ str("[") >> sd_id >> sd_params >> str("]")
116
+ end
117
+
118
+ rule :sd_params do
119
+ (sp >> sd_param).repeat.as(:sd_params)
120
+ end
121
+
122
+ rule :sd_param do
123
+ param_name >> str("=") >> str('"') >> param_value >> str('"')
124
+ end
125
+
126
+ rule :sd_id do
127
+ sd_name.as(:sd_id)
128
+ end
129
+
130
+ rule :param_name do
131
+ sd_name.as(:param_name)
132
+ end
133
+
134
+ rule :param_value do
135
+ # characters '"', '\' and ']' MUST be escaped
136
+ (esc_seq | char).repeat.as(:param_value)
137
+ end
138
+
139
+ rule :esc_seq do
140
+ str("\\") >> match(/[\]"]/).as(:esq_char)
141
+ end
142
+
143
+ rule :char do
144
+ match(/[\]"]/).absent? >> any.as(:char)
145
+ end
146
+
147
+ rule :sd_name do
148
+ # except '=', ' ', ']', '"'
149
+ (match(/[= \]"]/).absent? >> printusascii).repeat(1, 32)
150
+ end
151
+
152
+ rule :msg do
153
+ (any.repeat).as(:msg)
154
+ end
155
+
156
+ rule :sp do
157
+ str(" ")
158
+ end
159
+
160
+ rule :printusascii do
161
+ match(/[!-~]/)
162
+ end
163
+
164
+ rule :nonzero_digit do
165
+ match(/[1-9]/)
166
+ end
167
+
168
+ rule :digit do
169
+ str("0") | nonzero_digit
170
+ end
171
+
172
+ rule :nilvalue do
173
+ str("-").as(:nilvalue)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,19 @@
1
+ Message = Struct.new(
2
+ :prival,
3
+ :version,
4
+ :timestamp,
5
+ :hostname,
6
+ :app_name,
7
+ :procid,
8
+ :msgid,
9
+ :structured_data,
10
+ :msg,
11
+ ) do
12
+ def facility
13
+ @facility ||= prival / 8
14
+ end
15
+
16
+ def severity
17
+ @severity ||= prival - facility * 8
18
+ end
19
+ end
@@ -0,0 +1 @@
1
+ StructuredDataElement = Struct.new(:id, :params)
@@ -0,0 +1,38 @@
1
+ require "parslet"
2
+ require "syslog/parser/message"
3
+ require "syslog/parser/structured_data_element"
4
+ require "time"
5
+
6
+ module Syslog
7
+ class Parser
8
+ class Transform < Parslet::Transform
9
+ rule(nilvalue: simple(:nilvalue)) { nil }
10
+ rule(char: simple(:char)) { char.to_s }
11
+ rule(esq_char: simple(:esq_char)) { esq_char.to_s }
12
+
13
+ rule sd_id: simple(:sd_id), sd_params: subtree(:sd_params) do
14
+ params = sd_params.inject({}) do |params, sd_param|
15
+ params.merge!(
16
+ sd_param.fetch(:param_name).to_s =>
17
+ sd_param.fetch(:param_value).join,
18
+ )
19
+ end
20
+ StructuredDataElement.new(sd_id.to_s, params)
21
+ end
22
+
23
+ rule syslog_msg: subtree(:syslog_msg) do
24
+ Message.new(
25
+ Integer(syslog_msg[:prival]),
26
+ Integer(syslog_msg[:version]),
27
+ Time.parse(syslog_msg[:timestamp]),
28
+ syslog_msg[:hostname] && syslog_msg[:hostname].to_s,
29
+ syslog_msg[:app_name] && syslog_msg[:app_name].to_s,
30
+ syslog_msg[:procid] && syslog_msg[:procid].to_s,
31
+ syslog_msg[:msgid] && syslog_msg[:msgid].to_s,
32
+ syslog_msg[:structured_data] && syslog_msg[:structured_data],
33
+ syslog_msg[:msg] && syslog_msg[:msg].to_s,
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ module Syslog
2
+ class Parser
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "syslog/parser/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "syslog-parser"
7
+ spec.version = Syslog::Parser::VERSION
8
+ spec.authors = ["Calle Erlandsson"]
9
+ spec.email = ["calle@calleerlandsson.com"]
10
+
11
+ spec.summary = "Parse RFC5424 Syslog messages"
12
+ spec.homepage = "https://github.com/calleerlandsson/syslog-parser/"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^test/}) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency "parslet"
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.9"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "minitest"
23
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: syslog-parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Calle Erlandsson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parslet
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - calle@calleerlandsson.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".ruby-version"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - lib/syslog/parser.rb
84
+ - lib/syslog/parser/internal_parser.rb
85
+ - lib/syslog/parser/message.rb
86
+ - lib/syslog/parser/structured_data_element.rb
87
+ - lib/syslog/parser/transform.rb
88
+ - lib/syslog/parser/version.rb
89
+ - syslog-parser.gemspec
90
+ homepage: https://github.com/calleerlandsson/syslog-parser/
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.4.5
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Parse RFC5424 Syslog messages
114
+ test_files: []