timmy 0.2.0 → 0.3.0

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,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: