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 +4 -4
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/README.md +3 -0
- data/VERSION +1 -1
- data/haproxy_log_parser.gemspec +2 -7
- data/lib/haproxy_log_parser/error_entry.rb +24 -0
- data/lib/haproxy_log_parser/line.treetop +14 -1
- data/lib/haproxy_log_parser.rb +64 -34
- data/spec/haproxy_log_parser_spec.rb +18 -6
- data/spec/sample.log +3 -2
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdee7790396094ba841e7a79fda38388fddd31a6
|
4
|
+
data.tar.gz: 77f1cfd3fbe289140fe605dc8092ac38def83fb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92830fae50828e5422a120884f24c7ce5c64c2e8904fd3c6f3846e1e3d5224e028b89243abffe28913ed78773a70cbd4c68816efdbac8b794078ea408ffa50a1
|
7
|
+
data.tar.gz: 64254fac5285600842b6dd81b480f3ad5e8428558e7d92ef231b2b5ef4c0ad9e41e91c9b67196b65980242b880235e7dbb4fb4fbfe730257fc8056000f48b1c1
|
data/.gitignore
ADDED
data/Gemfile
ADDED
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
|
+
2.0.0
|
data/haproxy_log_parser.gemspec
CHANGED
@@ -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 =
|
15
|
-
|
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
|
-
|
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
|
data/lib/haproxy_log_parser.rb
CHANGED
@@ -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
|
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 =
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
+
create_error_entry(result.suffix)
|
55
33
|
end
|
56
|
-
|
57
|
-
|
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
|
-
|
5
|
-
|
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
|
9
|
-
entry = HAProxyLogParser.parse(LINES[
|
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
|
40
|
-
entry = HAProxyLogParser.parse(LINES[
|
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:
|
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:
|
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.
|
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
|