evidence 0.0.4 → 0.0.5

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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NTRlNjExNTY0MjllNzYwNjZkZTgyYTA0MGIwN2I3MzcxZDBhYjA3NA==
5
- data.tar.gz: !binary |-
6
- YjI4MzM1MjQwMzZlYjhiZThjYjhiNGZjNGMyYTU3ZmQ0MjliZTJiYw==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- M2MyMjY3Y2U3Y2IxODRiM2M2NzczODdjYzAxYThiODUwNjNlNDNhZTEyNzUz
10
- MjIyMjg0MGMyMTVmZDA1ZDRlNDQ5MDAyOTczMzFjYjU0OTBlMDBmYWE0NWU1
11
- ZmE1YWU4MDllYWZmODAxYTUyMzIxMGIwZWIzNDc5NDkzZTQ5ZGE=
12
- data.tar.gz: !binary |-
13
- Y2Q1MWQwMmIwZjRlMjU3MzQyNTZjYzNjMGQ3NjAwZTAxZGNhNjYxMWYzNjI3
14
- MWUxOTdlYTQxM2FmMjg3NTVjY2Y0NGNjOTVhM2FjMjU5OTQ3MjRjZGU0Yzg4
15
- NzgwNjJlNjAzOGNkMmFjMTdhOWIwNmM5Zjk5Y2EzNGJmNzg0MjE=
2
+ SHA1:
3
+ metadata.gz: f2314c07b4b1873ae89834bd97ac81be2f0c1783
4
+ data.tar.gz: b297e819e4f1792b05dc3a49dde97066378aed13
5
+ SHA512:
6
+ metadata.gz: 28d755cd6038e0c97bbee4ea1da7ed25940aaa8162887685a0da565b708c30cedcae3720ef0568d1d38fae1ee9560ce2a310fa01dafa3cc02c085b2f7745802d
7
+ data.tar.gz: 763e35c45812f677a1dc0448b80eab549396913cad12033dca7d5543ea822e63f0945ec746857460483c1dfbf4ebfe179f77bb3d561a7ebb97a7e34352665ff1
@@ -7,7 +7,7 @@ include Evidence
7
7
  mingle_log_pattern = /^
8
8
  \w{3}\s+\d+\s+\d{2}\:\d{2}\:\d{2}\s+
9
9
  (?<host_name>[^\s]+)\s+
10
- [\w-_]+\:\s+
10
+ [-_\w]+\:\s+
11
11
  INFO\s+
12
12
  \[(?<timestamp>[^\]]+)\]\s+
13
13
  \[(?<thread_label>[^\]]+)\]\s+
@@ -19,19 +19,16 @@ $/x
19
19
  pid = lambda {|log| "#{log[:host_name]}-#{log[:thread_label]}"}
20
20
  message = lambda {|log| log[:message]}
21
21
 
22
- logs = Dir['./log/**/*'].map { |f| stream(File.new(f)) | log_parser(mingle_log_pattern) }
22
+ logs = Dir['./log/*'].sort.lazy.map { |f| File.new(f).lazy.map(&parse_log(mingle_log_pattern)).compact }.flat_map {|a| a}
23
23
  time_window = (ARGV[0] || 60).to_i
24
24
  puts "[DEBUG]logs.size => #{logs.size.inspect}"
25
25
  puts "[DEBUG] time_window => #{time_window.inspect}"
26
26
 
27
- merged_logs = merge_streams(logs, lambda {|log1, log2| log1[:timestamp] <=> log2[:timestamp]})
28
- result = merged_logs | rails_action_parser(pid, message) | request_timestamp_parser | slice_stream(lambda {|action| action[:request][:timestamp]}, time_window) | littles_law_analysis
29
-
30
27
  FileUtils.mkdir_p('out')
31
28
  File.open('out/mingle_logs_littles_law_analysis', 'w') do |f|
32
- result.map do |range, avg_stay_in_system|
33
- t1 = range.min.strftime("%m-%d %H:%M")
34
- t2 = range.max.strftime("%H:%M")
35
- f.write("#{t1}-#{t2} #{avg_stay_in_system}\n".tap{|r| puts r})
36
- end
29
+ logs.map(&rails_action_parser(pid, message)).compact.map(&request_timestamp_parser).chunk(&by_time_window(time_window)).map(&littles_law_analysis).map do |stat|
30
+ t1 = stat[:range].min.strftime("%m-%d %H:%M")
31
+ t2 = stat[:range].max.strftime("%H:%M")
32
+ f.write("#{t1}-#{t2} #{stat[:value]}\n".tap{|r| puts r})
33
+ end.force
37
34
  end
@@ -0,0 +1,8 @@
1
+
2
+ module Evidence
3
+ module Lazy
4
+ def compact
5
+ reject(&:nil?)
6
+ end
7
+ end
8
+ end
@@ -1,26 +1,24 @@
1
1
  module Evidence
2
2
  class RailsActionParser
3
3
 
4
- def initialize(pid, message, unmatched)
4
+ def initialize(pid, message)
5
5
  @pid, @message = pid, message
6
- @unmatched = unmatched
6
+ @processes = Hash.new
7
7
  end
8
8
 
9
- def [](output)
10
- processes = Hash.new
9
+ def to_proc
11
10
  lambda do |log|
12
11
  pid = @pid[log]
13
- if processes.has_key?(pid)
14
- processes[pid] << log
12
+ if @processes.has_key?(pid)
13
+ @processes[pid] << log
15
14
  if end_action?(@message[log])
16
- output.call(parse_action_logs(processes.delete(pid)))
15
+ parse_action_logs(@processes.delete(pid))
17
16
  end
18
17
  else
19
18
  if start_action?(@message[log])
20
- processes[pid] = [log]
21
- else
22
- @unmatched.call(log)
19
+ @processes[pid] = [log]
23
20
  end
21
+ nil
24
22
  end
25
23
  end
26
24
  end
data/lib/evidence.rb CHANGED
@@ -1,51 +1,70 @@
1
- require 'evidence/stream'
2
- require 'evidence/log_parser'
1
+ require 'evidence/lazy'
3
2
  require 'evidence/rails_action_parser'
4
3
 
4
+ Enumerator::Lazy.send(:include, Evidence::Lazy)
5
+
5
6
  module Evidence
6
7
  module_function
7
8
 
8
9
  # Parse log file stream by given pattern
9
10
  # pattern: ruby regex expression, has named group specified
10
- # unmatched processor: process all unmatched log
11
- def log_parser(pattern, unmatched=default_unmatched_process)
12
- LogParser.new(pattern, unmatched)
11
+ # example: logs.map(&parse_log(pattern)).compact
12
+ def parse_log(pattern)
13
+ lambda do |log|
14
+ if m = pattern.match(log)
15
+ Hash[m.names.map(&:to_sym).zip(m.captures)]
16
+ end
17
+ end
13
18
  end
14
19
 
15
20
  # Parse out rails actions by given:
16
21
  # pid: a lambda returns process id used to group logs
17
22
  # message: a lambda returns rails log string message
18
- def rails_action_parser(pid, message, unmatched=default_unmatched_process)
19
- RailsActionParser.new(pid, message, unmatched)
23
+ # example: logs.map(&rails_action_parser(pid, message)).compact
24
+ def rails_action_parser(pid, message)
25
+ RailsActionParser.new(pid, message)
20
26
  end
21
27
 
22
28
  # Rails action request timestamp parser
23
- # log stream | rails_action_parser(pid, message) | request_timestamp_parser
29
+ # log.map(&rails_action_parser(pid, message)).compact.map(&request_timestamp_parser)
24
30
  def request_timestamp_parser(format="%Y-%m-%d %H:%M:%S")
25
- stream_each do |action|
31
+ lambda do |action|
26
32
  action[:request][:timestamp] = Time.strptime(action[:request][:timestamp], format)
33
+ action
27
34
  end
28
35
  end
29
36
 
30
- # Do the little's law analysis on rails actions stream with request_timestamp_parser
31
- # usage example:
32
- # log stream | rails_action_parser(pid, message) | request_timestamp_parser | slice_stream(lambda {|action| action[:request][:timestamp]}, 60) | littles_law_analysis
33
- def littles_law_analysis
34
- lambda do |output|
35
- lambda do |actions|
36
- statistics = actions[:stream].inject(sum: 0, count: 0) do |memo, action|
37
- memo[:count] += 1
38
- memo[:sum] += action[:response][:completed_time].to_i
39
- memo
40
- end
41
- avg_sec_arrival_rate = statistics[:count].to_f/(actions[:range].max - actions[:range].min)
42
- avg_sec_response_time = statistics[:sum].to_f / statistics[:count] /1000
43
- output[range: actions[:range], value: avg_sec_arrival_rate * avg_sec_response_time]
37
+ # actions.chunk(&time_window(60))
38
+ def by_time_window(time_window, start=nil)
39
+ range = nil
40
+ lambda do |ele|
41
+ start ||= ele[:request][:timestamp]
42
+ range ||= start..(start + time_window)
43
+ while(range.max <= ele[:request][:timestamp]) do
44
+ range = range.max..(range.max + time_window)
44
45
  end
46
+ range
45
47
  end
46
48
  end
47
49
 
48
- def default_unmatched_process
49
- lambda {|log| }
50
+ # Do the little's law analysis on rails actions stream
51
+ # usage example:
52
+ # rails_action_parser(pid, message).parse(parse_log(logs,
53
+ # pattern)).each(&request_timestamp_parser).chunk({start: nil},
54
+ # &time_window(60 seconds)).map do |range, actions|
55
+ # littles_law_analysis(range, actions)
56
+ # end
57
+ def littles_law_analysis
58
+ lambda do |args|
59
+ range, actions = args
60
+ statistics = actions.inject(sum: 0, count: 0) do |memo, action|
61
+ memo[:count] += 1
62
+ memo[:sum] += action[:response][:completed_time].to_i
63
+ memo
64
+ end
65
+ avg_sec_arrival_rate = statistics[:count].to_f/(range.max - range.min)
66
+ avg_sec_response_time = statistics[:sum].to_f / statistics[:count] /1000
67
+ {range: range, value: avg_sec_arrival_rate * avg_sec_response_time}
68
+ end
50
69
  end
51
70
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evidence
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xiao Li
@@ -9,20 +9,20 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-14 00:00:00.000000000 Z
12
+ date: 2013-08-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ! '>='
18
+ - - '>='
19
19
  - !ruby/object:Gem::Version
20
20
  version: '0'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ! '>='
25
+ - - '>='
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  description:
@@ -34,9 +34,8 @@ extensions: []
34
34
  extra_rdoc_files: []
35
35
  files:
36
36
  - README.md
37
- - lib/evidence/log_parser.rb
37
+ - lib/evidence/lazy.rb
38
38
  - lib/evidence/rails_action_parser.rb
39
- - lib/evidence/stream.rb
40
39
  - lib/evidence.rb
41
40
  - examples/mingle_logs_analysis.rb
42
41
  homepage: https://github.com/ThoughtWorksStudios/evidence
@@ -49,17 +48,17 @@ require_paths:
49
48
  - lib
50
49
  required_ruby_version: !ruby/object:Gem::Requirement
51
50
  requirements:
52
- - - ! '>='
51
+ - - '>='
53
52
  - !ruby/object:Gem::Version
54
- version: '0'
53
+ version: 2.0.0
55
54
  required_rubygems_version: !ruby/object:Gem::Requirement
56
55
  requirements:
57
- - - ! '>='
56
+ - - '>='
58
57
  - !ruby/object:Gem::Version
59
58
  version: '0'
60
59
  requirements: []
61
60
  rubyforge_project:
62
- rubygems_version: 2.0.6
61
+ rubygems_version: 2.0.0
63
62
  signing_key:
64
63
  specification_version: 4
65
64
  summary: Log Analysis Tool
@@ -1,21 +0,0 @@
1
- module Evidence
2
- class LogParser
3
- def initialize(pattern, unmatched)
4
- @pattern, @unmatched = pattern, unmatched
5
- end
6
-
7
- def [](output)
8
- lambda do |line|
9
- if m = @pattern.match(line)
10
- output.call(to_hash(m))
11
- else
12
- @unmatched.call(line)
13
- end
14
- end
15
- end
16
-
17
- def to_hash(m)
18
- Hash[m.names.map(&:to_sym).zip(m.captures)]
19
- end
20
- end
21
- end
@@ -1,195 +0,0 @@
1
- module Evidence
2
- module Stream
3
- include Enumerable
4
-
5
- def |(process)
6
- case process
7
- when SliceStream
8
- process.slice(self)
9
- else
10
- PipeStream.new(self, process)
11
- end
12
- end
13
- end
14
-
15
- class PipeStream
16
- include Stream
17
-
18
- def initialize(upstream, process)
19
- @upstream, @process = upstream, process
20
- end
21
-
22
- def eos?
23
- @upstream.eos?
24
- end
25
-
26
- def each(&output)
27
- @upstream.each(&@process[output])
28
- end
29
- end
30
-
31
- class EnumStream
32
- include Stream
33
-
34
- def initialize(enum)
35
- @enum = enum
36
- end
37
-
38
- def eos?
39
- @enum.peek
40
- false
41
- rescue StopIteration
42
- true
43
- end
44
-
45
- def each(&output)
46
- loop do
47
- output[@enum.next]
48
- end
49
- rescue StopIteration
50
- end
51
-
52
- def to_s
53
- "$[#{@enum.inspect}]"
54
- end
55
- end
56
-
57
- class MergedStream
58
- include Stream
59
-
60
- def initialize(streams, comparator)
61
- @comparator = comparator
62
- @heads = streams.map{|s| {stream: s}}
63
- end
64
-
65
- def eos?
66
- @heads.empty?
67
- end
68
-
69
- def each(&output)
70
- pull_heads
71
- loop do
72
- if min = @heads.min{|a, b| @comparator.call(a[:element], b[:element])}
73
- output.call(min.delete(:element).tap{ pull_heads })
74
- else
75
- break if @heads.empty?
76
- pull_heads
77
- end
78
- end
79
- end
80
-
81
- def pull_heads
82
- @heads.select!{|h| h[:element] ||= pull(h[:stream])}
83
- end
84
-
85
- def pull(stream)
86
- loop do
87
- return nil if stream.eos?
88
- if n = stream.first
89
- return n
90
- end
91
- end
92
- end
93
- end
94
-
95
- class SlicedStreams
96
- include Stream
97
-
98
- def initialize(stream, index, start_index, end_index)
99
- @stream, @index, @start_index, @end_index = stream, index, start_index, end_index
100
- @slice_start_index, @slice_end_index = nil
101
- end
102
-
103
- def eos?
104
- @stream.eos?
105
- end
106
-
107
- def each(&output)
108
- return if eos?
109
- @head ||= @stream.first
110
- @slice_start_index ||= @start_index || @index[@head]
111
- @slice_end_index ||= @end_index[@slice_start_index]
112
- @eos_in_slice ||= false
113
- loop do
114
- if @slice_start_index > @index[@head]
115
- return if eos?
116
- @head = @stream.first
117
- else
118
- break
119
- end
120
- end
121
-
122
- loop do
123
- break if @eos_in_slice
124
- range = @slice_start_index..@slice_end_index
125
- slice_enum = Enumerator.new do |y|
126
- loop do
127
- break if range.max <= @index[@head]
128
- if @eos_in_slice = eos?
129
- y << @head
130
- break
131
- end
132
- head, @head = @head, @stream.first
133
- y << head
134
- end
135
- end
136
- @slice_start_index, @slice_end_index = range.max, @end_index[range.max]
137
- output[range: range, stream: EnumStream.new(slice_enum)]
138
- end
139
- end
140
- end
141
-
142
- class SliceStream
143
- def initialize(index, start_index, end_index)
144
- @index, @start_index, @end_index = index, start_index, end_index
145
- end
146
-
147
- def slice(stream)
148
- SlicedStreams.new(stream, @index, @start_index, @end_index)
149
- end
150
- end
151
-
152
- module_function
153
- def stream(obj)
154
- EnumStream.new(obj.to_enum)
155
- end
156
-
157
- def merge_streams(streams, comparator)
158
- loop do
159
- s1 = streams.shift
160
- return s1 if streams.empty?
161
- s2 = streams.shift
162
- streams << MergedStream.new([s1, s2], comparator)
163
- end
164
- end
165
-
166
- def slice_stream(index, step, start_index=nil)
167
- end_index = step.is_a?(Proc) ? step : lambda { |index| index + step }
168
- SliceStream.new(index, start_index, end_index)
169
- end
170
-
171
- def stream_each(&block)
172
- lambda do |output|
173
- lambda do |i|
174
- block[i]
175
- output[i]
176
- end
177
- end
178
- end
179
-
180
- def stream_map(&block)
181
- lambda { |output| lambda { |i| output[block[i]] } }
182
- end
183
-
184
- def stream_filter(&block)
185
- lambda { |output| lambda { |i| output[i] if block[i] } }
186
- end
187
- alias :stream_select :stream_filter
188
-
189
- def counter
190
- count = 0
191
- counter = Enumerator.new { |y| loop { y << (count += 1) } }
192
- stream(counter)
193
- end
194
-
195
- end