heroku-log-parser 0.2

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,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ .rvmrc
@@ -0,0 +1,52 @@
1
+ heroku-log-parser
2
+ =======
3
+
4
+ A [syslog (rfc5424)](http://tools.ietf.org/html/rfc5424#section-6) parser written in Ruby and specifically
5
+ targeting Heroku's [http log drain](https://devcenter.heroku.com/articles/labs-https-drains).
6
+
7
+ ## Install
8
+
9
+ Declare `heroku-log-parser` in your `Gemfile`.
10
+
11
+ ```ruby
12
+ gem 'heroku-log-parser', :git => 'git@github.com:rwdaigle/heroku-log-parser.git'
13
+ ```
14
+
15
+ Run bundler.
16
+
17
+ ```term
18
+ $ bundle install
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ msg_str = "156 <40>1 2012-11-30T06:45:26+00:00 heroku web.3 d.73ea7440-270a-435a-a0ea-adf50b4e5f5a - Starting process with command `bundle exec rackup config.ru -p 24405`"
25
+
26
+ HerokuLogParser.parse(msg_str)
27
+ #=> [{:priority=>40, :syslog_version=>1, :emitted_at=>2012-11-30 06:45:26 UTC, :hostname=>"heroku", :appname=>nil, :proc_id=>"web.3", :msg_id=>"d.73ea7440-270a-435a-a0ea-adf50b4e5f5a", :structured_data=>nil, :message=>"Starting process with command `bundle exec rackup config.ru -p 24405`"}]
28
+ ```
29
+
30
+ `HerokuLogParser` is a stateless, regex-based parser that accepts a string of data holding one or more syslog messages
31
+ and returns an array of syslog message properties for each message. For those unwilling to read the spec, the
32
+ list of syslog tokens is as follows (and is stored in the `HerokuLogParser::SYSLOG_KEYS` array):
33
+
34
+ ```ruby
35
+ HerokuLogParser::SYSLOG_KEYS
36
+ #=> [:priority, :syslog_version, :emitted_at, :hostname, :appname, :proc_id, :msg_id, :structured_data, :message]
37
+ ```
38
+
39
+ ## Contributions
40
+
41
+ * [Ryan Smith](https://github.com/ryandotsmith/) for his work on [l2met](https://github.com/ryandotsmith/l2met) which forms the foundation of heroku-log-parser.
42
+
43
+ ## Todos
44
+
45
+ * TESTS!!!!
46
+ * 2nd order parsing. For instance, for parsing a structured message body into key=value pairs (including the structured_data message part)
47
+
48
+ ## Issues
49
+
50
+ Please submit all issues to the project's Github issues.
51
+
52
+ -- [@rwdaigle](https://twitter.com/rwdaigle)
@@ -0,0 +1,52 @@
1
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
2
+ require 'heroku-log-parser/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "heroku-log-parser"
6
+ s.version = HerokuLogParser::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.author = "Ryan Daigle"
9
+ s.email = ["ryan.daigle@gmail.com"]
10
+ s.homepage = "https://github.com/rwdaigle/heroku-log-parser"
11
+ s.summary = "Syslog message parser"
12
+ s.description = "Easily parse Heroku's syslog-based application log-stream"
13
+
14
+ s.rubyforge_project = "heroku-log-parser"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ if File.exists?('UPGRADING')
22
+ s.post_install_message = File.read("UPGRADING")
23
+ end
24
+
25
+ s.required_ruby_version = ">= 1.8.7"
26
+
27
+ # s.add_dependency('activerecord', '>= 3.0.0')
28
+ # s.add_dependency('activemodel', '>= 3.0.0')
29
+ # s.add_dependency('activesupport', '>= 3.0.0')
30
+ # s.add_dependency('cocaine', '~> 0.4.0')
31
+ # s.add_dependency('mime-types')
32
+
33
+ # s.add_development_dependency('shoulda')
34
+ # s.add_development_dependency('appraisal')
35
+ # s.add_development_dependency('mocha')
36
+ # s.add_development_dependency('aws-sdk', '>= 1.2.0')
37
+ # s.add_development_dependency('bourne')
38
+ # s.add_development_dependency('sqlite3', '~> 1.3.4')
39
+ # s.add_development_dependency('cucumber', '~> 1.2.1')
40
+ # s.add_development_dependency('aruba')
41
+ # s.add_development_dependency('nokogiri')
42
+ # s.add_development_dependency('capybara')
43
+ # s.add_development_dependency('bundler')
44
+ # s.add_development_dependency('cocaine', '~> 0.2')
45
+ # s.add_development_dependency('fog', '>= 1.4.0', "< 1.7.0")
46
+ # s.add_development_dependency('pry')
47
+ # s.add_development_dependency('launchy')
48
+ # s.add_development_dependency('rake')
49
+ # s.add_development_dependency('fakeweb')
50
+ # s.add_development_dependency('railties')
51
+ # s.add_development_dependency('actionmailer')
52
+ end
@@ -0,0 +1,77 @@
1
+ class HerokuLogParser
2
+
3
+ SYSLOG_KEYS = :priority, :syslog_version, :emitted_at, :hostname, :appname, :proc_id, :msg_id, :structured_data, :message
4
+
5
+ class << self
6
+
7
+ def parse(data_str)
8
+ events = []
9
+ lines(data_str) do |line|
10
+ if(matching = line.match(line_regex))
11
+ events << event_data(matching)
12
+ end
13
+ end
14
+ events
15
+ end
16
+
17
+ protected
18
+
19
+ # http://tools.ietf.org/html/rfc5424#page-8
20
+ # frame <prority>version time hostname <appname-missing> procid msgid [no structured data = '-'] msg
21
+ # 120 <40>1 2012-11-30T06:45:29+00:00 heroku web.3 d.73ea7440-270a-435a-a0ea-adf50b4e5f5a - State changed from starting to up
22
+ def line_regex
23
+ @line_regex ||= /\<(\d+)\>(1) (\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\+00:00) ([a-z0-9-]+) ([a-z0-9\-\_\.]+) ([a-z0-9\-\_\.]+) (\-) (.*)$/
24
+ end
25
+
26
+ # Heroku's http log drains (https://devcenter.heroku.com/articles/labs-https-drains)
27
+ # utilize octet counting framing (http://tools.ietf.org/html/draft-gerhards-syslog-plain-tcp-12#section-3.4.1)
28
+ # for transmission of syslog messages over TCP. Properly parse and delimit
29
+ # individual syslog messages, many of which may be contained in a single packet.
30
+ #
31
+ # I am still uncertain if this is the place for transport layer protocol handling. I suspect not.
32
+ #
33
+ def lines(data_str, &block)
34
+ d = data_str
35
+ while d && d.length > 0
36
+ if matching = d.match(/^(\d+) /) # if have a counting frame, use it
37
+ num_bytes = matching[1].to_i
38
+ frame_offset = matching[0].length
39
+ line_end = frame_offset + num_bytes
40
+ msg = d[frame_offset..line_end]
41
+ yield msg
42
+ d = d[line_end..d.length]
43
+ elsif matching = d.match(/\n/) # Newlines = explicit message delimiter
44
+ d = matching.post_match
45
+ else
46
+ STDERR.puts("Unable to parse: #{d}")
47
+ return
48
+ end
49
+ end
50
+ end
51
+
52
+ # Heroku is missing the appname token, otherwise can treat as standard syslog format
53
+ def event_data(matching)
54
+ event = {}
55
+ event[:priority] = matching[1].to_i
56
+ event[:syslog_version] = matching[2].to_i
57
+ event[:emitted_at] = nil?(matching[3]) ? nil : Time.parse(matching[3]).utc
58
+ event[:hostname] = interpret_nil(matching[4])
59
+ event[:appname] = nil
60
+ event[:proc_id] = interpret_nil(matching[5])
61
+ event[:msg_id] = interpret_nil(matching[6])
62
+ event[:structured_data] = interpret_nil(matching[7])
63
+ event[:message] = interpret_nil(matching[8])
64
+ event
65
+ end
66
+
67
+ private
68
+
69
+ def interpret_nil(val)
70
+ nil?(val) ? nil : val
71
+ end
72
+
73
+ def nil?(val)
74
+ val == "-"
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,3 @@
1
+ class HerokuLogParser
2
+ VERSION = "0.2" unless defined? HerokuLogParser::VERSION
3
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heroku-log-parser
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ryan Daigle
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-14 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Easily parse Heroku's syslog-based application log-stream
15
+ email:
16
+ - ryan.daigle@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - README.md
23
+ - heroku-log-parser.gemspec
24
+ - lib/heroku-log-parser.rb
25
+ - lib/heroku-log-parser/version.rb
26
+ homepage: https://github.com/rwdaigle/heroku-log-parser
27
+ licenses: []
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.8.7
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project: heroku-log-parser
46
+ rubygems_version: 1.8.23
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: Syslog message parser
50
+ test_files: []