heroku-log-parser 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []