haproxy_log_parser 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|