indy 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|