timmy 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab3e84bce8d95e5a76d488fb1abb5f8505c6c90a
4
- data.tar.gz: 10ccacef3de073ba665185003850a0e83c4334e3
3
+ metadata.gz: 9d197122524ccb2e1140f5cce09773a107679bfe
4
+ data.tar.gz: 463efa752c2bb2d56b33aa2886e792e9156f2ffd
5
5
  SHA512:
6
- metadata.gz: 26175e0bf819a6f13c5b9f29f28159c130865463837727ab23beb6d0d1e40f5101e5623025458719d3065a151a1f4f96c8f5c44b43ec53a686f1e2eb2d04550c
7
- data.tar.gz: d0e2ee57f092eea87d35ea718cdc76704568cc09b89e443cfd79b3cfb5589d33816ae7099c913bf2da298bbe739a433c796b69a2145f386b520e82c3e673534d
6
+ metadata.gz: b036c9c0a24faedfbf0407cb51b6353e60413115cc191922aa76f6cfeb3edb36727fee3be628920fd2124f970baf307c3f3b0dc575d12a113a239e9c8f59ce82
7
+ data.tar.gz: 96a35e620a984c902c18c317baca1890db96a6aa76bd53524aa8fbc7d3c44a590c016d42c27aa118974abed18033b5a783bc03fcb419ae673992d59c08edd473
data/bin/timmy CHANGED
@@ -3,32 +3,4 @@
3
3
  require 'optparse'
4
4
  require 'timmy'
5
5
 
6
- options = {
7
- replay: nil,
8
- profile: false
9
- }
10
-
11
- OptionParser.new do |parser|
12
- parser.banner = "Usage: [COMMAND] | timmy"
13
- parser.separator ""
14
- parser.separator "Options:"
15
- parser.on("-r LOG", "--replay LOG", "Replay specific log file") do |replay|
16
- options[:replay] = replay
17
- end
18
- parser.on("-p", "--profile", "Profile targeted timers ") do |profile|
19
- options[:profile] = profile
20
- end
21
- end.parse!
22
-
23
- Timmy::ConfigLoader.load
24
- Timmy::Logger.clear
25
-
26
- if replay = options[:replay]
27
- Timmy::Runner.replay_log(replay)
28
- else
29
- Timmy::Runner.consume_stdin
30
- end
31
-
32
- if options[:profile]
33
- Timmy::TargetedTimer.put_stopped_profiles
34
- end
6
+ Timmy::Runner.run
data/lib/timmy/logger.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  module Timmy
2
2
  class Logger
3
3
  class << self
4
- def clear
5
- @output = ''
6
- end
7
-
8
4
  def set_output_directory(dir)
9
5
  @output_directory = dir
10
6
  end
@@ -13,23 +9,57 @@ module Timmy
13
9
  @precision = precision
14
10
  end
15
11
 
16
- def put(line, internal: false, since: 0)
17
- duration = format_duration(MasterTimer.get - since)
18
- if internal
19
- @output += "#{duration} > #{line}\n"
20
- puts bold("#{duration} #{line}")
21
- else
22
- @output += "#{duration} | #{line}\n"
23
- puts feint(duration) + " " + line
12
+ def put_output(output)
13
+ duration = MasterTimer.get
14
+ formatted_duration = format_duration(duration)
15
+
16
+ puts feint(formatted_duration) + " " + output
17
+
18
+ @output ||= ''
19
+ @output += sprintf("%.9f %s\n", duration, output)
20
+ end
21
+
22
+ def put_timer(timer)
23
+ puts format_timer(timer)
24
+ end
25
+
26
+ def put_stopped_profiles
27
+ puts "Slowest targeted timers:"
28
+
29
+ slowest_timers = TargetedTimerManager
30
+ .stopped
31
+ .sort_by { |timer| -timer.duration }
32
+ .slice(0, 10)
33
+
34
+ slowest_timers.each do |timer|
35
+ put_timer(timer)
24
36
  end
37
+
38
+ puts
25
39
  end
26
40
 
27
41
  def finalize
28
- suffix = "#{MasterTimer.start.to_i}+#{(MasterTimer.get).to_i}"
42
+ suffix = "#{MasterTimer.start.to_i}+#{MasterTimer.get.to_i}"
29
43
  filename = File.join(output_directory, "timmy-#{suffix}.log")
30
44
 
31
- put("EOF > #{filename}", internal: true)
32
45
  File.write(filename, @output)
46
+
47
+ puts feint("Log written to #{filename}")
48
+ puts
49
+ end
50
+
51
+ private
52
+
53
+ def format_timer(timer)
54
+ string = "#{bold(format_duration(timer.duration))} #{format_id(timer.definition.id)}"
55
+ string += " (#{timer.group})" if timer.group
56
+ string += ": #{green(timer.label)}" if timer.label
57
+
58
+ string
59
+ end
60
+
61
+ def format_id(id)
62
+ id.to_s
33
63
  end
34
64
 
35
65
  def format_duration(duration)
@@ -37,7 +67,9 @@ module Timmy
37
67
  sprintf(format, duration / 60, duration % 60)
38
68
  end
39
69
 
40
- private
70
+ def green(string)
71
+ "\e[0m\e[32m#{string}\e[0m"
72
+ end
41
73
 
42
74
  def bold(string)
43
75
  "\e[0m\e[1m#{string}\e[0m"
@@ -1,18 +1,19 @@
1
1
  module Timmy
2
2
  class MasterTimer
3
3
  class << self
4
- def start(time = nil)
5
- @start_time = time if time
4
+ def start(frozen_time = nil)
5
+ @start_time = frozen_time if frozen_time
6
6
  @start_time ||= Time.now.to_f
7
7
  end
8
8
 
9
9
  def get
10
- return @duration if @duration
10
+ return @frozen_duration if @frozen_duration
11
+
11
12
  Time.now.to_f - @start_time
12
13
  end
13
14
 
14
- def set(duration)
15
- @duration = duration
15
+ def set(frozen_duration)
16
+ @frozen_duration = frozen_duration
16
17
  end
17
18
  end
18
19
  end
@@ -0,0 +1,54 @@
1
+ module Timmy
2
+ class OptionParser
3
+ class << self
4
+ def parse
5
+ options = {
6
+ replay: nil,
7
+ profile: false,
8
+ logger_output_directory: nil,
9
+ logger_precision: nil
10
+ }
11
+
12
+ ::OptionParser.new do |parser|
13
+ parser.banner = <<-EOS
14
+ \e[1mtimmy\e[0m -- time execution of commands and their stages based on console output
15
+
16
+ \e[33mUsage:\e[0m
17
+
18
+ Pipe output from arbitrary command:
19
+
20
+ \e[36m[COMMAND] | timmy [OPTIONS]\e[0m
21
+
22
+ Run without a pipe (usually with --replay):
23
+
24
+ \e[36mtimmy [OPTIONS]\e[0m
25
+ EOS
26
+
27
+ parser.separator ""
28
+ parser.separator "\e[33mOptions:\e[0m"
29
+ parser.separator ""
30
+
31
+ parser.on("-r LOG", "--replay LOG", "Replay specific log file") do |replay|
32
+ options[:replay] = replay
33
+ end
34
+
35
+ parser.on("-p", "--profile", "Profile targeted timers") do |profile|
36
+ options[:profile] = profile
37
+ end
38
+
39
+ parser.on("--logger-output-dir DIR", "Save logs to different directory (default: \"/tmp\")") do |logger_output_directory|
40
+ options[:logger_output_directory] = logger_output_directory
41
+ end
42
+
43
+ parser.on("--logger-precision NUM", Integer, "Set precision used when printing time (default: 0)") do |logger_precision|
44
+ options[:logger_precision] = logger_precision
45
+ end
46
+
47
+ parser.separator ""
48
+ end.parse!
49
+
50
+ options
51
+ end
52
+ end
53
+ end
54
+ end
data/lib/timmy/runner.rb CHANGED
@@ -1,45 +1,73 @@
1
1
  module Timmy
2
2
  class Runner
3
3
  class << self
4
- def consume_stdin
5
- MasterTimer.start
4
+ def run
5
+ ConfigLoader.load
6
6
 
7
- STDIN.each_line do |line|
8
- run_line(line.rstrip)
9
- end
7
+ options = OptionParser.parse
10
8
 
11
- TargetedTimer.stop_all
12
- Logger.finalize
13
- end
9
+ if value = options[:logger_output_directory]
10
+ Logger.set_output_directory(value)
11
+ end
14
12
 
15
- def replay_log(log)
16
- basename = File.basename(log)
17
- match = log.match(/timmy-(?<time>\d+)\+\d+.log/)
18
- time = match[:time].to_i
13
+ if value = options[:logger_precision]
14
+ Logger.set_precision(value)
15
+ end
19
16
 
20
- MasterTimer.start(time)
17
+ if replay = options[:replay]
18
+ replay_log(replay)
19
+ else
20
+ consume_stdin
21
+ end
21
22
 
22
- lines = File.readlines(log)
23
- lines.each do |line|
24
- if match = line.match(/^(?<m>\d+):(?<s>\d+(\.\d+)?) \| (?<content>.*)/)
25
- content = match[:content]
26
- time = match[:m].to_i * 60 + match[:s].to_f
23
+ if options[:profile]
24
+ Logger.put_stopped_profiles
25
+ end
26
+ end
27
27
 
28
- MasterTimer.set(time)
29
- run_line(content)
28
+ def consume_stdin
29
+ around_run_lines do
30
+ STDIN.each_line do |line|
31
+ run_line(line.rstrip)
30
32
  end
33
+ Logger.put_output("EOF")
31
34
  end
35
+ end
32
36
 
33
- TargetedTimer.stop_all
34
- Logger.finalize
37
+ def replay_log(log)
38
+ around_run_lines do
39
+ start_time = File.basename(log).match(/timmy-(?<time>\d+)\+\d+.log/)[:time].to_i
40
+ MasterTimer.start(start_time)
41
+
42
+ File.readlines(log).each do |line|
43
+ if match = line.match(/^(?<m>\d+):(?<s>\d+(\.\d+)?) \| (?<content>.*)/)
44
+ line_time = match[:m].to_i * 60 + match[:s].to_f
45
+ MasterTimer.set(line_time)
46
+ run_line(match[:content])
47
+ elsif match = line.match(/^(?<s>\d+(\.\d+)?) (?<content>.*)/)
48
+ line_time = match[:s].to_f
49
+ MasterTimer.set(line_time)
50
+ run_line(match[:content])
51
+ end
52
+ end
53
+ end
35
54
  end
36
55
 
37
56
  private
38
57
 
58
+ def around_run_lines
59
+ MasterTimer.start
60
+
61
+ yield
62
+
63
+ TargetedTimerManager.stop_all
64
+ Logger.finalize
65
+ end
66
+
39
67
  def run_line(line)
40
- TargetedTimer.start_for_line(line)
41
- Logger.put(line)
42
- TargetedTimer.stop_for_line(line)
68
+ TargetedTimerManager.start_for_line(line)
69
+ Logger.put_output(line)
70
+ TargetedTimerManager.stop_for_line(line)
43
71
  end
44
72
  end
45
73
  end
@@ -1,85 +1,16 @@
1
1
  module Timmy
2
2
  class TargetedTimer
3
- class << self
4
- def start_for_line(line)
5
- TargetedTimerDefinition.all.each do |definition|
6
- if match = line.match(definition.start_regex)
7
- stop_by_id(definition.id)
8
- label = match.send(:[], :label) rescue nil
9
- started.push(self.new(definition, label: label))
10
- end
11
- end
12
- end
3
+ attr_reader :definition, :label, :group, :start_time, :duration
13
4
 
14
- def stop_for_line(line)
15
- started.each do |timer|
16
- if (stop_regex = timer.definition.stop_regex) && line.match?(stop_regex)
17
- stop(timer)
18
- end
19
- end
20
- end
21
-
22
- def stop(timer)
23
- timer.stop
24
- started.delete(timer)
25
- stopped.push(timer)
26
- end
27
-
28
- def stop_by_id(id)
29
- matches = started.select { |timer| timer.definition.id == id }
30
- matches.each { |timer| stop(timer) }
31
- end
32
-
33
- def stop_all
34
- started.each { |timer| stop(timer) }
35
- end
36
-
37
- def started
38
- @started ||= []
39
- end
40
-
41
- def stopped
42
- @stopped ||= []
43
- end
44
-
45
- def put_stopped_profiles
46
- puts ""
47
- puts "Slowest targeted timers:"
48
- TargetedTimer.stopped.sort_by { |timer| -timer.duration }[0..9].each do |timer|
49
- timer.put_profile
50
- end
51
- end
52
- end
53
-
54
- attr_reader :definition, :label, :duration
55
-
56
- def initialize(definition, label: nil)
5
+ def initialize(definition, label: nil, group: nil)
57
6
  @definition = definition
58
- @start_time = MasterTimer.get
59
7
  @label = label
8
+ @group = group
9
+ @start_time = MasterTimer.get
60
10
  end
61
11
 
62
12
  def stop
63
- put
64
13
  @duration = MasterTimer.get - @start_time
65
14
  end
66
-
67
- def put_profile
68
- duration = Logger.format_duration(@duration)
69
- puts " - #{duration} - #{formatted_id} - #{@label}"
70
- end
71
-
72
- private
73
-
74
- def put
75
- log_line = formatted_id
76
- log_line += " #{@label}" if @label
77
-
78
- Logger.put(log_line, since: @start_time, internal: true)
79
- end
80
-
81
- def formatted_id
82
- @definition.id.to_s.split('_').collect(&:capitalize).join
83
- end
84
15
  end
85
16
  end
@@ -2,9 +2,14 @@ module Timmy
2
2
  class TargetedTimerDefinition
3
3
  class << self
4
4
  def add(id, start_regex:, stop_regex: nil)
5
+ delete(id)
5
6
  all.push(self.new(id, start_regex: start_regex, stop_regex: stop_regex))
6
7
  end
7
8
 
9
+ def delete(id)
10
+ all.reject! { |definition| definition.id == id }
11
+ end
12
+
8
13
  def all
9
14
  @all ||= [
10
15
  self.new(:docker_build,
@@ -0,0 +1,63 @@
1
+ module Timmy
2
+ class TargetedTimerManager
3
+ class << self
4
+ def start_for_line(line)
5
+ TargetedTimerDefinition.all.each do |definition|
6
+ if match = line.match(definition.start_regex)
7
+ label = get_capture(match, :label)
8
+ group = get_capture(match, :group)
9
+
10
+ stop_by_id_and_group(definition.id, group)
11
+ started.push(TargetedTimer.new(definition, label: label, group: group))
12
+ end
13
+ end
14
+ end
15
+
16
+ def stop_for_line(line)
17
+ started.each do |timer|
18
+ if (stop_regex = timer.definition.stop_regex) &&
19
+ (match = line.match(stop_regex)) &&
20
+ get_capture(match, :group) == timer.group
21
+ stop(timer)
22
+ end
23
+ end
24
+ end
25
+
26
+ def stop(timer)
27
+ timer.stop
28
+ Logger.put_timer(timer)
29
+
30
+ started.delete(timer)
31
+ stopped.push(timer)
32
+ end
33
+
34
+ def stop_by_id_and_group(id, group)
35
+ matches = started
36
+ .select { |timer| timer.definition.id }
37
+ .select { |timer| timer.group == group }
38
+
39
+ matches.each { |timer| stop(timer) }
40
+ end
41
+
42
+ def stop_all
43
+ started.each { |timer| stop(timer) }
44
+ end
45
+
46
+ def started
47
+ @started ||= []
48
+ end
49
+
50
+ def stopped
51
+ @stopped ||= []
52
+ end
53
+
54
+ private
55
+
56
+ def get_capture(match, name)
57
+ match[name]
58
+ rescue IndexError
59
+ nil
60
+ end
61
+ end
62
+ end
63
+ end
data/lib/timmy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Timmy
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/timmy.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require "timmy/config_loader"
2
2
  require "timmy/logger"
3
3
  require "timmy/master_timer"
4
+ require "timmy/option_parser"
4
5
  require "timmy/runner"
5
6
  require "timmy/targeted_timer"
6
7
  require "timmy/targeted_timer_definition"
8
+ require "timmy/targeted_timer_manager"
7
9
  require "timmy/version"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timmy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karol Słuszniak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-29 00:00:00.000000000 Z
11
+ date: 2019-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,9 +67,11 @@ files:
67
67
  - lib/timmy/config_loader.rb
68
68
  - lib/timmy/logger.rb
69
69
  - lib/timmy/master_timer.rb
70
+ - lib/timmy/option_parser.rb
70
71
  - lib/timmy/runner.rb
71
72
  - lib/timmy/targeted_timer.rb
72
73
  - lib/timmy/targeted_timer_definition.rb
74
+ - lib/timmy/targeted_timer_manager.rb
73
75
  - lib/timmy/version.rb
74
76
  homepage: https://github.com/karolsluszniak/timmy
75
77
  licenses: