haproxy_log_parser 1.0.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f019630e3a37d631615874f51d00936ade7a58a2
4
- data.tar.gz: c95c16a28cfc263e3b5f5b3310836b4afbf30094
3
+ metadata.gz: bdee7790396094ba841e7a79fda38388fddd31a6
4
+ data.tar.gz: 77f1cfd3fbe289140fe605dc8092ac38def83fb0
5
5
  SHA512:
6
- metadata.gz: 1d9567c587f66a274e8fa1d829e59c42cb07969094061c0fe5cde805ca322114880c71d62ff18a88f9494f779fc04d4f132105d425d5c22aff3de6e8b95bfef8
7
- data.tar.gz: 5d1d021bef436b3651e362a89ca555c1b3a8e3912bec57030a1bd3c76ad54d93f5fd5560df38460f9181bd36d232084e9020d28e254e0b319436735605d7d98b
6
+ metadata.gz: 92830fae50828e5422a120884f24c7ce5c64c2e8904fd3c6f3846e1e3d5224e028b89243abffe28913ed78773a70cbd4c68816efdbac8b794078ea408ffa50a1
7
+ data.tar.gz: 64254fac5285600842b6dd81b480f3ad5e8428558e7d92ef231b2b5ef4c0ad9e41e91c9b67196b65980242b880235e7dbb4fb4fbfe730257fc8056000f48b1c1
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ haproxy_log_parser-*.gem
2
+ /.bundle
3
+ /Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # haproxy\_log\_parser
2
2
 
3
3
  haproxy\_log\_parser is a gem that parses logs in HAProxy's HTTP log format.
4
+ Use `HAProxyLogParser.parse` to parse a line; this will return an
5
+ `HAProxyLogParser::Entry` for normal lines and `HAProxyLogParser::ErrorEntry`
6
+ for connection error lines.
4
7
 
5
8
  Example:
6
9
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 2.0.0
@@ -11,11 +11,6 @@ Gem::Specification.new do |s|
11
11
 
12
12
  s.add_development_dependency 'rspec', '~> 3.5.0'
13
13
 
14
- s.files = Dir.glob('lib/**/*') + [
15
- 'LICENSE',
16
- 'README.md',
17
- 'VERSION',
18
- 'haproxy_log_parser.gemspec'
19
- ]
20
- s.test_files = Dir.glob('spec/**/*')
14
+ s.files = `git ls-files`.split($/)
15
+ s.test_files = s.files.grep(%r{\Aspec/})
21
16
  end
@@ -0,0 +1,24 @@
1
+ module HAProxyLogParser
2
+ # An instance of this class represents a connection error line of an HAProxy
3
+ # log. See the "Logging > Log formats > Error log format" section in
4
+ # HAProxy's configuration.txt for documentation of fields.
5
+ class ErrorEntry
6
+ # @return [String]
7
+ attr_accessor :client_ip
8
+
9
+ # @return [Integer]
10
+ attr_accessor :client_port
11
+
12
+ # @return [Time]
13
+ attr_accessor :accept_date
14
+
15
+ # @return [String]
16
+ attr_accessor :frontend_name
17
+
18
+ # @return [String]
19
+ attr_accessor :bind_name
20
+
21
+ # @return [String]
22
+ attr_accessor :message
23
+ end
24
+ end
@@ -4,6 +4,11 @@ module HAProxyLogParser
4
4
  syslog_portion:([^\[]+ '[' integer ']: ')
5
5
  client_ip:ip4_address ':' client_port:integer ' '
6
6
  '[' accept_date '] '
7
+ suffix:(normal_suffix / error_suffix)
8
+ "\n"?
9
+ end
10
+
11
+ rule normal_suffix
7
12
  frontend_name:proxy_name transport_mode:'~'? ' '
8
13
  backend_name:proxy_name '/' server_name ' '
9
14
  tq:integer '/' tw:integer '/' tc:integer '/' tr:integer '/' tt:integer ' '
@@ -18,7 +23,11 @@ module HAProxyLogParser
18
23
  captured_request_headers:('{' headers:captured_headers '} ')?
19
24
  captured_response_headers:('{' headers:captured_headers '} ')?
20
25
  '"' http_request:[^"]+ '"'
21
- "\n"?
26
+ end
27
+
28
+ rule error_suffix
29
+ frontend_name:proxy_name '/' bind_name ': '
30
+ message:[^\n]+
22
31
  end
23
32
 
24
33
  rule integer
@@ -52,5 +61,9 @@ module HAProxyLogParser
52
61
  rule captured_headers
53
62
  [^}]*
54
63
  end
64
+
65
+ rule bind_name
66
+ [-_A-Za-z0-9.]+
67
+ end
55
68
  end
56
69
  end
@@ -1,6 +1,7 @@
1
1
  require 'treetop'
2
2
 
3
3
  require 'haproxy_log_parser/entry'
4
+ require 'haproxy_log_parser/error_entry'
4
5
  require 'haproxy_log_parser/line'
5
6
 
6
7
  module HAProxyLogParser
@@ -10,11 +11,11 @@ module HAProxyLogParser
10
11
  class ParseError < ::StandardError; end
11
12
 
12
13
  class << self
13
- # Returns an Entry object resulting from the given HAProxy HTTP-format log
14
- # +line+.
14
+ # Returns an Entry or ErrorEntry object resulting from the given HAProxy
15
+ # HTTP-format log +line+.
15
16
  #
16
17
  # @param line [String] a line from an HAProxy log
17
- # @return [Entry]
18
+ # @return [Entry, ErrorEntry]
18
19
  # @raise [ParseError] if the line was not parsed successfully
19
20
  def parse(line)
20
21
  parser = LineParser.new
@@ -24,39 +25,15 @@ module HAProxyLogParser
24
25
  raise ParseError, parser.failure_reason
25
26
  end
26
27
 
27
- entry = Entry.new
28
- [
29
- :client_ip, :frontend_name, :transport_mode,
30
- :backend_name, :server_name, :termination_state
31
- ].each do |field|
32
- entry.send("#{field}=", result.send(field).text_value)
33
- end
34
- [
35
- :client_port, :tq, :tw, :tc, :tr, :tt, :status_code, :bytes_read,
36
- :actconn, :feconn, :beconn, :srv_conn, :retries, :srv_queue,
37
- :backend_queue
38
- ].each do |field|
39
- entry.send("#{field}=", result.send(field).text_value.to_i)
40
- end
41
-
42
- entry.accept_date = parse_accept_date(result.accept_date.text_value)
43
- [:captured_request_cookie, :captured_response_cookie].each do |field|
44
- cookie = decode_captured_cookie(result.send(field).text_value)
45
- entry.send("#{field}=", cookie)
46
- end
47
-
48
- [:captured_request_headers, :captured_response_headers].each do |field|
49
- if result.send(field).respond_to?(:headers)
50
- headers = decode_captured_headers(
51
- result.send(field).headers.text_value
52
- )
28
+ entry =
29
+ if result.suffix.respond_to?(:http_request)
30
+ create_normal_entry(result.suffix)
53
31
  else
54
- headers = []
32
+ create_error_entry(result.suffix)
55
33
  end
56
- entry.send("#{field}=", headers)
57
- end
58
-
59
- entry.http_request = unescape(result.http_request.text_value)
34
+ entry.client_ip = result.client_ip.text_value
35
+ entry.client_port = result.client_port.text_value.to_i
36
+ entry.accept_date = parse_accept_date(result.accept_date.text_value)
60
37
 
61
38
  entry
62
39
  end
@@ -103,5 +80,58 @@ module HAProxyLogParser
103
80
  def decode_captured_headers(string)
104
81
  string.split('|', -1).map! { |header| unescape(header) }
105
82
  end
83
+
84
+ private
85
+
86
+ # @param result [Treetop::Runtime::SyntaxNode]
87
+ # @return [Entry]
88
+ def create_normal_entry(result)
89
+ entry = Entry.new
90
+
91
+ %i(
92
+ frontend_name transport_mode
93
+ backend_name server_name termination_state
94
+ ).each do |field|
95
+ entry.send("#{field}=", result.send(field).text_value)
96
+ end
97
+
98
+ %i(
99
+ tq tw tc tr tt status_code bytes_read
100
+ actconn feconn beconn srv_conn retries srv_queue backend_queue
101
+ ).each do |field|
102
+ entry.send("#{field}=", result.send(field).text_value.to_i)
103
+ end
104
+
105
+ %i(captured_request_cookie captured_response_cookie).each do |field|
106
+ cookie = decode_captured_cookie(result.send(field).text_value)
107
+ entry.send("#{field}=", cookie)
108
+ end
109
+
110
+ %i(captured_request_headers captured_response_headers).each do |field|
111
+ if result.send(field).respond_to?(:headers)
112
+ headers = decode_captured_headers(
113
+ result.send(field).headers.text_value
114
+ )
115
+ else
116
+ headers = []
117
+ end
118
+ entry.send("#{field}=", headers)
119
+ end
120
+
121
+ entry.http_request = unescape(result.http_request.text_value)
122
+ entry
123
+ end
124
+
125
+ # @param result [Treetop::Runtime::SyntaxNode]
126
+ # @return [ErrorEntry]
127
+ def create_error_entry(result)
128
+ entry = ErrorEntry.new
129
+
130
+ %i(frontend_name bind_name message).each do |field|
131
+ entry.send("#{field}=", result.send(field).text_value)
132
+ end
133
+
134
+ entry
135
+ end
106
136
  end
107
137
  end
@@ -1,12 +1,14 @@
1
1
  require 'haproxy_log_parser'
2
2
 
3
3
  RSpec.describe HAProxyLogParser do
4
- # TODO Use something better instead of LINES[0], LINES[1], ...
5
- LINES = IO.readlines(File.join(File.dirname(__FILE__), 'sample.log'))
4
+ LINES = Hash[
5
+ IO.readlines(File.join(File.dirname(__FILE__), 'sample.log')).
6
+ map { |line| line.split(',', 2) }
7
+ ]
6
8
 
7
9
  describe '.parse' do
8
- it 'parses LINE[0] correctly' do
9
- entry = HAProxyLogParser.parse(LINES[0])
10
+ it 'parses the good1 case correctly' do
11
+ entry = HAProxyLogParser.parse(LINES['good1'])
10
12
  expect(entry.client_ip).to eq('10.0.8.2')
11
13
  expect(entry.client_port).to eq(34028)
12
14
  expect(entry.accept_date).to eq(Time.local(2011, 8, 9, 20, 30, 46, 429))
@@ -36,8 +38,8 @@ RSpec.describe HAProxyLogParser do
36
38
  expect(entry.http_request).to eq('GET / HTTP/1.1')
37
39
  end
38
40
 
39
- it 'parses LINES[1] correctly' do
40
- entry = HAProxyLogParser.parse(LINES[1])
41
+ it 'parses the good2 case correctly' do
42
+ entry = HAProxyLogParser.parse(LINES['good2'])
41
43
  expect(entry.client_ip).to eq('192.168.1.215')
42
44
  expect(entry.client_port).to eq(50679)
43
45
  expect(entry.accept_date).to eq(Time.local(2012, 5, 21, 1, 35, 46, 146))
@@ -67,6 +69,16 @@ RSpec.describe HAProxyLogParser do
67
69
  expect(entry.http_request).to eq('GET /images/image.gif HTTP/1.1')
68
70
  end
69
71
 
72
+ it 'parses connection error lines correctly' do
73
+ entry = HAProxyLogParser.parse(LINES['error1'])
74
+ expect(entry.client_ip).to eq('127.0.0.1')
75
+ expect(entry.client_port).to eq(56059)
76
+ expect(entry.accept_date).to eq(Time.local(2012, 12, 3, 17, 35, 10, 380))
77
+ expect(entry.frontend_name).to eq('frt')
78
+ expect(entry.bind_name).to eq('f1')
79
+ expect(entry.message).to eq('Connection error during SSL handshake')
80
+ end
81
+
70
82
  it 'raises ParseError if the line is invalid' do
71
83
  line = 'Aug 9 20:30:46 localhost haproxy[2022]: '
72
84
  expect { HAProxyLogParser.parse(line) }.
data/spec/sample.log CHANGED
@@ -1,2 +1,3 @@
1
- Aug 9 20:30:46 localhost haproxy[2022]: 10.0.8.2:34028 [09/Aug/2011:20:30:46.429] proxy-out~ proxy-out/cache1 1/0/2/126/+128 301 +223 - - LR-- 617/523/336/168/0 0/0 {www.sytadin.equipement.gouv.fr||http://trafic.1wt.eu/} {Apache|230|||http://www.sytadin.} "GET / HTTP/1.1"
2
- May 21 01:35:46 10.18.237.5 haproxy[26747]: 192.168.1.215:50679 [21/May/2012:01:35:46.146] webapp webapp_backend/web09 27/0/1/0/217 200 1367 session=abc session=xyz ---- 600/529/336/158/0 0/0 {#7C#7C #7B5F41#7D|http://google.com/|} "GET /images/image.gif HTTP/1.1"
1
+ good1,Aug 9 20:30:46 localhost haproxy[2022]: 10.0.8.2:34028 [09/Aug/2011:20:30:46.429] proxy-out~ proxy-out/cache1 1/0/2/126/+128 301 +223 - - LR-- 617/523/336/168/0 0/0 {www.sytadin.equipement.gouv.fr||http://trafic.1wt.eu/} {Apache|230|||http://www.sytadin.} "GET / HTTP/1.1"
2
+ good2,May 21 01:35:46 10.18.237.5 haproxy[26747]: 192.168.1.215:50679 [21/May/2012:01:35:46.146] webapp webapp_backend/web09 27/0/1/0/217 200 1367 session=abc session=xyz ---- 600/529/336/158/0 0/0 {#7C#7C #7B5F41#7D|http://google.com/|} "GET /images/image.gif HTTP/1.1"
3
+ error1,Dec 3 18:27:14 localhost haproxy[6103]: 127.0.0.1:56059 [03/Dec/2012:17:35:10.380] frt/f1: Connection error during SSL handshake
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haproxy_log_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toby Hsieh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-03 00:00:00.000000000 Z
11
+ date: 2017-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: treetop
@@ -44,12 +44,15 @@ executables: []
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
+ - ".gitignore"
48
+ - Gemfile
47
49
  - LICENSE
48
50
  - README.md
49
51
  - VERSION
50
52
  - haproxy_log_parser.gemspec
51
53
  - lib/haproxy_log_parser.rb
52
54
  - lib/haproxy_log_parser/entry.rb
55
+ - lib/haproxy_log_parser/error_entry.rb
53
56
  - lib/haproxy_log_parser/line.treetop
54
57
  - spec/haproxy_log_parser_spec.rb
55
58
  - spec/sample.log
@@ -73,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
76
  version: '0'
74
77
  requirements: []
75
78
  rubyforge_project:
76
- rubygems_version: 2.5.1
79
+ rubygems_version: 2.6.8
77
80
  signing_key:
78
81
  specification_version: 4
79
82
  summary: Parser for HAProxy logs in the HTTP log format