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