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 +4 -4
- data/bin/timmy +1 -29
- data/lib/timmy/logger.rb +47 -15
- data/lib/timmy/master_timer.rb +6 -5
- data/lib/timmy/option_parser.rb +54 -0
- data/lib/timmy/runner.rb +53 -25
- data/lib/timmy/targeted_timer.rb +4 -73
- data/lib/timmy/targeted_timer_definition.rb +5 -0
- data/lib/timmy/targeted_timer_manager.rb +63 -0
- data/lib/timmy/version.rb +1 -1
- data/lib/timmy.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d197122524ccb2e1140f5cce09773a107679bfe
|
4
|
+
data.tar.gz: 463efa752c2bb2d56b33aa2886e792e9156f2ffd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
17
|
-
duration =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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}+#{
|
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
|
-
|
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"
|
data/lib/timmy/master_timer.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
module Timmy
|
2
2
|
class MasterTimer
|
3
3
|
class << self
|
4
|
-
def start(
|
5
|
-
@start_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 @
|
10
|
+
return @frozen_duration if @frozen_duration
|
11
|
+
|
11
12
|
Time.now.to_f - @start_time
|
12
13
|
end
|
13
14
|
|
14
|
-
def set(
|
15
|
-
@
|
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
|
5
|
-
|
4
|
+
def run
|
5
|
+
ConfigLoader.load
|
6
6
|
|
7
|
-
|
8
|
-
run_line(line.rstrip)
|
9
|
-
end
|
7
|
+
options = OptionParser.parse
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
if value = options[:logger_output_directory]
|
10
|
+
Logger.set_output_directory(value)
|
11
|
+
end
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
time = match[:time].to_i
|
13
|
+
if value = options[:logger_precision]
|
14
|
+
Logger.set_precision(value)
|
15
|
+
end
|
19
16
|
|
20
|
-
|
17
|
+
if replay = options[:replay]
|
18
|
+
replay_log(replay)
|
19
|
+
else
|
20
|
+
consume_stdin
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
41
|
-
Logger.
|
42
|
-
|
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
|
data/lib/timmy/targeted_timer.rb
CHANGED
@@ -1,85 +1,16 @@
|
|
1
1
|
module Timmy
|
2
2
|
class TargetedTimer
|
3
|
-
|
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
|
-
|
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
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.
|
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-
|
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:
|