log_weaver 0.0.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.
- checksums.yaml +15 -0
- data/.hgignore +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +62 -0
- data/LICENSE +22 -0
- data/README.md +31 -0
- data/Rakefile +56 -0
- data/bin/log_weaver +57 -0
- data/cucumber.yml +2 -0
- data/features/log_weaver.feature +430 -0
- data/features/step_definitions/log_weaver_steps.rb +16 -0
- data/features/support/env.rb +17 -0
- data/lib/log_weaver.rb +4 -0
- data/lib/log_weaver/combined_log.rb +37 -0
- data/lib/log_weaver/combined_log_index_key.rb +14 -0
- data/lib/log_weaver/monkey_patch.rb +14 -0
- data/lib/log_weaver/parsed_log.rb +41 -0
- data/lib/log_weaver/parsed_log_key.rb +11 -0
- data/lib/log_weaver/prefix_generator.rb +98 -0
- data/lib/log_weaver/version.rb +3 -0
- data/log_weaver.gemspec +40 -0
- data/spec/combined_log_index_key_spec.rb +19 -0
- data/spec/combined_log_spec.rb +95 -0
- data/spec/factories_and_globals.rb +177 -0
- data/spec/parsed_log_key_spec.rb +25 -0
- data/spec/parsed_log_spec.rb +49 -0
- data/spec/prefix_generator_spec.rb +85 -0
- data/spec/spec_helper.rb +10 -0
- metadata +227 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
if ENV['FAILFAST']
|
4
|
+
After do |s|
|
5
|
+
Cucumber.wants_to_quit = true if s.failed?
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^no file named "([^"]*)"$/ do |file_name|
|
10
|
+
check_file_presence( [file_name], false )
|
11
|
+
end
|
12
|
+
|
13
|
+
Before do
|
14
|
+
`rm -rf tmp`
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'aruba/cucumber'
|
2
|
+
require 'methadone/cucumber'
|
3
|
+
|
4
|
+
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
5
|
+
LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
|
6
|
+
|
7
|
+
Before do
|
8
|
+
@aruba_timeout_seconds = 3600
|
9
|
+
# Using "announce" causes massive warnings on 1.9.2
|
10
|
+
@puts = true
|
11
|
+
@original_rubylib = ENV['RUBYLIB']
|
12
|
+
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
After do
|
16
|
+
ENV['RUBYLIB'] = @original_rubylib
|
17
|
+
end
|
data/lib/log_weaver.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module LogWeaver
|
2
|
+
|
3
|
+
class CombinedLog
|
4
|
+
attr_accessor :logs
|
5
|
+
|
6
|
+
def initialize(logs)
|
7
|
+
@logs = logs
|
8
|
+
@index = CombinedLog::build_index(@logs)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build_index(logs)
|
12
|
+
# need to sort by timestamp, then prefix
|
13
|
+
index = {}
|
14
|
+
logs.each do |log|
|
15
|
+
log.lines.each do |t,l|
|
16
|
+
key = CombinedLogIndexKey.new(log.prefix, t)
|
17
|
+
index[key] = l
|
18
|
+
end
|
19
|
+
end
|
20
|
+
#TODO: sorting at this point may have seriously bad performance for large logs; consider a
|
21
|
+
# data structure that stays sorted as you insert
|
22
|
+
Hash[index.sort]
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
res = ""
|
27
|
+
@index.each do |key, lines|
|
28
|
+
lines.each do |l|
|
29
|
+
res << "#{key.prefix}#{l}\n"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
res
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module LogWeaver
|
2
|
+
CombinedLogIndexKey = Struct.new(:prefix, :timestamp) do
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def <=>(other)
|
6
|
+
return timestamp <=> other.timestamp unless timestamp == other.timestamp
|
7
|
+
return prefix <=> other.prefix
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{prefix}#{timestamp}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module LogWeaver
|
4
|
+
class ParsedLog
|
5
|
+
attr_accessor :lines #TODO: rename; attr_reader should suffice
|
6
|
+
attr_reader :prefix
|
7
|
+
|
8
|
+
def initialize(prefix, log)
|
9
|
+
@prefix = prefix
|
10
|
+
@lines = ParsedLog.parse_log log
|
11
|
+
end
|
12
|
+
|
13
|
+
#private TODO: see http://stackoverflow.com/questions/4952980/creating-private-class-method; test per
|
14
|
+
# http://kailuowang.blogspot.ca/2010/08/testing-private-methods-in-rspec.html
|
15
|
+
def self.parse_log(log)
|
16
|
+
res = {}
|
17
|
+
previous_key = nil
|
18
|
+
log.string.split("\n").each do |line|
|
19
|
+
(timestamp, message) = extract_time_stamp(line)
|
20
|
+
if timestamp
|
21
|
+
key = timestamp
|
22
|
+
res[key] = [] unless key == previous_key
|
23
|
+
previous_key = key
|
24
|
+
else
|
25
|
+
raise ArgumentError, "Log does not begin with a timestamp." if previous_key.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
res[previous_key] << line #message
|
29
|
+
end
|
30
|
+
res
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.extract_time_stamp(line)
|
34
|
+
timestamp = line[/^[0-9]{4}-[01][0-9]-[0-3][0-9] [0-2][0-9](:[0-5][0-9]){2}\.[0-9]{3}/,0]
|
35
|
+
message = line.sub(/^#{timestamp}/,'')
|
36
|
+
timestamp = Time.parse(timestamp) unless timestamp.nil?
|
37
|
+
[timestamp,message]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module LogWeaver
|
2
|
+
ParsedLogKey = Struct.new( :prefix, :timestamp, :count ) do
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def <=>(other)
|
6
|
+
return timestamp <=> other.timestamp unless timestamp == other.timestamp
|
7
|
+
return count <=> other.count unless count == other.count
|
8
|
+
prefix <=> other.prefix
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'methadone'
|
2
|
+
|
3
|
+
include Methadone::Main
|
4
|
+
|
5
|
+
module LogWeaver
|
6
|
+
module PrefixGenerator
|
7
|
+
|
8
|
+
# given an array of file paths, generate file name prefixes given the following rules:
|
9
|
+
# 1. prefixes have to differ
|
10
|
+
# 2. prefixes have to be at least as long as min_length, unless file name is shorter
|
11
|
+
# 3. if file names match, and are shorter than min_length, grab whole directories from directory path until they don't match
|
12
|
+
# results are returned as a hash keyed on passed-in file names
|
13
|
+
def get_file_prefixes(file_paths, min_length = 4)
|
14
|
+
# pseudocode:
|
15
|
+
# sort by base_name length
|
16
|
+
# get common prefix of base_names
|
17
|
+
# append letters to prefix from file name at least until min_length and all unique
|
18
|
+
# prepend directories until all unique
|
19
|
+
|
20
|
+
base_names = []
|
21
|
+
expanded_paths = []
|
22
|
+
processed_file_paths = {}
|
23
|
+
max_base_name_length = 0
|
24
|
+
max_path_component_length = 0
|
25
|
+
|
26
|
+
file_paths.each do |fp|
|
27
|
+
max_base_name_length = fp.length if fp.length > max_base_name_length
|
28
|
+
base_name = File.basename fp
|
29
|
+
base_names << base_name
|
30
|
+
processed_file_paths[fp] = {}
|
31
|
+
processed_file_paths[fp][:base_name] = base_name
|
32
|
+
processed_file_paths[fp][:expanded_path] = File.expand_path(fp)
|
33
|
+
expanded_paths << processed_file_paths[fp][:expanded_path]
|
34
|
+
path_dirs = processed_file_paths[fp][:expanded_path].split('/')
|
35
|
+
path_dirs.pop
|
36
|
+
processed_file_paths[fp][:path_dirs] = path_dirs
|
37
|
+
max_path_component_length = processed_file_paths[fp][:path_dirs].length if processed_file_paths[fp][:path_dirs].length > max_path_component_length
|
38
|
+
end
|
39
|
+
|
40
|
+
raise ArgumentError, "File list is not unique." unless expanded_paths.uniq?
|
41
|
+
|
42
|
+
# initialize accumulator data structures with the common prefix
|
43
|
+
prefix = get_longest_common_prefix base_names
|
44
|
+
prefixes = []
|
45
|
+
file_paths.each do |fp|
|
46
|
+
processed_file_paths[fp][:prefix] = prefix.dup
|
47
|
+
prefixes << processed_file_paths[fp][:prefix]
|
48
|
+
end
|
49
|
+
|
50
|
+
# append as many remaining characters from file basename as it will take to take us
|
51
|
+
# over min_length and make each prefix unique
|
52
|
+
(prefix.length .. max_base_name_length - 1).each do |i|
|
53
|
+
file_paths.each do |fp|
|
54
|
+
# append an additional letter; note, if nil, to_s will convert it to ""
|
55
|
+
processed_file_paths[fp][:prefix] << processed_file_paths[fp][:base_name][i].to_s
|
56
|
+
end
|
57
|
+
if i+1 >= min_length
|
58
|
+
break if prefixes.uniq?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# prepend dir path components if still not unique
|
63
|
+
(max_path_component_length - 1).downto(0) do |i|
|
64
|
+
break if prefixes.uniq?
|
65
|
+
file_paths.each do |fp|
|
66
|
+
processed_file_paths[fp][:prefix].insert(0, processed_file_paths[fp][:path_dirs][i].to_s + "/")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# pick out the results
|
71
|
+
res = {}
|
72
|
+
longest_prefix_length = 0
|
73
|
+
file_paths.each do |fp|
|
74
|
+
res[fp] = processed_file_paths[fp][:prefix]
|
75
|
+
longest_prefix_length = res[fp].length if res[fp].length > longest_prefix_length
|
76
|
+
end
|
77
|
+
|
78
|
+
file_paths.each do |fp|
|
79
|
+
orig_prefix_length = res[fp].length
|
80
|
+
res[fp] << ": " << " " * (longest_prefix_length - orig_prefix_length)
|
81
|
+
end
|
82
|
+
|
83
|
+
res
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_longest_common_prefix(words)
|
87
|
+
words = words.dup
|
88
|
+
return nil if words.include? nil
|
89
|
+
prefix = words.shift.dup
|
90
|
+
until prefix == ""
|
91
|
+
break if words.all?{ |w| w =~ /^#{prefix}/ }
|
92
|
+
prefix.chop!
|
93
|
+
end
|
94
|
+
prefix
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/log_weaver.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/log_weaver/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.add_development_dependency('readme', '~> 0.1')
|
6
|
+
require 'readme'
|
7
|
+
r = Readme.file
|
8
|
+
gem.authors = r.authors
|
9
|
+
gem.email = ["raphael.borowiecki@gmail.com"]
|
10
|
+
gem.description = r.description
|
11
|
+
gem.summary = "Weaves log files by timestamp."
|
12
|
+
gem.homepage = r.homepage
|
13
|
+
|
14
|
+
if File.directory?('.hg')
|
15
|
+
gem.files = `hg manifest`.split($\)
|
16
|
+
elsif File.directory?('.git')
|
17
|
+
gem.files = `git ls-files`.split($\)
|
18
|
+
else
|
19
|
+
raise "Need to build from a git or hg repo to generate manifest."
|
20
|
+
end
|
21
|
+
|
22
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
23
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
24
|
+
gem.name = "log_weaver"
|
25
|
+
gem.require_paths = ["lib"]
|
26
|
+
gem.version = LogWeaver::VERSION
|
27
|
+
|
28
|
+
gem.add_development_dependency('yard', '~> 0.8')
|
29
|
+
gem.add_development_dependency('unindent', '~> 1.0')
|
30
|
+
gem.add_development_dependency('aruba', '~> 0.5')
|
31
|
+
gem.add_development_dependency('cucumber', '~> 1.2')
|
32
|
+
gem.add_development_dependency('rspec', '~> 2.13')
|
33
|
+
gem.add_development_dependency('factory_girl', '~> 4.2')
|
34
|
+
gem.add_development_dependency('rake','~> 0.9')
|
35
|
+
gem.add_dependency('methadone', '~> 1.2')
|
36
|
+
gem.add_dependency('require_all', '~> 1.2')
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LogWeaver
|
4
|
+
class CombinedLogIndexKey
|
5
|
+
describe "#<=>" do
|
6
|
+
it "should compare timestamp first" do
|
7
|
+
($k_p1_t2 <=> $k_p2_t1).should == ($k_p1_t2.timestamp <=> $k_p2_t1.timestamp)
|
8
|
+
end
|
9
|
+
it "should compare prefix second" do
|
10
|
+
($k_p1_t1 <=> $k_p2_t1).should == ($k_p1_t1.prefix <=> $k_p2_t1.prefix)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
describe"#to_s" do
|
14
|
+
it "prints 'prefix: timestamp'" do
|
15
|
+
$k_p1_t1.to_s.should == "#{$p1}#{$t1}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'unindent'
|
3
|
+
|
4
|
+
module LogWeaver
|
5
|
+
|
6
|
+
def index(logs)
|
7
|
+
CombinedLog.new(logs).instance_variable_get(:index)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
describe CombinedLog do
|
12
|
+
=begin
|
13
|
+
before(:all) do
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
describe ".initialize" do
|
18
|
+
it "stores the logs" do
|
19
|
+
@cl.logs.should == @logs
|
20
|
+
end
|
21
|
+
end
|
22
|
+
=end
|
23
|
+
|
24
|
+
describe ".build_index" do
|
25
|
+
# note: it's much briefer/easier/more readable to give symbolic descriptions
|
26
|
+
# for the examples; pn is the prefix, tn is the timestamp, ln are the line contents
|
27
|
+
=begin
|
28
|
+
it "handles [p1_t1_l1, p2_t2_l1] (2 logs, ordered timestamps, 1 line each)" do
|
29
|
+
CombinedLog.build_index([$pl_p1_t1_l1, $pl_p2_t2_l1]).to_a.should == $hash_p1_t1_l1_and_p2_t2_l1.to_a
|
30
|
+
end
|
31
|
+
it "handles [p1_t1_l1, p2_t1_l1] (2 logs, same timestamp, 1 line each)" do
|
32
|
+
CombinedLog.build_index([$pl_p1_t1_l1, $pl_p2_t1_l1]).to_a.should == $hash_p1_t1_l1_and_p2_t1_l1.to_a
|
33
|
+
end
|
34
|
+
it "handles [p1_t2_l1, p2_t1_l1] (2 logs, p2 timestamp comes before p1)" do
|
35
|
+
CombinedLog.build_index([$pl_p1_t2_l1, $pl_p2_t1_l1]).to_a.should == $hash_p1_t2_l1_and_p2_t1_l1.to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
sum = @parsed_log_p1_t1l1_t2l1 + @parsed_log_p1_t1l2_t2l2
|
40
|
+
sum = @parsed_log_p1_t2l1 + @parsed_log_p2_t1l1
|
41
|
+
sum = @parsed_log_p1_t1l1_t2l1 + @parsed_log_p2_t1l1_t2l1
|
42
|
+
it "handles [p1_t2_l1, p2_t1_l1] (2 logs, p2 timestamp comes before p1)" do
|
43
|
+
cl = CombinedLog.build_index([$pl_p1_t2_l1, $pl_p2_t1_l1])
|
44
|
+
cl.should == $hash_p1_t2_l1_and_p2_t1_l1
|
45
|
+
CombinedLog.build_index([$pl_p1_t2_l1, $pl_p2_t1_l1]).to_a.should == $hash_p1_t2_l1_and_p2_t1_l1.to_a
|
46
|
+
end
|
47
|
+
=end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#to_s" do
|
51
|
+
it "prints p1_t1_l1, p2_t2_l1" do
|
52
|
+
output = <<-eos
|
53
|
+
#{$out_p1_t1_l1}
|
54
|
+
#{$out_p2_t2_l1}
|
55
|
+
eos
|
56
|
+
CombinedLog.new([$pl_p1_t1_l1, $pl_p2_t2_l1]).to_s.should == output.unindent
|
57
|
+
end
|
58
|
+
it "prints p1_t1_l1, p2_t1_l1 (same timestamp across files)" do
|
59
|
+
output = <<-eos
|
60
|
+
#{$out_p1_t1_l1}
|
61
|
+
#{$out_p2_t1_l1}
|
62
|
+
eos
|
63
|
+
CombinedLog.new([$pl_p1_t1_l1, $pl_p2_t1_l1]).to_s.should == output.unindent
|
64
|
+
end
|
65
|
+
it "prints p1_t1_l1_t1_l2, p2_t1_l3_t1_l4 (same timestamp across files, more lines)" do
|
66
|
+
output = <<-eos
|
67
|
+
#{$out_p1_t1_l1}
|
68
|
+
#{$out_p1_t1_l2}
|
69
|
+
#{$out_p2_t1_l3}
|
70
|
+
#{$out_p2_t1_l4}
|
71
|
+
eos
|
72
|
+
CombinedLog.new([$pl_p1_t1_l1_t1_l2, $pl_p2_t1_l3_t1_l4]).to_s.should == output.unindent
|
73
|
+
end
|
74
|
+
it "prints p1_t1_l1_t2_l1, p2_t3_l1" do
|
75
|
+
output = <<-eos
|
76
|
+
#{$out_p1_t1_l1}
|
77
|
+
#{$out_p1_t2_l1}
|
78
|
+
#{$out_p2_t3_l1}
|
79
|
+
eos
|
80
|
+
CombinedLog.new([$pl_p1_t1_l1_t2_l1, $pl_p2_t3_l1]).to_s.should == output.unindent
|
81
|
+
end
|
82
|
+
=begin
|
83
|
+
# from parsed_log_spec
|
84
|
+
it "prepends the prefix to every line with a timestamp" do
|
85
|
+
ParsedLog.parse_log(@t1l1_t2l1_log, @p1).should == @hash_with_one_line_per_timestamp
|
86
|
+
end
|
87
|
+
it "does not prepend the prefix to lines with no time stamp" do
|
88
|
+
ParsedLog.parse_log(@log_with_missing_timestamps, @p1).should == @hash_with_more_than_one_line_per_timestamp
|
89
|
+
end
|
90
|
+
=end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'log_weaver'
|
3
|
+
|
4
|
+
|
5
|
+
# Vars common to specs and factories; I tried wrapping them in a module, i.e.:
|
6
|
+
# module CommonVariables
|
7
|
+
# def p1() 'p1' end
|
8
|
+
# end
|
9
|
+
# but having modules deep in Rspec get access to it by extending was way more voodoo
|
10
|
+
# than I can stomach for now. So we'll go with globals.
|
11
|
+
|
12
|
+
$p1 = 'p1:'
|
13
|
+
$p2 = 'p2:'
|
14
|
+
|
15
|
+
$t1 = Time.parse('2000-01-01 00:00:01.000') # NOTE: init time this way to discard values below msec
|
16
|
+
$t2 = $t1 + 1
|
17
|
+
$t3 = $t2 + 1
|
18
|
+
|
19
|
+
$l1 = ' l1'
|
20
|
+
$l2 = ' l2'
|
21
|
+
$l3 = ' l3'
|
22
|
+
$l4 = ' l4'
|
23
|
+
|
24
|
+
$t1_l1 = "#{$t1}#{$l1}"
|
25
|
+
$t1_l2 = "#{$t1}#{$l2}"
|
26
|
+
$t1_l3 = "#{$t1}#{$l3}"
|
27
|
+
$t1_l4 = "#{$t1}#{$l4}"
|
28
|
+
$t2_l1 = "#{$t2}#{$l1}"
|
29
|
+
$t2_l2 = "#{$t2}#{$l2}"
|
30
|
+
$t3_l1 = "#{$t3}#{$l1}"
|
31
|
+
|
32
|
+
$no_t_l1 = $l1
|
33
|
+
$no_t_l2 = $l2
|
34
|
+
|
35
|
+
$out_p1_t1_l1 = "#{$p1}#{$t1_l1}"
|
36
|
+
$out_p1_t1_l2 = "#{$p1}#{$t1_l2}"
|
37
|
+
$out_p1_t2_l1 = "#{$p1}#{$t2_l1}"
|
38
|
+
$out_p2_t1_l1 = "#{$p2}#{$t1_l1}"
|
39
|
+
$out_p2_t1_l3 = "#{$p2}#{$t1_l3}"
|
40
|
+
$out_p2_t1_l4 = "#{$p2}#{$t1_l4}"
|
41
|
+
$out_p2_t2_l1 = "#{$p2}#{$t2_l1}"
|
42
|
+
$out_p2_t3_l1 = "#{$p2}#{$t3_l1}"
|
43
|
+
|
44
|
+
$io_empty = StringIO.new
|
45
|
+
$io_t1_l1 = StringIO.new($t1_l1)
|
46
|
+
$io_t2_l1 = StringIO.new($t2_l1)
|
47
|
+
$io_t1_l1_t2_l1 = StringIO.new([$t1_l1, $t2_l1].join("\n"))
|
48
|
+
$io_t1_l1_t1_l2 = StringIO.new([$t1_l1, $t1_l2].join("\n"))
|
49
|
+
$io_no_t_l1_no_t_l2 = StringIO.new([$no_t_l1, $no_t_l2].join("\n"))
|
50
|
+
$io_no_t_l1_t1_l2 = StringIO.new([$no_t_l1, $t1_l2].join("\n"))
|
51
|
+
$io_t1_l1_no_t_l2 = StringIO.new([$t1_l1, $no_t_l2].join("\n"))
|
52
|
+
|
53
|
+
$io_t1l2_t2l2 = StringIO.new([$t1_l2, $t2_l2].join("\n"))
|
54
|
+
$io_with_missing_timestamps = StringIO.new([$t1_l1, $no_t_line, $t2_l1].join("\n"))
|
55
|
+
$io_with_duplicate_timestamp = StringIO.new([$t1_l1, $t1_l1].join("\n"))
|
56
|
+
$io_starting_with_no_timestamp = StringIO.new([$no_t_line, $t2_l1].join("\n"))
|
57
|
+
|
58
|
+
$hash_t1_l1 = { $t1 => [$t1_l1] }
|
59
|
+
$hash_t1_l1_t2_l1 = {
|
60
|
+
$t1 => [$t1_l1],
|
61
|
+
$t2 => [$t2_l1]
|
62
|
+
}
|
63
|
+
$hash_t1_l3_t1_l4 = {
|
64
|
+
$t1 => [$t1_l3, $t1_l4]
|
65
|
+
}
|
66
|
+
$hash_t2_l1 = { $t2 => [$t2_l1] }
|
67
|
+
$hash_t3_l1 = { $t3 => [$t3_l1] }
|
68
|
+
$hash_t1_l1_t1_l2 = {
|
69
|
+
$t1 => [$t1_l1, $t1_l2]
|
70
|
+
}
|
71
|
+
$hash_t1_l1_no_t_l2 = {
|
72
|
+
$t1 => [$t1_l1, $no_t_l2]
|
73
|
+
}
|
74
|
+
|
75
|
+
# need to monkey-patch in argumentless constructors for FactoryGirl to be happy;
|
76
|
+
# see http://stackoverflow.com/a/6838145/26819
|
77
|
+
module LogWeaver
|
78
|
+
class ParsedLog
|
79
|
+
attr_accessor :prefix
|
80
|
+
def initialize
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
FactoryGirl.define do
|
86
|
+
#TODO: can this be LogWeaver::ParsedLog instead of a string?
|
87
|
+
factory 'log_weaver/parsed_log' do
|
88
|
+
factory :pl_p1 do
|
89
|
+
prefix {$p1} #TODO: are the curlies needed here?
|
90
|
+
factory :pl_p1_t1_l1 do
|
91
|
+
lines $hash_t1_l1
|
92
|
+
end
|
93
|
+
factory :pl_p1_t1_l1_t1_l2 do
|
94
|
+
lines $hash_t1_l1_t1_l2
|
95
|
+
end
|
96
|
+
factory :pl_p1_t1_l1_t2_l1 do
|
97
|
+
lines $hash_t1_l1_t2_l1
|
98
|
+
end
|
99
|
+
factory :pl_p1_t2_l1 do
|
100
|
+
lines $hash_t2_l1
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
factory :pl_p2 do
|
105
|
+
prefix $p2
|
106
|
+
factory :pl_p2_t1_l1 do
|
107
|
+
lines $hash_t1_l1
|
108
|
+
end
|
109
|
+
factory :pl_p2_t1_l3_t1_l4 do
|
110
|
+
lines $hash_t1_l3_t1_l4
|
111
|
+
end
|
112
|
+
factory :pl_p2_t2_l1 do
|
113
|
+
lines $hash_t2_l1
|
114
|
+
end
|
115
|
+
factory :pl_p2_t3_l1 do
|
116
|
+
lines $hash_t3_l1
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
$pl_p1_t1_l1 = FactoryGirl.build :pl_p1_t1_l1
|
122
|
+
$pl_p1_t1_l1_t1_l2 = FactoryGirl.build :pl_p1_t1_l1_t1_l2
|
123
|
+
$pl_p1_t1_l1_t2_l1 = FactoryGirl.build :pl_p1_t1_l1_t2_l1
|
124
|
+
$pl_p1_t2_l1 = FactoryGirl.build :pl_p1_t2_l1
|
125
|
+
$pl_p2_t1_l1 = FactoryGirl.build :pl_p2_t1_l1
|
126
|
+
$pl_p2_t1_l3_t1_l4 = FactoryGirl.build :pl_p2_t1_l3_t1_l4
|
127
|
+
$pl_p2_t2_l1 = FactoryGirl.build :pl_p2_t2_l1
|
128
|
+
$pl_p2_t3_l1 = FactoryGirl.build :pl_p2_t3_l1
|
129
|
+
|
130
|
+
|
131
|
+
factory 'log_weaver/combined_log_index_key' do
|
132
|
+
factory :k_p1 do
|
133
|
+
prefix $p1
|
134
|
+
factory :k_p1_t1 do
|
135
|
+
timestamp $t1
|
136
|
+
end
|
137
|
+
factory :k_p1_t2 do
|
138
|
+
timestamp $t2
|
139
|
+
end
|
140
|
+
end
|
141
|
+
factory :k_p2 do
|
142
|
+
prefix $p2
|
143
|
+
factory :k_p2_t1 do
|
144
|
+
timestamp $t1
|
145
|
+
end
|
146
|
+
factory :k_p2_t2 do
|
147
|
+
timestamp $t2
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
$k_p1_t1 = FactoryGirl.build :k_p1_t1
|
154
|
+
$k_p1_t2 = FactoryGirl.build :k_p1_t2
|
155
|
+
$k_p2_t1 = FactoryGirl.build :k_p2_t1
|
156
|
+
$k_p2_t2 = FactoryGirl.build :k_p2_t2
|
157
|
+
|
158
|
+
$hash_p1_t1_l1_and_p2_t2_l1 = {
|
159
|
+
$k_p1_t1 => [$l1],
|
160
|
+
$k_p2_t2 => [$l1]
|
161
|
+
}
|
162
|
+
|
163
|
+
$hash_p1_t1_l1_and_p2_t1_l1 = {
|
164
|
+
$k_p1_t1 => [$l1],
|
165
|
+
$k_p2_t1 => [$l1]
|
166
|
+
}
|
167
|
+
|
168
|
+
$hash_p1_t2_l1_and_p2_t1_l1 = {
|
169
|
+
$k_p2_t1 => [$l1],
|
170
|
+
$k_p1_t2 => [$l1]
|
171
|
+
}
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
|