mnemonic 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +43 -0
- data/LICENSE +22 -0
- data/README.md +47 -0
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +7 -0
- data/examples/gc_stat.rb +13 -0
- data/examples/instances.rb +23 -0
- data/examples/logger.rb +30 -0
- data/examples/rss.rb +13 -0
- data/examples/to_csv.rb +39 -0
- data/examples/to_json.rb +17 -0
- data/lib/mnemonic.rb +74 -0
- data/lib/mnemonic/config.rb +62 -0
- data/lib/mnemonic/logger_proxy.rb +91 -0
- data/lib/mnemonic/metric.rb +14 -0
- data/lib/mnemonic/metric/base.rb +30 -0
- data/lib/mnemonic/metric/gc_stat.rb +50 -0
- data/lib/mnemonic/metric/hash_metric.rb +54 -0
- data/lib/mnemonic/metric/instances_count.rb +20 -0
- data/lib/mnemonic/metric/instances_size.rb +20 -0
- data/lib/mnemonic/metric/objects_count.rb +48 -0
- data/lib/mnemonic/metric/objects_size.rb +47 -0
- data/lib/mnemonic/metric/rss.rb +16 -0
- data/lib/mnemonic/metric/rss/proc_fs.rb +14 -0
- data/lib/mnemonic/metric/rss/ps.rb +9 -0
- data/lib/mnemonic/metric/time.rb +19 -0
- data/lib/mnemonic/metric/time_milliseconds.rb +19 -0
- data/lib/mnemonic/metric/time_seconds.rb +19 -0
- data/lib/mnemonic/sink.rb +8 -0
- data/lib/mnemonic/sink/csv.rb +43 -0
- data/lib/mnemonic/sink/json.rb +41 -0
- data/lib/mnemonic/sink/pretty.rb +89 -0
- data/lib/mnemonic/util.rb +6 -0
- data/lib/mnemonic/util/os.rb +27 -0
- data/lib/mnemonic/util/page_size.rb +24 -0
- data/lib/mnemonic/version.rb +3 -0
- data/mnemonic.gemspec +23 -0
- metadata +141 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'monitor'
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
class Mnemonic
|
6
|
+
class LoggerProxy
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def initialize(logger, mnemonic = Mnemonic.new(&proc))
|
10
|
+
super()
|
11
|
+
@monitor = Monitor.new
|
12
|
+
@logger = logger
|
13
|
+
@mnemonic = mnemonic
|
14
|
+
enable_mnemonic!
|
15
|
+
end
|
16
|
+
|
17
|
+
def enable_mnemonic!
|
18
|
+
@monitor.synchronize do
|
19
|
+
@sink = @mnemonic.attach_pretty(@logger)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def disable_mnemonic!
|
24
|
+
@monitor.synchronize do
|
25
|
+
@mnemonic.detach(@sink)
|
26
|
+
@sink = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def mnemonic_enabled?
|
31
|
+
@monitor.synchronize { !!@sink }
|
32
|
+
end
|
33
|
+
|
34
|
+
def_delegators :@logger,
|
35
|
+
:level,
|
36
|
+
:level=,
|
37
|
+
:formatter,
|
38
|
+
:formatter=,
|
39
|
+
:datetime_format,
|
40
|
+
:datetime_format=
|
41
|
+
|
42
|
+
[
|
43
|
+
:debug,
|
44
|
+
:info,
|
45
|
+
:warn,
|
46
|
+
:error,
|
47
|
+
:fatal,
|
48
|
+
:unknown
|
49
|
+
].each do |name|
|
50
|
+
severity = Logger.const_get(name.to_s.upcase)
|
51
|
+
define_method(name) do |progname = nil, &block|
|
52
|
+
add(severity, nil, progname, &block)
|
53
|
+
end
|
54
|
+
def_delegator :@logger, "#{name}?"
|
55
|
+
end
|
56
|
+
|
57
|
+
def add(severity, message = nil, progname = nil, &block)
|
58
|
+
@monitor.synchronize do
|
59
|
+
@logger.add(severity, message, progname, &block).tap do |result|
|
60
|
+
@mnemonic.trigger! if result && @sink && severity >= @logger.level
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
alias_method :log, :add
|
65
|
+
|
66
|
+
def <<(msg)
|
67
|
+
@monitor.synchronize do
|
68
|
+
@logger << msg
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def close
|
73
|
+
@monitor.synchronize do
|
74
|
+
disable_mnemonic!
|
75
|
+
@logger.close
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def method_missing(method_name, *args, &block)
|
80
|
+
if @logger.respond_to? method_name
|
81
|
+
@logger.send(method_name, *args, &block)
|
82
|
+
else
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def respond_to?(method_name, include_private = false)
|
88
|
+
@logger.respond_to?(method_name) || super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
require 'mnemonic/metric/base'
|
4
|
+
require 'mnemonic/metric/hash_metric'
|
5
|
+
require 'mnemonic/metric/rss'
|
6
|
+
require 'mnemonic/metric/time'
|
7
|
+
require 'mnemonic/metric/time_milliseconds'
|
8
|
+
require 'mnemonic/metric/gc_stat'
|
9
|
+
require 'mnemonic/metric/objects_count'
|
10
|
+
require 'mnemonic/metric/objects_size'
|
11
|
+
require 'mnemonic/metric/instances_count'
|
12
|
+
require 'mnemonic/metric/instances_size'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
class Base
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :start_value, :prev_value, :value
|
6
|
+
attr_reader :diff, :diff_from_start
|
7
|
+
|
8
|
+
def start!
|
9
|
+
@start_value = @value = current_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def refresh!
|
13
|
+
@prev_value = @value
|
14
|
+
@value = current_value
|
15
|
+
@diff = @value - @prev_value
|
16
|
+
@diff_from_start = @value - @start_value
|
17
|
+
end
|
18
|
+
|
19
|
+
def each_submetric
|
20
|
+
yield self
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def current_value
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
class GCStat < HashMetric
|
4
|
+
def initialize(*keys)
|
5
|
+
keys = DEFAULT_KEYS if keys.empty?
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
'GCStat'.freeze
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def refresh_hash!
|
16
|
+
GC.stat(@current_hash_value)
|
17
|
+
end
|
18
|
+
|
19
|
+
KIND_TABLE = {
|
20
|
+
:count => :number,
|
21
|
+
:heap_allocated_pages => :number,
|
22
|
+
:heap_sorted_length => :number,
|
23
|
+
:heap_allocatable_pages => :number,
|
24
|
+
:heap_available_slots => :number,
|
25
|
+
:heap_live_slots => :number,
|
26
|
+
:heap_free_slots => :number,
|
27
|
+
:heap_final_slots => :number,
|
28
|
+
:heap_marked_slots => :number,
|
29
|
+
:heap_swept_slots => :number,
|
30
|
+
:heap_eden_pages => :number,
|
31
|
+
:heap_tomb_pages => :number,
|
32
|
+
:total_allocated_pages => :number,
|
33
|
+
:total_freed_pages => :number,
|
34
|
+
:total_allocated_objects => :number,
|
35
|
+
:total_freed_objects => :number,
|
36
|
+
:malloc_increase_bytes => :bytes,
|
37
|
+
:malloc_increase_bytes_limit => :bytes,
|
38
|
+
:minor_gc_count => :number,
|
39
|
+
:major_gc_count => :number,
|
40
|
+
:remembered_wb_unprotected_objects => :number,
|
41
|
+
:remembered_wb_unprotected_objects_limit => :number,
|
42
|
+
:old_objects => :bytes,
|
43
|
+
:old_objects_limit => :bytes,
|
44
|
+
:oldmalloc_increase_bytes => :number,
|
45
|
+
:oldmalloc_increase_bytes_limit => :number
|
46
|
+
}
|
47
|
+
DEFAULT_KEYS = KIND_TABLE.keys
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
class HashMetric
|
4
|
+
class Submetric < Base
|
5
|
+
attr_reader :kind
|
6
|
+
|
7
|
+
def initialize(parent, key, kind)
|
8
|
+
@parent = parent
|
9
|
+
@key = key
|
10
|
+
@name = "#{parent.name}(#{key.inspect})"
|
11
|
+
@kind = kind
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def current_value
|
17
|
+
@parent[@key]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(*keys)
|
22
|
+
@current_hash_value = {}
|
23
|
+
kind_table = self.class.const_get(:KIND_TABLE)
|
24
|
+
@submetrics = keys.map do |key|
|
25
|
+
Submetric.new(self, key, kind_table[key])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def start!
|
30
|
+
refresh_hash!
|
31
|
+
@submetrics.each(&:start!)
|
32
|
+
end
|
33
|
+
|
34
|
+
def refresh!
|
35
|
+
refresh_hash!
|
36
|
+
@submetrics.each(&:refresh!)
|
37
|
+
end
|
38
|
+
|
39
|
+
def each_submetric(&block)
|
40
|
+
@submetrics.each(&block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](key)
|
44
|
+
@current_hash_value[key]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def refresh_hash!
|
50
|
+
raise NotImplementedError
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
class InstancesCount < Base
|
4
|
+
def initialize(klass)
|
5
|
+
@enum = ObjectSpace.each_object(klass)
|
6
|
+
@name = "Count(#{klass.name || klass.inspect})"
|
7
|
+
end
|
8
|
+
|
9
|
+
def kind
|
10
|
+
:number
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def current_value
|
16
|
+
@enum.count
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
class InstancesSize < Base
|
4
|
+
def initialize(klass)
|
5
|
+
@klass = klass
|
6
|
+
@name = "Size(#{klass.name || klass.inspect})"
|
7
|
+
end
|
8
|
+
|
9
|
+
def kind
|
10
|
+
:bytes
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def current_value
|
16
|
+
ObjectSpace.memsize_of_all(@klass)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
class ObjectsCount < HashMetric
|
4
|
+
def initialize(*keys)
|
5
|
+
keys = DEFAULT_KEYS if keys.empty?
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
'Count'.freeze
|
11
|
+
end
|
12
|
+
|
13
|
+
def kind
|
14
|
+
:number
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def refresh_hash!
|
20
|
+
ObjectSpace.count_objects(@current_hash_value)
|
21
|
+
end
|
22
|
+
|
23
|
+
KIND_TABLE = {
|
24
|
+
:TOTAL => :number,
|
25
|
+
:FREE => :number,
|
26
|
+
:T_OBJECT => :number,
|
27
|
+
:T_CLASS => :number,
|
28
|
+
:T_MODULE => :number,
|
29
|
+
:T_FLOAT => :number,
|
30
|
+
:T_STRING => :number,
|
31
|
+
:T_REGEXP => :number,
|
32
|
+
:T_ARRAY => :number,
|
33
|
+
:T_HASH => :number,
|
34
|
+
:T_STRUCT => :number,
|
35
|
+
:T_BIGNUM => :number,
|
36
|
+
:T_FILE => :number,
|
37
|
+
:T_DATA => :number,
|
38
|
+
:T_MATCH => :number,
|
39
|
+
:T_COMPLEX => :number,
|
40
|
+
:T_RATIONAL => :number,
|
41
|
+
:T_SYMBOL => :number,
|
42
|
+
:T_NODE => :number,
|
43
|
+
:T_ICLASS => :number
|
44
|
+
}
|
45
|
+
DEFAULT_KEYS = KIND_TABLE.keys
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Mnemonic
|
2
|
+
module Metric
|
3
|
+
class ObjectsSize < HashMetric
|
4
|
+
def initialize(*keys)
|
5
|
+
keys = DEFAULT_KEYS if keys.empty?
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
'Size'.freeze
|
11
|
+
end
|
12
|
+
|
13
|
+
def kind
|
14
|
+
:bytes
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def refresh_hash!
|
20
|
+
ObjectSpace.count_objects_size(@current_hash_value)
|
21
|
+
end
|
22
|
+
|
23
|
+
KIND_TABLE = {
|
24
|
+
:T_OBJECT => :bytes,
|
25
|
+
:T_CLASS => :bytes,
|
26
|
+
:T_MODULE => :bytes,
|
27
|
+
:T_FLOAT => :bytes,
|
28
|
+
:T_STRING => :bytes,
|
29
|
+
:T_REGEXP => :bytes,
|
30
|
+
:T_ARRAY => :bytes,
|
31
|
+
:T_HASH => :bytes,
|
32
|
+
:T_STRUCT => :bytes,
|
33
|
+
:T_BIGNUM => :bytes,
|
34
|
+
:T_FILE => :bytes,
|
35
|
+
:T_DATA => :bytes,
|
36
|
+
:T_MATCH => :bytes,
|
37
|
+
:T_COMPLEX => :bytes,
|
38
|
+
:T_RATIONAL => :bytes,
|
39
|
+
:T_SYMBOL => :bytes,
|
40
|
+
:T_NODE => :bytes,
|
41
|
+
:T_ICLASS => :bytes,
|
42
|
+
:TOTAL => :bytes
|
43
|
+
}
|
44
|
+
DEFAULT_KEYS = KIND_TABLE.keys
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|