log_parser 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +7 -0
- data/lib/log_parser/client.rb +157 -0
- data/lib/log_parser/line_item.rb +19 -0
- data/lib/log_parser/version.rb +2 -2
- data/lib/log_parser.rb +8 -153
- data/test/log_parser/client_test.rb +13 -0
- data/test/log_parser/line_item_test.rb +10 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb1f9e6441835510f872d57775b0a110af39da64
|
4
|
+
data.tar.gz: f66ab92bb2d953afd409289012f6ab55996f8192
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28f051e09df01a61e4030debadd7ccdff952a3e7e5db684f9edb92d0fd8959c96d8d9ca4daa7bb56905ffa0c1156d65ae1e61b76fd34a43afea918f378cbc4aa
|
7
|
+
data.tar.gz: 66e4fab78c195b9f1fe2622b1059e0ddc1da9b78f6d8dec9fce3b678cb1a30697fcf2f5ed2516550e720d372b76d99c11aabf65bfb028d26a4164524a6e6c164
|
data/Rakefile
CHANGED
@@ -0,0 +1,157 @@
|
|
1
|
+
module LogParser
|
2
|
+
class Client
|
3
|
+
#
|
4
|
+
# = Class
|
5
|
+
#
|
6
|
+
# Filter lines from the log file by chaining methods together:
|
7
|
+
#
|
8
|
+
# log = LogParser::Client.new('open_table.log')
|
9
|
+
# log.errors.by_message('authentication failed').since(1.day.ago)
|
10
|
+
# #=> ["[2014-11-13T23:12:14-07:00] ERROR [page_id 95239] Authentication failed with token ..."]
|
11
|
+
#
|
12
|
+
|
13
|
+
LINE_PATTERN = %r{
|
14
|
+
\[(\d+-\d+-\d+T\d+:\d+:\d+-\d+:\d+)\] # timestamp
|
15
|
+
(\s(\w+):)? # type of message (ERROR, WARNING, INFO)
|
16
|
+
(\s\[(.+)\])? # prefix (introduced by log.rb)
|
17
|
+
\s(.+)$ # message body
|
18
|
+
}x
|
19
|
+
|
20
|
+
attr_reader :file
|
21
|
+
attr_writer :lines
|
22
|
+
|
23
|
+
#
|
24
|
+
# @param [String|Pathname] log name of the file in 'log' directory or a Pathname object
|
25
|
+
# @param [Hash] options optional parameters
|
26
|
+
# @option :line_items is an array of LineItem objects
|
27
|
+
# @option :pattern a custom pattern to use for matching lines
|
28
|
+
#
|
29
|
+
def initialize(log = '', options = {})
|
30
|
+
@file = log.is_a?(String) ? LogParser.path_for(log) : log
|
31
|
+
@lines = options.fetch :line_items, []
|
32
|
+
@pattern = options.fetch :pattern, LINE_PATTERN
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Chainable
|
37
|
+
#
|
38
|
+
|
39
|
+
def errors
|
40
|
+
by_type('ERROR')
|
41
|
+
end
|
42
|
+
|
43
|
+
def warnings
|
44
|
+
by_type('WARNING')
|
45
|
+
end
|
46
|
+
|
47
|
+
def infos
|
48
|
+
by_type('INFO')
|
49
|
+
end
|
50
|
+
|
51
|
+
def since(timestamp)
|
52
|
+
chain do |items|
|
53
|
+
for line in lines
|
54
|
+
items << line if DateTime.parse(line.timestamp) > timestamp
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def by_message(text)
|
60
|
+
chain do |items|
|
61
|
+
for line in lines
|
62
|
+
items << line if line.message =~ Regexp.new(text, Regexp::IGNORECASE)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def by_prefix(name)
|
68
|
+
chain do |items|
|
69
|
+
for line in lines
|
70
|
+
items << line if line.prefix == name
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def by_type(name)
|
76
|
+
chain do |items|
|
77
|
+
for line in lines
|
78
|
+
items << line if line.type == name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Non-chainable Helpers
|
85
|
+
#
|
86
|
+
|
87
|
+
def prefixes
|
88
|
+
items = Set.new
|
89
|
+
lines.each { |line| items << line.prefix }
|
90
|
+
items.to_a.compact
|
91
|
+
end
|
92
|
+
|
93
|
+
def uniq
|
94
|
+
lines.uniq(&:full_message)
|
95
|
+
end
|
96
|
+
|
97
|
+
def timestamps
|
98
|
+
lines.map(&:timestamp)
|
99
|
+
end
|
100
|
+
|
101
|
+
def messages
|
102
|
+
lines.map(&:message)
|
103
|
+
end
|
104
|
+
|
105
|
+
def strings
|
106
|
+
lines.map(&:to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
def count
|
110
|
+
lines.count
|
111
|
+
end
|
112
|
+
|
113
|
+
def sort
|
114
|
+
lines.sort
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Default overrides
|
119
|
+
#
|
120
|
+
|
121
|
+
def to_s
|
122
|
+
Array(@lines).map(&:to_s).to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
alias inspect to_s
|
126
|
+
|
127
|
+
#
|
128
|
+
# Private
|
129
|
+
#
|
130
|
+
|
131
|
+
def scan
|
132
|
+
line_items = []
|
133
|
+
File.open(file) do |f|
|
134
|
+
line = nil
|
135
|
+
begin
|
136
|
+
line = f.gets
|
137
|
+
line_items << LineItem.new($1, $3, $5, $6) if line =~ @pattern
|
138
|
+
end while line
|
139
|
+
end
|
140
|
+
line_items
|
141
|
+
end
|
142
|
+
|
143
|
+
def chain
|
144
|
+
items = []
|
145
|
+
yield items
|
146
|
+
self.class.new(file, line_items: items)
|
147
|
+
end
|
148
|
+
|
149
|
+
def lines
|
150
|
+
@lines = scan if @lines.nil? || @lines.empty?
|
151
|
+
@lines
|
152
|
+
end
|
153
|
+
|
154
|
+
alias to_a lines
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module LogParser
|
2
|
+
class LineItem < Struct.new(:timestamp, :type, :prefix, :message)
|
3
|
+
def to_s
|
4
|
+
s = "[#{timestamp}] "
|
5
|
+
s << "#{type}: " if type
|
6
|
+
s << "[#{prefix}] " if prefix
|
7
|
+
s << "#{message}"
|
8
|
+
s
|
9
|
+
end
|
10
|
+
|
11
|
+
def full_message
|
12
|
+
prefix ? "[#{prefix}] #{message}" : message
|
13
|
+
end
|
14
|
+
|
15
|
+
def <=>(other)
|
16
|
+
timestamp <=> other.timestamp
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/log_parser/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = '0.0
|
1
|
+
module LogParser
|
2
|
+
VERSION = '0.1.0'.freeze
|
3
3
|
end
|
data/lib/log_parser.rb
CHANGED
@@ -1,158 +1,13 @@
|
|
1
|
-
require
|
1
|
+
require 'log_parser/version'
|
2
|
+
require 'log_parser/line_item'
|
3
|
+
require 'log_parser/client'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
# Filter lines from the log file by chaining methods together:
|
8
|
-
#
|
9
|
-
# log = LogParser.new('open_table.log')
|
10
|
-
# log.errors.by_message('authentication failed').since(1.day.ago)
|
11
|
-
# #=> ["[2014-11-13T23:12:14-07:00] ERROR [page_id 95239] Authentication failed with token ..."]
|
12
|
-
#
|
13
|
-
|
14
|
-
LINE_PATTERN = %r{
|
15
|
-
\[(\d+-\d+-\d+T\d+:\d+:\d+-\d+:\d+)\] # timestamp
|
16
|
-
(\s(\w+):)? # type of message (ERROR, WARNING, INFO)
|
17
|
-
(\s\[(.+)\])? # prefix (introduced by log.rb)
|
18
|
-
\s(.+)$ # message body
|
19
|
-
}x
|
20
|
-
|
21
|
-
attr_reader :file_path
|
22
|
-
attr_writer :lines
|
23
|
-
|
24
|
-
# @param log [String|Pathname] The file name in the log directory or a pathname to a log file
|
25
|
-
# @param line_items [Array] (optional) An array of LineItems
|
26
|
-
def initialize(log = '', line_items = [])
|
27
|
-
@file_path = log.is_a?(String) ? Rails.root.join('log', log) : log
|
28
|
-
@lines = line_items
|
29
|
-
end
|
30
|
-
|
31
|
-
#
|
32
|
-
# Chainable
|
33
|
-
#
|
34
|
-
|
35
|
-
def errors
|
36
|
-
by_type('ERROR')
|
37
|
-
end
|
38
|
-
|
39
|
-
def warnings
|
40
|
-
by_type('WARNING')
|
41
|
-
end
|
42
|
-
|
43
|
-
def infos
|
44
|
-
by_type('INFO')
|
45
|
-
end
|
46
|
-
|
47
|
-
def by_message(text)
|
48
|
-
chain do |items|
|
49
|
-
for line in lines
|
50
|
-
items << line if line.message =~ Regexp.new(text, Regexp::IGNORECASE)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def since(timestamp)
|
56
|
-
chain do |items|
|
57
|
-
for line in lines
|
58
|
-
items << line if DateTime.parse(line.timestamp) > timestamp
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def by_prefix(name)
|
64
|
-
chain do |items|
|
65
|
-
for line in lines
|
66
|
-
items << line if line.prefix == name
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def by_type(name)
|
72
|
-
chain do |items|
|
73
|
-
for line in lines
|
74
|
-
items << line if line.type == name
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
#
|
80
|
-
# Helpers
|
81
|
-
#
|
82
|
-
|
83
|
-
def prefixes
|
84
|
-
items = Set.new
|
85
|
-
lines.each { |line| items << line.prefix }
|
86
|
-
items.to_a.compact
|
87
|
-
end
|
88
|
-
|
89
|
-
def timestamps
|
90
|
-
lines.map(&:timestamp)
|
91
|
-
end
|
92
|
-
|
93
|
-
def messages
|
94
|
-
lines.map(&:message)
|
95
|
-
end
|
96
|
-
|
97
|
-
def strings
|
98
|
-
lines.map(&:to_s)
|
99
|
-
end
|
100
|
-
|
101
|
-
def count
|
102
|
-
lines.count
|
103
|
-
end
|
104
|
-
|
105
|
-
def uniq
|
106
|
-
lines.uniq { |line| line.full_message }
|
107
|
-
end
|
108
|
-
|
109
|
-
def to_s
|
110
|
-
Array(@lines).map(&:to_s).to_s
|
5
|
+
module LogParser
|
6
|
+
def self.root
|
7
|
+
File.expand_path('../..', __FILE__)
|
111
8
|
end
|
112
9
|
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
# Private
|
117
|
-
#
|
118
|
-
|
119
|
-
def scan
|
120
|
-
line_items = []
|
121
|
-
File.open(file_path) do |f|
|
122
|
-
while line = f.gets
|
123
|
-
line_items << LineItem.new($1, $3, $5, $6) if line =~ LINE_PATTERN
|
124
|
-
end
|
125
|
-
end
|
126
|
-
line_items
|
127
|
-
end
|
128
|
-
|
129
|
-
def chain
|
130
|
-
items = []
|
131
|
-
yield items
|
132
|
-
self.class.new(file_path, items)
|
133
|
-
end
|
134
|
-
|
135
|
-
def lines
|
136
|
-
@lines.presence || scan
|
137
|
-
end
|
138
|
-
|
139
|
-
alias to_a lines
|
140
|
-
|
141
|
-
class LineItem < Struct.new(:timestamp, :type, :prefix, :message)
|
142
|
-
def to_s
|
143
|
-
s = "[#{timestamp}] "
|
144
|
-
s << "#{type}: " if type
|
145
|
-
s << "[#{prefix}] " if prefix
|
146
|
-
s << "#{message}"
|
147
|
-
s
|
148
|
-
end
|
149
|
-
|
150
|
-
def full_message
|
151
|
-
prefix ? "[#{prefix}] #{message}" : message
|
152
|
-
end
|
153
|
-
|
154
|
-
def <=>(other)
|
155
|
-
timestamp <=> other.timestamp
|
156
|
-
end
|
10
|
+
def self.path_for(file)
|
11
|
+
Pathname.new(File.join(root, 'log', file))
|
157
12
|
end
|
158
13
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'log_parser'
|
4
|
+
|
5
|
+
class LogParser::ClientTest < MiniTest::Unit::TestCase
|
6
|
+
|
7
|
+
def test_initialize_with_string
|
8
|
+
@log = LogParser::Client.new('production.log')
|
9
|
+
assert_kind_of Pathname, @log.file_path
|
10
|
+
assert_equal '/log', @log.file_path.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: log_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Buckley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -51,8 +51,12 @@ files:
|
|
51
51
|
- README.md
|
52
52
|
- Rakefile
|
53
53
|
- lib/log_parser.rb
|
54
|
+
- lib/log_parser/client.rb
|
55
|
+
- lib/log_parser/line_item.rb
|
54
56
|
- lib/log_parser/version.rb
|
55
57
|
- log_parser.gemspec
|
58
|
+
- test/log_parser/client_test.rb
|
59
|
+
- test/log_parser/line_item_test.rb
|
56
60
|
homepage: https://github.com/ridiculous
|
57
61
|
licenses:
|
58
62
|
- MIT
|
@@ -77,4 +81,6 @@ rubygems_version: 2.2.2
|
|
77
81
|
signing_key:
|
78
82
|
specification_version: 4
|
79
83
|
summary: Easily search log files
|
80
|
-
test_files:
|
84
|
+
test_files:
|
85
|
+
- test/log_parser/client_test.rb
|
86
|
+
- test/log_parser/line_item_test.rb
|