log_parser 0.0.1 → 0.1.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/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
|