evidence 0.0.4 → 0.0.5

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