indy 0.1.1
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.
- data/.autotest +18 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/History.txt +11 -0
- data/README.md +132 -0
- data/Rakefile +68 -0
- data/autotest/discover.rb +2 -0
- data/cucumber.yml +6 -0
- data/features/after_time.feature +41 -0
- data/features/application.feature +33 -0
- data/features/around_time.feature +34 -0
- data/features/before_time.feature +34 -0
- data/features/custom_pattern.feature +52 -0
- data/features/exact_log_level.feature +35 -0
- data/features/exact_message.feature +33 -0
- data/features/exact_mulitple_fields.feature +38 -0
- data/features/exact_time.feature +28 -0
- data/features/file.feature +30 -0
- data/features/log_levels.feature +40 -0
- data/features/message.feature +39 -0
- data/features/multiple_fields.feature +38 -0
- data/features/step_definitions/find_by.steps.rb +55 -0
- data/features/step_definitions/log_file.steps.rb +8 -0
- data/features/step_definitions/support/env.rb +1 -0
- data/features/step_definitions/support/transforms.rb +28 -0
- data/features/step_definitions/test_setup.steps.rb +4 -0
- data/features/step_definitions/test_teardown.steps.rb +0 -0
- data/features/step_definitions/time.steps.rb +29 -0
- data/features/within_time.feature +41 -0
- data/indy.gemspec +61 -0
- data/lib/indy.rb +5 -0
- data/lib/indy/indy.rb +463 -0
- data/lib/indy/result_set.rb +8 -0
- data/performance/helper.rb +5 -0
- data/performance/profile_spec.rb +35 -0
- data/spec/data.log +2 -0
- data/spec/helper.rb +4 -0
- data/spec/indy_spec.rb +212 -0
- data/spec/result_set_spec.rb +9 -0
- data/spec/search_spec.rb +97 -0
- data/spec/time_spec.rb +80 -0
- metadata +126 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/helper"
|
2
|
+
|
3
|
+
describe "Search Performance" do
|
4
|
+
|
5
|
+
|
6
|
+
context "with a small data set" do
|
7
|
+
|
8
|
+
longer_subject = [
|
9
|
+
"2000-09-07 14:07:41 INFO MyApp - Entering application.\n",
|
10
|
+
"2000-09-07 14:07:42 DEBUG MyApp - Focusing application.\n",
|
11
|
+
"2000-09-07 14:07:43 DEBUG MyApp - Blurring application.\n",
|
12
|
+
"2000-09-07 14:07:44 WARN MyApp - Low on Memory.\n",
|
13
|
+
"2000-09-07 14:07:45 ERROR MyApp - Out of Memory.\n",
|
14
|
+
"2000-09-07 14:07:46 INFO MyApp - Exiting application.\n"
|
15
|
+
].collect {|line| line * 70 }.join
|
16
|
+
|
17
|
+
profile :file => STDOUT, :printer => :flat, :min_percent => 1 do
|
18
|
+
|
19
|
+
it "should perform well using #for(:all)" do
|
20
|
+
Indy.search(longer_subject.dup).for(:all)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should perform well using #for(:field => 'value')" do
|
24
|
+
Indy.search(longer_subject.dup).for(:severity => 'INFO')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should perform well using #time()" do
|
28
|
+
Indy.search(longer_subject.dup).after(:time => "2000-09-07 14:07:45").for(:all)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/spec/data.log
ADDED
data/spec/helper.rb
ADDED
data/spec/indy_spec.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/helper"
|
2
|
+
|
3
|
+
describe Indy do
|
4
|
+
|
5
|
+
context :initialize do
|
6
|
+
|
7
|
+
# http://log4r.rubyforge.org/rdoc/Log4r/rdoc/patternformatter.html
|
8
|
+
it "should accept a log4r pattern string without error" do
|
9
|
+
lambda { Indy.new(:pattern => ["(%d) (%i) (%c) - (%m)", :time, :info, :class, :message]) }.should_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
# http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
|
13
|
+
it "should accept a log4j pattern string without error" do
|
14
|
+
lambda { Indy.new(:pattern => ["%d [%M] %p %C{1} - %m", :time, :info, :class, :message])}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should not raise error with non-conforming data" do
|
18
|
+
@indy = Indy.new(:source => " \nfoobar\n\n baz", :pattern => ['([^\s]+) (\w+)', :time, :message])
|
19
|
+
lambda{ @indy.for(:all) }.should_not raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should accept time_format parameter" do
|
23
|
+
@indy = Indy.new(:time_format => '%d-%m-%Y', :source => "1-13-2000 yes", :pattern => ['^([^\s]+) (\w+)$', :time, :message])
|
24
|
+
lambda{ @indy.for(:all) }.should_not raise_error
|
25
|
+
@indy.instance_variable_get(:@time_format).should == '%d-%m-%Y'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should accept an initialization hash passed to #search" do
|
29
|
+
hash = {:time_format => '%d-%m-%Y',
|
30
|
+
:source => "1-13-2000 yes",
|
31
|
+
:pattern => ['^([^\s]+) (\w+)$', :time, :message]}
|
32
|
+
lambda{ @indy = Indy.search( hash ) }.should_not raise_error
|
33
|
+
@indy.for(:all).count.should == 1
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'instance' do
|
40
|
+
|
41
|
+
before(:all) do
|
42
|
+
@indy = Indy.new(:source => '1/2/2002 string', :pattern => ['([^\s]+) (\w+)', :time, :message])
|
43
|
+
end
|
44
|
+
|
45
|
+
context "method" do
|
46
|
+
|
47
|
+
it "parse_line() should return a hash" do
|
48
|
+
@indy.send(:parse_line, "1/2/2002 string").class.should == Hash
|
49
|
+
end
|
50
|
+
|
51
|
+
it "parse_line() should return :time and :message" do
|
52
|
+
hash = @indy.send(:parse_line, "1/2/2002 string")
|
53
|
+
hash[:time] == "1/2/2002"
|
54
|
+
hash[:message] == "string"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
context :search do
|
62
|
+
|
63
|
+
it "should be a class method" do
|
64
|
+
Indy.should respond_to(:search)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should accept a string parameter" do
|
68
|
+
lambda{ Indy.search("String Log") }.should_not raise_error
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should accept a :cmd symbol and a command string parameter" do
|
72
|
+
lambda{ Indy.search(:cmd =>"ls") }.should_not raise_error
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should return an instance of Indy" do
|
76
|
+
Indy.search("source string").should be_kind_of(Indy)
|
77
|
+
Indy.search(:cmd => "ls").should be_kind_of(Indy)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "the instance should have the source specified" do
|
81
|
+
Indy.search("source string").source.should_not be_nil
|
82
|
+
Indy.search(:cmd => "ls").source.should_not be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
context "for a String" do
|
86
|
+
|
87
|
+
let(:log_file) { "#{File.dirname(__FILE__)}/data.log" }
|
88
|
+
|
89
|
+
context "treat it first like a file" do
|
90
|
+
|
91
|
+
it "should attempt to open the file" do
|
92
|
+
IO.should_receive(:open).with("possible_file.ext").ordered
|
93
|
+
Indy.search("possible_file.ext")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not throw an error for an invalid file" do
|
97
|
+
lambda { Indy.search("possible_file.ext") }.should_not raise_error
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should return an IO object when there is a file" do
|
101
|
+
IO.should_receive(:open).with("file_exists.ext").and_return(StringIO.new("2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION."))
|
102
|
+
Indy.search("file_exists.ext").for(:application => 'MyApp').length.should == 1
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should handle a real file" do
|
106
|
+
Indy.search(log_file).for(:application => 'MyApp').length.should == 2
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
context "treat it second like a string" do
|
112
|
+
|
113
|
+
it "should attempt to treat it as a string" do
|
114
|
+
expecting_string = mock("String With Expectation")
|
115
|
+
expecting_string.should_receive(:[])
|
116
|
+
expecting_string.should_receive(:to_s).and_return("2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.")
|
117
|
+
|
118
|
+
IO.should_receive(:open).with(expecting_string).ordered
|
119
|
+
|
120
|
+
Indy.search(expecting_string).for(:application => 'MyApp').length.should == 1
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
context "treat it optionally like a command" do
|
127
|
+
|
128
|
+
it "should attempt open the command" do
|
129
|
+
IO.stub!(:popen).with('ssh user@system "bash --login -c \"cat /var/log/standard.log\" "')
|
130
|
+
Indy.search(:cmd => 'ssh user@system "bash --login -c \"cat /var/log/standard.log\" "')
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not throw an error for an invalid command" do
|
134
|
+
IO.stub!(:popen).with('an invalid command').and_return('')
|
135
|
+
lambda { Indy.search(:cmd => "an invalid command") }.should_not raise_error
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should return an IO object upon a successful command" do
|
139
|
+
IO.stub!(:popen).with("a command").and_return(StringIO.new("2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION."))
|
140
|
+
Indy.search(:cmd => "a command").for(:application => 'MyApp').length.should == 1
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should handle a real command" do
|
144
|
+
Indy.search(:cmd => "cat #{log_file}").for(:application => 'MyApp').length.should == 2
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
context "instance" do
|
154
|
+
|
155
|
+
before(:each) do
|
156
|
+
log = "2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.\n \n2000-09-07 14:07:41 INFO MyApp Entering APPLICATION.\n2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.\n\n"
|
157
|
+
@indy = Indy.search(log)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "with() should be a method" do
|
161
|
+
@indy.should respond_to(:with)
|
162
|
+
end
|
163
|
+
|
164
|
+
# http://log4r.rubyforge.org/rdoc/Log4r/rdoc/patternformatter.html
|
165
|
+
it "with() should accept a log4r pattern string without error" do
|
166
|
+
lambda { @indy.with(["(%d) (%i) (%c) - (%m)", :time, :info, :class, :message]) }.should_not raise_error
|
167
|
+
end
|
168
|
+
|
169
|
+
# http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
|
170
|
+
it "with() should accept a log4j pattern string without error" do
|
171
|
+
lambda { @indy.with(["(%d) (%i) (%c) - (%m)", :time, :info, :class, :message])}.should_not raise_error
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should return itself" do
|
175
|
+
@indy.with(["(%d) (%i) (%c) - (%m)", :time, :info, :class, :message]).should == @indy
|
176
|
+
end
|
177
|
+
|
178
|
+
[:for, :search, :like, :matching].each do |method|
|
179
|
+
it "#{method}() should exist" do
|
180
|
+
@indy.should respond_to(method)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "#{method}() should accept a hash of search criteria" do
|
184
|
+
lambda { @indy.send(method,:severity => "INFO") }.should_not raise_error
|
185
|
+
end
|
186
|
+
|
187
|
+
it "#{method}() should return a set of results" do
|
188
|
+
@indy.send(method,:severity => "DEBUG").should be_kind_of(Array)
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
context "_search when given source, param and value" do
|
194
|
+
|
195
|
+
before(:each) do
|
196
|
+
@results = @indy.send(:_search, StringIO.new("2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION."),[Indy::DEFAULT_LOG_PATTERN, Indy::DEFAULT_LOG_FIELDS].flatten) {|result| result if result[:application] == "MyApp" }
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should not return nil" do
|
200
|
+
@results.should_not be_nil
|
201
|
+
@results.should be_kind_of(Array)
|
202
|
+
@results.should_not be_empty
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should return an array of results" do
|
206
|
+
@results.first[:application].should == "MyApp"
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
end
|
data/spec/search_spec.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/helper"
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe Indy do
|
5
|
+
|
6
|
+
context "search with string" do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
log_string = ["2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.",
|
10
|
+
"2000-09-07 14:08:41 INFO MyOtherApp - Exiting APPLICATION.",
|
11
|
+
"2000-09-07 14:10:55 INFO MyApp - Exiting APPLICATION."].join("\n")
|
12
|
+
@indy = Indy.search(log_string)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return 2 records" do
|
16
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should search entire string on each successive search" do
|
20
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
21
|
+
@indy.for(:severity => 'INFO').length.should == 3
|
22
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
context "search file" do
|
28
|
+
|
29
|
+
before(:all) do
|
30
|
+
@file = Tempfile.new('file_search_spec')
|
31
|
+
@file_path = @file.path
|
32
|
+
@file.write([ "2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.",
|
33
|
+
"2000-09-07 14:08:41 INFO MyOtherApp - Exiting APPLICATION.",
|
34
|
+
"2000-09-07 14:10:55 INFO MyApp - Exiting APPLICATION."
|
35
|
+
].join("\n"))
|
36
|
+
@file.flush
|
37
|
+
@indy = Indy.search(@file_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return 2 records" do
|
41
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should search entire file on each successive search" do
|
45
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
46
|
+
@indy.for(:severity => 'INFO').length.should == 3
|
47
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should search reopened file on each successive search" do
|
51
|
+
@file.write("\n2000-09-07 14:10:55 INFO MyApp - really really Exiting APPLICATION.\n")
|
52
|
+
@file.flush
|
53
|
+
@indy.for(:application => 'MyApp').length.should == 3
|
54
|
+
@indy.for(:severity => 'INFO').length.should == 4
|
55
|
+
@indy.for(:application => 'MyApp').length.should == 3
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
context "search using cmd" do
|
61
|
+
|
62
|
+
before(:all) do
|
63
|
+
@file = Tempfile.new('file_search_spec')
|
64
|
+
@file_path = @file.path
|
65
|
+
@file.write([ "2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.",
|
66
|
+
"2000-09-07 14:08:41 INFO MyOtherApp - Exiting APPLICATION.",
|
67
|
+
"2000-09-07 14:10:55 INFO MyApp - Exiting APPLICATION."
|
68
|
+
].join("\n"))
|
69
|
+
@file.flush
|
70
|
+
|
71
|
+
cmd = "ruby -e 'puts File.open(\"#{@file_path}\").read'"
|
72
|
+
|
73
|
+
@indy = Indy.search(:cmd => cmd)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should return 2 records" do
|
77
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should execute cmd on each successive search" do
|
81
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
82
|
+
@indy.for(:severity => 'INFO').length.should == 3
|
83
|
+
@indy.for(:application => 'MyApp').length.should == 2
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should execute cmd on each successive search" do
|
87
|
+
@file.write("\n2000-09-07 14:10:55 INFO MyApp - really really Exiting APPLICATION.\n")
|
88
|
+
@file.flush
|
89
|
+
@indy.for(:application => 'MyApp').length.should == 3
|
90
|
+
@indy.for(:severity => 'INFO').length.should == 4
|
91
|
+
@indy.for(:application => 'MyApp').length.should == 3
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/spec/time_spec.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/helper"
|
2
|
+
|
3
|
+
describe Indy do
|
4
|
+
|
5
|
+
context "default time handling" do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@indy = Indy.search("2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should parse a standard date" do
|
12
|
+
line_hash = {:time => "2000-09-07 14:07:41", :message => "Entering APPLICATION"}
|
13
|
+
@indy.send(:parse_date, line_hash).class.should == DateTime
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
context "non-default time handling" do
|
19
|
+
|
20
|
+
before(:all) do
|
21
|
+
pattern = "(\w+) (\d{4}-\d{2}-\d{2}) (\w+) - (.*)"
|
22
|
+
@indy = Indy.new(:source => "INFO 2000-09-07 MyApp - Entering APPLICATION.", :pattern => [pattern, :severity, :time, :application, :message])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should parse a non-standard date" do
|
26
|
+
line_hash = {:time => "2000/09/07", :message => "Entering APPLICATION"}
|
27
|
+
@indy.send(:parse_date, line_hash).class.should == DateTime
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
context "explicit time format" do
|
33
|
+
|
34
|
+
before(:each) do
|
35
|
+
pattern = "^([^\s]+) (.*)$"
|
36
|
+
@indy = Indy.new(:time_format => '%m-%d-%Y', :source => "1-13-2002 message\n1-14-2002 another message\n1-15-2002 another message", :pattern => [pattern, :time, :message])
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should parse a US style date when given a time format" do
|
40
|
+
line_hash = {:time => '1-13-2002', :message => 'message'}
|
41
|
+
@indy.send(:parse_date, line_hash).class.should == DateTime
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should accept standard time format searches even while using an explicit log time format" do
|
45
|
+
@indy.after(:time => 'Jan 13 2002').for(:all).count.should == 2
|
46
|
+
@indy.after(:time => 'Jan 14 2002').for(:all).last._time.mday.should == 15
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
context "built-in _time field" do
|
52
|
+
|
53
|
+
before(:all) do
|
54
|
+
log_string = ["2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.",
|
55
|
+
"2000-09-07 14:08:41 INFO MyApp - Exiting APPLICATION.",
|
56
|
+
"2000-09-07 14:10:55 INFO MyApp - Exiting APPLICATION."].join("\n")
|
57
|
+
@search_result = Indy.search(log_string).for(:application => 'MyApp')
|
58
|
+
@time_search_result = Indy.search(log_string).before(:time => "2100-09-07").for(:application => 'MyApp')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not exist as an attribute when unless performing a time search" do
|
62
|
+
@search_result.first._time.class.should == NilClass
|
63
|
+
@time_search_result.first._time.class.should == DateTime
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be accurate" do
|
67
|
+
@time_search_result.first._time.to_s.should == "2000-09-07T14:07:41+00:00"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should allow for time range calculations" do
|
71
|
+
time_span = @time_search_result.last._time - @time_search_result.first._time
|
72
|
+
hours,minutes,seconds,frac = Date.day_fraction_to_time( time_span )
|
73
|
+
hours.should == 0
|
74
|
+
minutes.should == 3
|
75
|
+
seconds.should == 14
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|