indy 0.4.0.pre → 0.5.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/.travis.yml +4 -4
- data/History.txt +9 -2
- data/README.md +75 -53
- data/features/{file.feature → file_object.feature} +1 -1
- data/features/file_path.feature +30 -0
- data/features/log_format_combined.feature +15 -0
- data/features/log_format_common.feature +19 -0
- data/features/step_definitions/find_by.steps.rb +1 -1
- data/features/step_definitions/log_file.steps.rb +10 -2
- data/features/step_definitions/support/transforms.rb +1 -1
- data/indy.gemspec +20 -21
- data/lib/indy/indy.rb +15 -13
- data/lib/indy/log_definition.rb +6 -4
- data/lib/indy/log_formats.rb +6 -5
- data/lib/indy/search.rb +28 -16
- data/lib/indy/source.rb +21 -5
- data/lib/indy/time.rb +7 -27
- data/lib/indy/version.rb +1 -1
- data/spec/indy_private_spec.rb +3 -3
- data/spec/indy_spec.rb +59 -59
- data/spec/indy_struct_spec.rb +6 -6
- data/spec/log_definition_spec.rb +11 -11
- data/spec/log_format_spec.rb +25 -13
- data/spec/search_spec.rb +15 -15
- data/spec/source_spec.rb +17 -17
- data/spec/time_scope_spec.rb +39 -34
- data/spec/time_spec.rb +7 -16
- data/spec/tmp/rspec_guard_result +1 -0
- metadata +115 -88
data/lib/indy/log_definition.rb
CHANGED
@@ -19,7 +19,7 @@ class Indy
|
|
19
19
|
@entry_regexp = Regexp.new(params_hash[:entry_regexp])
|
20
20
|
end
|
21
21
|
@entry_fields = params_hash[:entry_fields]
|
22
|
-
@time_format = params_hash[:time_format]
|
22
|
+
@time_format = params_hash[:time_format] || Indy::LogFormats::DEFAULT_DATE_TIME # '%Y-%m-%d %H:%M:%S'
|
23
23
|
define_struct
|
24
24
|
end
|
25
25
|
|
@@ -27,6 +27,7 @@ class Indy
|
|
27
27
|
params_hash = {}
|
28
28
|
params_hash[:entry_regexp] = Indy::LogFormats::DEFAULT_ENTRY_REGEXP
|
29
29
|
params_hash[:entry_fields] = Indy::LogFormats::DEFAULT_ENTRY_FIELDS
|
30
|
+
params_hash[:time_format] = Indy::LogFormats::DEFAULT_DATE_TIME
|
30
31
|
params_hash
|
31
32
|
end
|
32
33
|
|
@@ -54,7 +55,7 @@ class Indy
|
|
54
55
|
end
|
55
56
|
|
56
57
|
#
|
57
|
-
# Define Struct::Entry
|
58
|
+
# Define Struct::Entry. Ignore warnings.
|
58
59
|
#
|
59
60
|
def define_struct
|
60
61
|
fields = (@entry_fields + [:raw_entry]).sort_by{|key|key.to_s}
|
@@ -85,13 +86,14 @@ class Indy
|
|
85
86
|
match_data = /#{@entry_regexp}/.match(raw_entry)
|
86
87
|
return nil unless match_data
|
87
88
|
values = match_data.captures
|
89
|
+
values.shift if @multiline
|
88
90
|
entry_hash([raw_entry, values].flatten)
|
89
91
|
end
|
90
92
|
|
91
93
|
#
|
92
94
|
# Return a hash of field=>value pairs for the array of captured values from a log entry
|
93
95
|
#
|
94
|
-
# @param [Array] capture_array The array of values captured by the
|
96
|
+
# @param [Array] capture_array The array of values captured by the LogDefinition regexp
|
95
97
|
#
|
96
98
|
def parse_entry_captures( capture_array )
|
97
99
|
entire_entry = capture_array.shift
|
@@ -106,7 +108,7 @@ class Indy
|
|
106
108
|
if values.length == @entry_fields.length + 1 # values also includes raw_entry
|
107
109
|
@field_list_is_valid = true
|
108
110
|
else
|
109
|
-
raise
|
111
|
+
raise Indy::Source::FieldMismatchException, "Number of expected fields does not match those captured via the regexp pattern.\nThe expected fields are:\n=> #{@entry_fields.join("\n=> ")}\nThe log entry and captured fields are:\n=> #{values.join("\n=> ")}"
|
110
112
|
end
|
111
113
|
end
|
112
114
|
|
data/lib/indy/log_formats.rb
CHANGED
@@ -17,17 +17,18 @@ class Indy
|
|
17
17
|
NUMBER_REGEXP = '(\d+)'
|
18
18
|
DQUOTE_DELIM_REGEXP = '"([^"]+)"'
|
19
19
|
|
20
|
-
DEFAULT_DATE_TIME = '\d{4}.\d{2}.\d{2}\s+\d{2}.\d{2}.\d{2}'
|
20
|
+
DEFAULT_DATE_TIME = '\d{4}.\d{2}.\d{2}\s+\d{2}.\d{2}.\d{2}' # "%Y-%m-%d %H:%M:%S"
|
21
21
|
DEFAULT_SEVERITY = [:trace,:debug,:info,:warn,:error,:fatal]
|
22
22
|
DEFAULT_SEVERITY_PATTERN = "(?:#{DEFAULT_SEVERITY.map{|s| s.to_s.upcase}.join("|")})"
|
23
23
|
DEFAULT_APPLICATION = '\w+'
|
24
24
|
DEFAULT_MESSAGE = '.+'
|
25
25
|
|
26
|
-
DEFAULT_ENTRY_FIELDS = [:time
|
26
|
+
DEFAULT_ENTRY_FIELDS = [:time, :severity, :application, :message]
|
27
27
|
DEFAULT_ENTRY_REGEXP = /^(#{DEFAULT_DATE_TIME})\s+(#{DEFAULT_SEVERITY_PATTERN})\s+(#{DEFAULT_APPLICATION})\s+-\s+(#{DEFAULT_MESSAGE})$/
|
28
28
|
|
29
29
|
COMMON_FIELDS = [:host, :ident, :authuser, :time, :request, :status, :bytes]
|
30
30
|
COMMON_REGEXP = /^#{IPV4_REGEXP} #{SPACE_DELIM_REGEXP} #{SPACE_DELIM_REGEXP} #{BRACKET_DELIM_REGEXP} #{DQUOTE_DELIM_REGEXP} #{HTTP_STATUS_REGEXP} #{NUMBER_REGEXP}$/
|
31
|
+
COMMON_TIME_FORMAT = "%d\/%b\/%Y:%H:%M:%S %z"
|
31
32
|
|
32
33
|
COMBINED_FIELDS = COMMON_FIELDS + [:referrer, :user_agent]
|
33
34
|
COMBINED_REGEXP = /^#{IPV4_REGEXP} #{SPACE_DELIM_REGEXP} #{SPACE_DELIM_REGEXP} #{BRACKET_DELIM_REGEXP} #{DQUOTE_DELIM_REGEXP} #{HTTP_STATUS_REGEXP} #{NUMBER_REGEXP} #{DQUOTE_DELIM_REGEXP} #{DQUOTE_DELIM_REGEXP}$/
|
@@ -42,7 +43,7 @@ class Indy
|
|
42
43
|
#
|
43
44
|
# Indy default log format
|
44
45
|
# e.g.:
|
45
|
-
#
|
46
|
+
# 2000-09-07 INFO MyApp - Entering APPLICATION.
|
46
47
|
#
|
47
48
|
DEFAULT_LOG_FORMAT = {:entry_regexp => LogFormats::DEFAULT_ENTRY_REGEXP, :entry_fields => LogFormats::DEFAULT_ENTRY_FIELDS}
|
48
49
|
|
@@ -59,11 +60,11 @@ class Indy
|
|
59
60
|
#
|
60
61
|
# NCSA Common Log Format log format
|
61
62
|
#
|
62
|
-
COMMON_LOG_FORMAT = {:entry_regexp => LogFormats::COMMON_REGEXP, :entry_fields => LogFormats::COMMON_FIELDS}
|
63
|
+
COMMON_LOG_FORMAT = {:entry_regexp => LogFormats::COMMON_REGEXP, :entry_fields => LogFormats::COMMON_FIELDS, :time_format => LogFormats::COMMON_TIME_FORMAT}
|
63
64
|
|
64
65
|
#
|
65
66
|
# NCSA Combined Log Format log format
|
66
67
|
#
|
67
|
-
COMBINED_LOG_FORMAT = {:entry_regexp => LogFormats::COMBINED_REGEXP, :entry_fields => LogFormats::COMBINED_FIELDS}
|
68
|
+
COMBINED_LOG_FORMAT = {:entry_regexp => LogFormats::COMBINED_REGEXP, :entry_fields => LogFormats::COMBINED_FIELDS, :time_format => LogFormats::COMMON_TIME_FORMAT}
|
68
69
|
|
69
70
|
end
|
data/lib/indy/search.rb
CHANGED
@@ -3,11 +3,10 @@ class Indy
|
|
3
3
|
class Search
|
4
4
|
|
5
5
|
attr_accessor :source
|
6
|
-
attr_accessor :log_definition
|
7
6
|
|
8
7
|
attr_accessor :start_time, :end_time, :inclusive
|
9
8
|
|
10
|
-
def initialize(params_hash)
|
9
|
+
def initialize(params_hash={})
|
11
10
|
while (param = params_hash.shift) do
|
12
11
|
send("#{param.first}=",param.last)
|
13
12
|
end
|
@@ -25,7 +24,7 @@ class Indy
|
|
25
24
|
results = []
|
26
25
|
results += search do |entry|
|
27
26
|
if type == :all || is_match?(type,entry,search_criteria)
|
28
|
-
result_struct = @log_definition.create_struct(entry)
|
27
|
+
result_struct = @source.log_definition.create_struct(entry)
|
29
28
|
if block_given?
|
30
29
|
block.call(result_struct)
|
31
30
|
else
|
@@ -38,12 +37,12 @@ class Indy
|
|
38
37
|
|
39
38
|
#
|
40
39
|
# Search the @source and yield to the block the entry that was found
|
41
|
-
# with
|
40
|
+
# with the LogDefinition
|
42
41
|
#
|
43
42
|
# This method is supposed to be used internally.
|
44
43
|
#
|
45
44
|
def search(&block)
|
46
|
-
if @log_definition.multiline
|
45
|
+
if @source.log_definition.multiline
|
47
46
|
multiline_search(&block)
|
48
47
|
else
|
49
48
|
standard_search(&block)
|
@@ -58,9 +57,9 @@ class Indy
|
|
58
57
|
results = []
|
59
58
|
source_lines = (is_time_search ? @source.open([@start_time,@end_time]) : @source.open)
|
60
59
|
source_lines.each do |single_line|
|
61
|
-
hash = @log_definition.parse_entry(single_line)
|
60
|
+
hash = @source.log_definition.parse_entry(single_line)
|
62
61
|
next unless hash
|
63
|
-
next unless
|
62
|
+
next unless inside_time_window?(hash[:time],@start_time,@end_time,@inclusive) if is_time_search
|
64
63
|
results << (block.call(hash) if block_given?)
|
65
64
|
end
|
66
65
|
results.compact
|
@@ -72,10 +71,10 @@ class Indy
|
|
72
71
|
def multiline_search(&block)
|
73
72
|
is_time_search = use_time_criteria?
|
74
73
|
source_io = StringIO.new( (is_time_search ? @source.open([@start_time,@end_time]) : @source.open ).join("\n") )
|
75
|
-
results = source_io.read.scan(@log_definition.entry_regexp).collect do |entry|
|
76
|
-
hash = @log_definition.parse_entry_captures(entry)
|
74
|
+
results = source_io.read.scan(@source.log_definition.entry_regexp).collect do |entry|
|
75
|
+
hash = @source.log_definition.parse_entry_captures(entry)
|
77
76
|
next unless hash
|
78
|
-
next unless
|
77
|
+
next unless inside_time_window?(hash[:time],@start_time,@end_time,@inclusive) if is_time_search
|
79
78
|
block.call(hash) if block_given?
|
80
79
|
end
|
81
80
|
results.compact
|
@@ -87,8 +86,8 @@ class Indy
|
|
87
86
|
def use_time_criteria?
|
88
87
|
if @start_time || @end_time
|
89
88
|
# ensure both boundaries are set
|
90
|
-
@start_time ||= Indy::Time.forever_ago(@log_definition.time_format)
|
91
|
-
@end_time ||= Indy::Time.forever(@log_definition.time_format)
|
89
|
+
@start_time ||= Indy::Time.forever_ago(@source.log_definition.time_format)
|
90
|
+
@end_time ||= Indy::Time.forever(@source.log_definition.time_format)
|
92
91
|
end
|
93
92
|
@start_time && @end_time
|
94
93
|
end
|
@@ -114,8 +113,8 @@ class Indy
|
|
114
113
|
if params_hash[:time]
|
115
114
|
time_scope_from_direction(params_hash[:direction], params_hash[:span], params_hash[:time])
|
116
115
|
else
|
117
|
-
@start_time = Indy::Time.parse_date(params_hash[:start_time]) if params_hash[:start_time]
|
118
|
-
@end_time = Indy::Time.parse_date(params_hash[:end_time]) if params_hash[:end_time]
|
116
|
+
@start_time = Indy::Time.parse_date(params_hash[:start_time], @source.log_definition.time_format) if params_hash[:start_time]
|
117
|
+
@end_time = Indy::Time.parse_date(params_hash[:end_time], @source.log_definition.time_format) if params_hash[:end_time]
|
119
118
|
end
|
120
119
|
@inclusive = params_hash[:inclusive]
|
121
120
|
end
|
@@ -124,7 +123,7 @@ class Indy
|
|
124
123
|
# Parse direction, span, and time to set @start_time and @end_time
|
125
124
|
#
|
126
125
|
def time_scope_from_direction(direction, span, time)
|
127
|
-
|
126
|
+
time = Indy::Time.parse_date(time, @source.log_definition.time_format)
|
128
127
|
span = (span.to_i * 60).seconds if span
|
129
128
|
if direction == :before
|
130
129
|
@end_time = time
|
@@ -142,6 +141,19 @@ class Indy
|
|
142
141
|
@inclusive = @start_time = @end_time = nil
|
143
142
|
end
|
144
143
|
|
145
|
-
|
144
|
+
#
|
145
|
+
# Evaluate if a log entry satisfies the configured time conditions
|
146
|
+
#
|
147
|
+
def inside_time_window?(time_string,start_time,end_time,inclusive)
|
148
|
+
time = Indy::Time.parse_date(time_string, @source.log_definition.time_format)
|
149
|
+
return false unless time
|
150
|
+
if inclusive
|
151
|
+
return true unless (time > end_time || time < start_time)
|
152
|
+
else
|
153
|
+
return true unless (time >= end_time || time <= start_time)
|
154
|
+
end
|
155
|
+
return false
|
156
|
+
end
|
146
157
|
|
158
|
+
end
|
147
159
|
end
|
data/lib/indy/source.rb
CHANGED
@@ -5,6 +5,12 @@ class Indy
|
|
5
5
|
#
|
6
6
|
class Source
|
7
7
|
|
8
|
+
# Exception raised when entry regexp does not match data
|
9
|
+
class FieldMismatchException < Exception; end
|
10
|
+
|
11
|
+
# log definition
|
12
|
+
attr_accessor :log_definition
|
13
|
+
|
8
14
|
# log source type. :cmd, :file, or :string
|
9
15
|
attr_reader :type
|
10
16
|
|
@@ -14,9 +20,6 @@ class Indy
|
|
14
20
|
# the StringIO object
|
15
21
|
attr_reader :io
|
16
22
|
|
17
|
-
# log definition
|
18
|
-
attr_reader :log_definition
|
19
|
-
|
20
23
|
# Exception raised when unable to open source
|
21
24
|
class Invalid < Exception; end
|
22
25
|
|
@@ -27,7 +30,7 @@ class Indy
|
|
27
30
|
#
|
28
31
|
def initialize(param,log_definition=nil)
|
29
32
|
raise Indy::Source::Invalid, "No source specified." if param.nil?
|
30
|
-
|
33
|
+
self.log_definition = log_definition || LogDefinition.new()
|
31
34
|
return discover_connection(param) unless param.respond_to?(:keys)
|
32
35
|
if param[:cmd]
|
33
36
|
set_connection(:cmd, param[:cmd])
|
@@ -155,7 +158,20 @@ class Indy
|
|
155
158
|
# Return the time of a log entry index, with an optional offset
|
156
159
|
#
|
157
160
|
def time_at(index, delta=0)
|
158
|
-
|
161
|
+
begin
|
162
|
+
entry = @entries[index + delta]
|
163
|
+
time = @log_definition.parse_entry(entry)[:time]
|
164
|
+
result = Indy::Time.parse_date(time, @log_definition.time_format)
|
165
|
+
rescue FieldMismatchException => fme
|
166
|
+
raise
|
167
|
+
rescue Exception => e
|
168
|
+
msg = "Unable to parse time from entry. Time value was #{time.inspect}. Original exception was:\n#{e.class}\n"
|
169
|
+
raise Indy::Time::ParseException, msg + e.message
|
170
|
+
end
|
171
|
+
if result.nil?
|
172
|
+
raise Indy::Time::ParseException, "Unable to parse datetime. Raw value was #{time.inspect}. Entry was #{entry}."
|
173
|
+
end
|
174
|
+
result
|
159
175
|
end
|
160
176
|
|
161
177
|
#
|
data/lib/indy/time.rb
CHANGED
@@ -1,38 +1,35 @@
|
|
1
|
+
require 'active_support'
|
1
2
|
require 'active_support/core_ext'
|
2
3
|
|
3
4
|
class Indy
|
4
5
|
|
5
6
|
class Time
|
6
7
|
|
8
|
+
# Exception raised when unable to parse an entry's time value
|
9
|
+
class ParseException < Exception; end
|
10
|
+
|
7
11
|
class << self
|
8
12
|
|
9
13
|
#
|
10
14
|
# Return a valid DateTime object for the log entry string or hash
|
11
15
|
#
|
12
|
-
# @param [String, Hash]
|
16
|
+
# @param [String, Hash] time The log entry string or hash
|
17
|
+
#
|
18
|
+
# @param [String] time_format String of #strftime directives to define format
|
13
19
|
#
|
14
20
|
def parse_date(time,time_format=nil)
|
15
21
|
return time if time.kind_of? ::Time or time.kind_of? DateTime
|
16
22
|
if time_format
|
17
23
|
begin
|
18
|
-
# Attempt the appropriate parse method
|
19
24
|
DateTime.strptime(time, time_format)
|
20
25
|
rescue
|
21
|
-
# If appropriate, fall back to simple parse method
|
22
26
|
DateTime.parse(time) rescue nil
|
23
27
|
end
|
24
28
|
else
|
25
29
|
begin
|
26
|
-
# If appropriate, fall back to simple parse method
|
27
30
|
::Time.parse(time)
|
28
31
|
rescue => e
|
29
32
|
raise "Failed to create time object. The error was: #{e.message}"
|
30
|
-
#begin
|
31
|
-
# # one last try!!
|
32
|
-
# DateTime.parse(time)
|
33
|
-
#rescue Exception => e
|
34
|
-
# raise "Failed to create time object. The error was: #{e.message}"
|
35
|
-
#end
|
36
33
|
end
|
37
34
|
end
|
38
35
|
end
|
@@ -56,23 +53,6 @@ class Indy
|
|
56
53
|
end
|
57
54
|
end
|
58
55
|
|
59
|
-
#
|
60
|
-
# Evaluate if a log entry satisfies the configured time conditions
|
61
|
-
#
|
62
|
-
# @param [Hash] entry_hash The log entry's hash
|
63
|
-
#
|
64
|
-
def inside_time_window?(time_string,start_time,end_time,inclusive)
|
65
|
-
time = Indy::Time.parse_date(time_string)
|
66
|
-
#return false unless time && entry_hash
|
67
|
-
if inclusive
|
68
|
-
true unless time > end_time or time < start_time
|
69
|
-
else
|
70
|
-
true unless time >= end_time or time <= start_time
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
56
|
end
|
75
|
-
|
76
57
|
end
|
77
|
-
|
78
58
|
end
|
data/lib/indy/version.rb
CHANGED
data/spec/indy_private_spec.rb
CHANGED
@@ -12,12 +12,12 @@ describe 'Indy' do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it "#last_entries should return an array of Struct::Entry object" do
|
15
|
-
@indy.send(:last_entries, 2).class.
|
16
|
-
@indy.send(:last_entries, 2).first.class.
|
15
|
+
expect(@indy.send(:last_entries, 2).class).to eq(Array)
|
16
|
+
expect(@indy.send(:last_entries, 2).first.class).to eq(Struct::Entry)
|
17
17
|
end
|
18
18
|
|
19
19
|
it "#last_entries should return correct Struct::Entry objects" do
|
20
|
-
@indy.send(:last_entries, 2).first.time.
|
20
|
+
expect(@indy.send(:last_entries, 2).first.time).to eq('2000-09-07 14:07:43')
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
data/spec/indy_spec.rb
CHANGED
@@ -7,20 +7,20 @@ describe 'Indy' do
|
|
7
7
|
it "should accept v0.3.4 initialization params" do
|
8
8
|
indy_obj = Indy.new(:source => "foo\nbar\n").with(Indy::DEFAULT_LOG_FORMAT)
|
9
9
|
search_obj = indy_obj.search
|
10
|
-
search_obj.log_definition.class.
|
11
|
-
search_obj.log_definition.entry_regexp.class.
|
12
|
-
search_obj.log_definition.entry_fields.class.
|
10
|
+
expect(search_obj.source.log_definition.class).to eq Indy::LogDefinition
|
11
|
+
expect(search_obj.source.log_definition.entry_regexp.class).to eq Regexp
|
12
|
+
expect(search_obj.source.log_definition.entry_fields.class).to eq Array
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should not raise error with non-conforming data" do
|
16
16
|
@indy = Indy.new(:source => " \nfoobar\n\n baz", :entry_regexp => '([^\s]+) (\w+)', :entry_fields => [:time, :message])
|
17
|
-
@indy.all.class.
|
17
|
+
expect(@indy.all.class).to eq(Array)
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should accept time_format parameter" do
|
21
21
|
@indy = Indy.new(:time_format => '%d-%m-%Y', :source => "1-13-2000 yes", :entry_regexp => '^([^\s]+) (\w+)$', :entry_fields => [:time, :message])
|
22
|
-
@indy.all.class.
|
23
|
-
@indy.search.log_definition.time_format.
|
22
|
+
expect(@indy.all.class).to eq(Array)
|
23
|
+
expect(@indy.search.source.log_definition.time_format).to eq('%d-%m-%Y')
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should accept an initialization hash passed to #search" do
|
@@ -29,8 +29,8 @@ describe 'Indy' do
|
|
29
29
|
:entry_regexp => '^([^\s]+) (\w+)$',
|
30
30
|
:entry_fields => [:time, :message]}
|
31
31
|
@indy = Indy.search(hash)
|
32
|
-
@indy.class.
|
33
|
-
@indy.all.length.
|
32
|
+
expect(@indy.class).to eq(Indy)
|
33
|
+
expect(@indy.all.length).to eq(1)
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
@@ -47,49 +47,51 @@ describe 'Indy' do
|
|
47
47
|
context "method" do
|
48
48
|
|
49
49
|
it "#with should return self" do
|
50
|
-
@indy.with().class.
|
50
|
+
expect(@indy.with().class).to eq(Indy)
|
51
51
|
end
|
52
52
|
|
53
53
|
it "#with should use default log pattern when passed :default" do
|
54
|
-
@indy.with(:default).all.length.
|
54
|
+
expect(@indy.with(:default).all.length).to eq(3)
|
55
55
|
end
|
56
56
|
|
57
|
-
it "should raise
|
57
|
+
it "should raise Exception when regexp captures don't match fields" do
|
58
58
|
log = [ "2000-09-07 14:06:41 INFO MyApp - Entering APPLICATION.",
|
59
59
|
"2000-09-07 14:07:42 DEBUG MyApp - Initializing APPLICATION."].join("\n")
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
expect{
|
61
|
+
expect(Indy.search(log).
|
62
|
+
with(:entry_regexp => Indy::LogFormats::DEFAULT_ENTRY_REGEXP,
|
63
|
+
:entry_fields => [:field_one]
|
64
|
+
).all.length).to be > 0
|
65
|
+
}.to raise_error Indy::Source::FieldMismatchException
|
64
66
|
end
|
65
67
|
|
66
68
|
[:for, :like, :matching].each do |method|
|
67
69
|
it "##{method} should exist" do
|
68
|
-
@indy.
|
70
|
+
expect(@indy).to respond_to(method)
|
69
71
|
end
|
70
72
|
|
71
73
|
it "##{method} should accept a hash of search criteria" do
|
72
|
-
@indy.send(method,:severity => "INFO").
|
74
|
+
expect(@indy.send(method,:severity => "INFO")).to be_kind_of(Array)
|
73
75
|
end
|
74
76
|
|
75
77
|
it "##{method} should return a set of results" do
|
76
|
-
@indy.send(method,:severity => "DEBUG").
|
78
|
+
expect(@indy.send(method,:severity => "DEBUG")).to be_kind_of(Array)
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
80
82
|
it "#last should return self" do
|
81
|
-
@indy.last(:span => 1).
|
83
|
+
expect(@indy.last(:span => 1)).to be_kind_of Indy
|
82
84
|
end
|
83
85
|
|
84
86
|
it "#last should set the time scope to the correct number of minutes" do
|
85
|
-
@indy.last(:span => 1).all.count.
|
87
|
+
expect(@indy.last(:span => 1).all.count).to eq(2)
|
86
88
|
end
|
87
89
|
|
88
90
|
it "#last should raise an error if passed an invalid parameter" do
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
expect{ @indy.last('a') }.to raise_error( ArgumentError )
|
92
|
+
expect{ @indy.last() }.to raise_error( ArgumentError )
|
93
|
+
expect{ @indy.last(nil) }.to raise_error( ArgumentError )
|
94
|
+
expect{ @indy.last({}) }.to raise_error( ArgumentError )
|
93
95
|
end
|
94
96
|
|
95
97
|
end
|
@@ -98,39 +100,39 @@ describe 'Indy' do
|
|
98
100
|
context '#search' do
|
99
101
|
|
100
102
|
it "should be a class method" do
|
101
|
-
Indy.
|
103
|
+
expect(Indy).to respond_to(:search)
|
102
104
|
end
|
103
105
|
|
104
106
|
it "should accept a string parameter" do
|
105
|
-
Indy.search("String Log").class.
|
107
|
+
expect(Indy.search("String Log").class).to eq(Indy)
|
106
108
|
end
|
107
109
|
|
108
110
|
it "should accept a hash with :cmd key" do
|
109
|
-
Indy.search(:cmd => "ls").class.
|
111
|
+
expect(Indy.search(:cmd => "ls").class).to eq(Indy)
|
110
112
|
end
|
111
113
|
|
112
114
|
it "should accept a hash with :file => filepath" do
|
113
|
-
Indy.search(:file => "#{File.dirname(__FILE__)}/data.log").all.length.
|
115
|
+
expect(Indy.search(:file => "#{File.dirname(__FILE__)}/data.log").all.length).to eq(2)
|
114
116
|
end
|
115
117
|
|
116
118
|
it "should accept a hash with :file => File" do
|
117
|
-
Indy.search(:file => File.open("#{File.dirname(__FILE__)}/data.log")).all.length.
|
119
|
+
expect(Indy.search(:file => File.open("#{File.dirname(__FILE__)}/data.log")).all.length).to eq(2)
|
118
120
|
end
|
119
121
|
|
120
122
|
it "should accept a valid :source hash" do
|
121
|
-
Indy.search(:source => {:cmd => 'ls'}).class.
|
123
|
+
expect(Indy.search(:source => {:cmd => 'ls'}).class).to eq(Indy)
|
122
124
|
end
|
123
125
|
|
124
126
|
it "should create an instance of Indy::Source in Search object" do
|
125
|
-
Indy.search("source string").search.source.
|
127
|
+
expect(Indy.search("source string").search.source).to be_kind_of(Indy::Source)
|
126
128
|
end
|
127
129
|
|
128
130
|
it "should raise an exception when passed an invalid source: nil" do
|
129
|
-
|
131
|
+
expect{ Indy.search(nil) }.to raise_error(Indy::Source::Invalid, /No source specified/)
|
130
132
|
end
|
131
133
|
|
132
134
|
it "should raise an exception when the arity is incorrect" do
|
133
|
-
|
135
|
+
expect{ Indy.search( ) }.to raise_error Indy::Source::Invalid
|
134
136
|
end
|
135
137
|
|
136
138
|
context "with explicit source hash" do
|
@@ -142,48 +144,46 @@ describe 'Indy' do
|
|
142
144
|
end
|
143
145
|
|
144
146
|
it "should attempt open the command" do
|
145
|
-
IO.
|
147
|
+
allow(IO).to receive(:popen).with('ssh user@system "bash --login -c \"cat /var/log/standard.log\" "')
|
146
148
|
Indy.search(:cmd => 'ssh user@system "bash --login -c \"cat /var/log/standard.log\" "')
|
147
149
|
end
|
148
150
|
|
149
151
|
it "should not throw an error for an invalid command" do
|
150
|
-
IO.
|
151
|
-
Indy.search(:cmd => "an invalid command").class.
|
152
|
+
allow(IO).to receive(:popen).with('an invalid command').and_return('')
|
153
|
+
expect(Indy.search(:cmd => "an invalid command").class).to eq(Indy)
|
152
154
|
end
|
153
155
|
|
154
156
|
it "should use IO.popen on cmd value" do
|
155
|
-
IO.
|
156
|
-
Indy.search(:cmd => "a command").for(:application => 'MyApp').length.
|
157
|
+
allow(IO).to receive(:popen).with("a command").and_return(StringIO.new("2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION."))
|
158
|
+
expect(Indy.search(:cmd => "a command").for(:application => 'MyApp').length).to eq(1)
|
157
159
|
end
|
158
160
|
|
159
161
|
it "should handle a real command" do
|
160
162
|
log_file = "data.log"
|
161
163
|
cat_cmd = (is_windows? ? 'type' : 'cat')
|
162
|
-
Indy.search(:cmd => "#{cat_cmd} #{log_file}").for(:application => 'MyApp').length.
|
164
|
+
expect(Indy.search(:cmd => "#{cat_cmd} #{log_file}").for(:application => 'MyApp').length).to eq(2)
|
163
165
|
end
|
164
166
|
|
165
167
|
it "should raise Source::Invalid for an invalid command" do
|
166
|
-
IO.
|
167
|
-
|
168
|
+
allow(IO).to receive(:popen).with("zzzzzzzzzzzz").and_return('Invalid command')
|
169
|
+
expect{ Indy.search(:cmd => "zzzzzzzzzzzz").all }.to raise_error( Indy::Source::Invalid, /Unable to open log source/)
|
168
170
|
end
|
169
171
|
|
170
172
|
end
|
171
173
|
|
172
174
|
it "using :file" do
|
173
|
-
|
174
|
-
file = stub!(:size).and_return(1)
|
175
|
-
lambda{ Indy.search(:file => file) }
|
175
|
+
expect(Indy.search(:file => "#{File.dirname(__FILE__)}/data.log").all.length).to eq(2)
|
176
176
|
end
|
177
177
|
|
178
178
|
it "using :string" do
|
179
179
|
string = "2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION."
|
180
180
|
string_io = StringIO.new(string)
|
181
|
-
StringIO.
|
182
|
-
Indy.search(:string => string).for(:application => 'MyApp').length.
|
181
|
+
expect(StringIO).to receive(:new).with(string).ordered.and_return(string_io)
|
182
|
+
expect(Indy.search(:string => string).for(:application => 'MyApp').length).to eq(1)
|
183
183
|
end
|
184
184
|
|
185
185
|
it "should raise error when given an invalid key" do
|
186
|
-
|
186
|
+
expect{ Indy.search(:foo => "a string").all }.to raise_error( Indy::Source::Invalid )
|
187
187
|
end
|
188
188
|
|
189
189
|
end
|
@@ -196,7 +196,7 @@ describe 'Indy' do
|
|
196
196
|
log = [ "2000-09-07 14:06:41 INFO MyApp - Entering APPLICATION.",
|
197
197
|
"2000-09-07 14:07:42 DEBUG MyApp - Initializing APPLICATION.",
|
198
198
|
"2000-09-07 14:07:43 INFO MyApp - Exiting APPLICATION."].join("\n")
|
199
|
-
Indy.search(log).all.length.
|
199
|
+
expect(Indy.search(log).all.length).to eq(3)
|
200
200
|
end
|
201
201
|
|
202
202
|
it "should ignore invalid entries" do
|
@@ -204,13 +204,13 @@ describe 'Indy' do
|
|
204
204
|
"2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.\n",
|
205
205
|
" bad \n",
|
206
206
|
"2000-09-07 14:07:41 INFO MyApp - Entering APPLICATION.\n\n"].join("\n")
|
207
|
-
Indy.search(log).all.length.
|
207
|
+
expect(Indy.search(log).all.length).to eq(3)
|
208
208
|
end
|
209
209
|
|
210
210
|
it "should handle no matching entries" do
|
211
211
|
log = ["2000-09-07 MyApp - Entering APPLICATION.\n \n",
|
212
212
|
"2000-09-07 14:07:41\n"].join
|
213
|
-
Indy.search(log).all.length.
|
213
|
+
expect(Indy.search(log).all.length).to eq(0)
|
214
214
|
end
|
215
215
|
|
216
216
|
context "with explicit time format" do
|
@@ -223,7 +223,7 @@ describe 'Indy' do
|
|
223
223
|
:time_format => '%d-%Y-%m',
|
224
224
|
:entry_regexp => /^(\d\d-\d\d\d\d-\d\d)\s+(#{Indy::LogFormats::DEFAULT_SEVERITY_PATTERN})\s+(#{Indy::LogFormats::DEFAULT_APPLICATION})\s+-\s+(#{Indy::LogFormats::DEFAULT_MESSAGE})$/,
|
225
225
|
:entry_fields => Indy::LogFormats::DEFAULT_ENTRY_FIELDS)
|
226
|
-
indy.all.length.
|
226
|
+
expect(indy.all.length).to eq(2)
|
227
227
|
end
|
228
228
|
end
|
229
229
|
|
@@ -242,20 +242,20 @@ describe 'Indy' do
|
|
242
242
|
end
|
243
243
|
|
244
244
|
it "should return all entries using #all" do
|
245
|
-
@indy.all.count.
|
245
|
+
expect(@indy.all.count).to eq(5)
|
246
246
|
end
|
247
247
|
|
248
248
|
it "should return correct number of entries with #for" do
|
249
|
-
@indy.for(:severity => 'INFO').count.
|
249
|
+
expect(@indy.for(:severity => 'INFO').count).to eq(3)
|
250
250
|
end
|
251
251
|
|
252
252
|
it "should return correct number of entries with #like" do
|
253
|
-
@indy.like(:message => 'ntering').count.
|
253
|
+
expect(@indy.like(:message => 'ntering').count).to eq(2)
|
254
254
|
end
|
255
255
|
|
256
256
|
it "should return correct number of entries using #after time scope" do
|
257
257
|
results = @indy.after(:time => '2000-09-07 14:07:43', :inclusive => false).all
|
258
|
-
results.length.
|
258
|
+
expect(results.length).to eq(2)
|
259
259
|
end
|
260
260
|
|
261
261
|
end
|
@@ -270,7 +270,7 @@ describe 'Indy' do
|
|
270
270
|
|
271
271
|
it "with #for should yield Struct::Entry" do
|
272
272
|
Indy.search(log).all do |result|
|
273
|
-
result.
|
273
|
+
expect(result).to be_kind_of(Struct::Entry)
|
274
274
|
end
|
275
275
|
end
|
276
276
|
|
@@ -279,7 +279,7 @@ describe 'Indy' do
|
|
279
279
|
Indy.search(log).all do |result|
|
280
280
|
actual_yield_count += 1
|
281
281
|
end
|
282
|
-
actual_yield_count.
|
282
|
+
expect(actual_yield_count).to eq(3)
|
283
283
|
end
|
284
284
|
|
285
285
|
it "with #for should yield each entry" do
|
@@ -287,7 +287,7 @@ describe 'Indy' do
|
|
287
287
|
Indy.search(log).for(:severity => 'INFO') do |result|
|
288
288
|
actual_yield_count += 1
|
289
289
|
end
|
290
|
-
actual_yield_count.
|
290
|
+
expect(actual_yield_count).to eq(2)
|
291
291
|
end
|
292
292
|
|
293
293
|
it "with #like should yield each entry" do
|
@@ -295,7 +295,7 @@ describe 'Indy' do
|
|
295
295
|
Indy.search(log).like(:message => '\be\S+ing') do |result|
|
296
296
|
actual_yield_count += 1
|
297
297
|
end
|
298
|
-
actual_yield_count.
|
298
|
+
expect(actual_yield_count).to eq(2)
|
299
299
|
end
|
300
300
|
|
301
301
|
end
|