oink 0.1.2 → 0.9.1
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.
- data/README.rdoc +15 -29
- data/Rakefile +4 -6
- data/bin/oink +2 -2
- data/lib/oink.rb +1 -9
- data/lib/oink/cli.rb +71 -67
- data/lib/oink/instrumentation.rb +2 -0
- data/lib/oink/instrumentation/active_record.rb +63 -0
- data/lib/oink/instrumentation/memory_snapshot.rb +119 -0
- data/lib/oink/middleware.rb +48 -0
- data/lib/oink/rails/instance_type_counter.rb +8 -62
- data/lib/oink/rails/memory_usage_logger.rb +11 -33
- data/lib/oink/reports/active_record_instantiation_oinked_request.rb +13 -0
- data/lib/oink/reports/active_record_instantiation_report.rb +67 -0
- data/lib/oink/reports/base.rb +38 -0
- data/lib/oink/reports/memory_oinked_request.rb +13 -0
- data/lib/oink/reports/memory_usage_report.rb +71 -0
- data/lib/oink/reports/priority_queue.rb +41 -0
- data/lib/oink/reports/request.rb +20 -0
- data/lib/oink/utils/hash_utils.rb +9 -0
- data/spec/fakes/fake_application_controller.rb +30 -0
- data/spec/fakes/psuedo_output.rb +7 -0
- data/spec/helpers/database.rb +20 -0
- data/spec/{rails → oink/instrumentation}/instance_type_counter_spec.rb +11 -9
- data/spec/oink/instrumentation/memory_snapshot_spec.rb +84 -0
- data/spec/oink/middleware_spec.rb +73 -0
- data/spec/oink/rails/instance_type_counter_spec.rb +52 -0
- data/spec/oink/rails/memory_usage_logger_spec.rb +23 -0
- data/spec/oink/reports/active_record_instantiation_report_spec.rb +193 -0
- data/spec/oink/reports/memory_usage_report_spec.rb +267 -0
- data/spec/oink/reports/oinked_request_spec.rb +22 -0
- data/spec/oink/reports/priority_queue_spec.rb +74 -0
- data/spec/spec_helper.rb +10 -26
- metadata +158 -29
- data/lib/oink/active_record_instantiation_reporter.rb +0 -68
- data/lib/oink/base.rb +0 -40
- data/lib/oink/memory_usage_reporter.rb +0 -72
- data/lib/oink/oinked_request/oinked_ar_request.rb +0 -9
- data/lib/oink/oinked_request/oinked_memory_request.rb +0 -9
- data/lib/oink/oinked_request/oinked_request.rb +0 -16
- data/lib/oink/priority_queue.rb +0 -37
- data/spec/oink/active_record_instantiation_reporter_spec.rb +0 -191
- data/spec/oink/memory_usage_reporter_spec.rb +0 -265
- data/spec/oinked_request/oinked_request_spec.rb +0 -20
- data/spec/priority_queue/priority_queue_spec.rb +0 -75
- data/spec/rails/memory_usage_logger_spec.rb +0 -87
data/README.rdoc
CHANGED
@@ -26,47 +26,33 @@ Currently oink can only parse logs in the Hodel3000Logger format
|
|
26
26
|
|
27
27
|
When used as a gem, this is automatically brought in as a dependency.
|
28
28
|
|
29
|
-
=== Installation
|
29
|
+
=== Installation
|
30
30
|
|
31
|
-
|
31
|
+
Add oink to your Gemfile
|
32
32
|
|
33
|
-
|
33
|
+
gem "oink"
|
34
34
|
|
35
|
-
|
35
|
+
In most rails environments this is sufficient and oink will be required for you via bundler. If not, add a require statement "require 'oink'" in your app.
|
36
36
|
|
37
|
-
|
38
|
-
# your other config here
|
39
|
-
config.gem 'oink'
|
40
|
-
begin
|
41
|
-
require 'hodel_3000_compliant_logger'
|
42
|
-
config.logger = Hodel3000CompliantLogger.new(config.log_path)
|
43
|
-
rescue LoadError => e
|
44
|
-
$stderr.puts "Hodel3000CompliantLogger unavailable, oink will be disabled"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
37
|
+
=== Configuration
|
48
38
|
|
49
|
-
Oink
|
39
|
+
Oink is middleware for instrumentation during the runtime of your application as well as log parsers for offline crunching of the logs generated by
|
40
|
+
the middleware.
|
50
41
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
For memory usage, add this to app/controllers/application_controller.rb:
|
42
|
+
The middleware class to include into your stack is "Oink::Middleware". In rails this can be included by adding the following to your
|
43
|
+
application.rb or environment.rb file:
|
55
44
|
|
56
|
-
|
57
|
-
include Oink::MemoryUsageLogger
|
58
|
-
end
|
45
|
+
config.middleware.use "Oink::Middleware"
|
59
46
|
|
60
|
-
|
47
|
+
or in an initializer, you can also include it:
|
61
48
|
|
62
|
-
|
63
|
-
include Oink::InstanceTypeCounter
|
64
|
-
end
|
49
|
+
YouApplication::Application.middleware.use Oink::Middleware
|
65
50
|
|
51
|
+
Note that the previous way of configuring oink, as a set of modules to include into rails controllers, is deprecated and is set to be removed shortly.
|
66
52
|
|
67
|
-
==
|
53
|
+
== Analyzing logs
|
68
54
|
|
69
|
-
After installing the plugin and aggregating some enhanced logs, run the 'oink' executable against your server logs.
|
55
|
+
After installing the plugin and aggregating some enhanced logs, run the 'oink' executable against your server logs.
|
70
56
|
|
71
57
|
Usage: oink [options] files
|
72
58
|
-t, --threshold [INTEGER] Memory threshold in MB
|
data/Rakefile
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require "rake/gempackagetask"
|
3
2
|
require "rake/clean"
|
4
|
-
require "
|
5
|
-
require './lib/oink/base.rb'
|
3
|
+
require "rspec/core/rake_task"
|
6
4
|
|
7
5
|
begin
|
8
6
|
require 'jeweler'
|
9
7
|
Jeweler::Tasks.new do |s|
|
10
8
|
s.name = "oink"
|
11
|
-
s.version =
|
9
|
+
s.version = "0.9.1"
|
12
10
|
s.author = "Noah Davis"
|
13
11
|
s.email = "noahd1" + "@" + "yahoo.com"
|
14
12
|
s.homepage = "http://github.com/noahd1/oink"
|
@@ -23,8 +21,8 @@ rescue LoadError
|
|
23
21
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
24
22
|
end
|
25
23
|
|
26
|
-
|
27
|
-
t.
|
24
|
+
RSpec::Core::RakeTask.new do |t|
|
25
|
+
t.rspec_opts == ["--color"]
|
28
26
|
end
|
29
27
|
|
30
28
|
desc "Run the specs"
|
data/bin/oink
CHANGED
data/lib/oink.rb
CHANGED
@@ -1,9 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require "oink/memory_usage_reporter"
|
4
|
-
require "oink/active_record_instantiation_reporter"
|
5
|
-
require "oink/cli"
|
6
|
-
|
7
|
-
if defined?(Rails)
|
8
|
-
require 'oink/rails'
|
9
|
-
end
|
1
|
+
require 'oink/middleware'
|
data/lib/oink/cli.rb
CHANGED
@@ -1,97 +1,101 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'oink/reports/base'
|
3
|
+
require 'oink/reports/active_record_instantiation_report'
|
4
|
+
require 'oink/reports/memory_usage_report'
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(args)
|
6
|
-
@args = args
|
7
|
-
end
|
8
|
-
|
9
|
-
def process
|
10
|
-
options = { :format => :short_summary, :type => :memory }
|
6
|
+
module Oink
|
7
|
+
class Cli
|
11
8
|
|
12
|
-
|
13
|
-
|
9
|
+
def initialize(args)
|
10
|
+
@args = args
|
11
|
+
end
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
options[:threshold] = threshold
|
18
|
-
end
|
13
|
+
def process
|
14
|
+
options = { :format => :short_summary, :type => :memory }
|
19
15
|
|
20
|
-
|
21
|
-
options
|
22
|
-
end
|
16
|
+
op = OptionParser.new do |opts|
|
17
|
+
opts.banner = "Usage: oink [options] files"
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
19
|
+
opts.on("-t", "--threshold [INTEGER]", Integer,
|
20
|
+
"Memory threshold in MB") do |threshold|
|
21
|
+
options[:threshold] = threshold
|
22
|
+
end
|
29
23
|
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
opts.on("-f", "--file filepath", "Output to file") do |filename|
|
25
|
+
options[:output_file] = filename
|
26
|
+
end
|
27
|
+
|
28
|
+
format_list = (Oink::Reports::Base::FORMAT_ALIASES.keys + Oink::Reports::Base::FORMATS).join(',')
|
29
|
+
opts.on("--format FORMAT", Oink::Reports::Base::FORMATS, Oink::Reports::Base::FORMAT_ALIASES, "Select format",
|
30
|
+
" (#{format_list})") do |format|
|
31
|
+
options[:format] = format.to_sym
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-m", "--memory", "Check for Memory Threshold (default)") do |v|
|
35
|
+
options[:type] = :memory
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("-r", "--active-record", "Check for Active Record Threshold") do |v|
|
39
|
+
options[:type] = :active_record
|
40
|
+
end
|
33
41
|
|
34
|
-
opts.on("-r", "--active-record", "Check for Active Record Threshold") do |v|
|
35
|
-
options[:type] = :active_record
|
36
42
|
end
|
37
43
|
|
38
|
-
|
44
|
+
op.parse!(@args)
|
39
45
|
|
40
|
-
|
46
|
+
if @args.empty?
|
47
|
+
puts op
|
48
|
+
exit
|
49
|
+
end
|
41
50
|
|
42
|
-
|
43
|
-
puts op
|
44
|
-
exit
|
45
|
-
end
|
51
|
+
output = nil
|
46
52
|
|
47
|
-
|
53
|
+
if options[:output_file]
|
54
|
+
output = File.open(options[:output_file], 'w')
|
55
|
+
else
|
56
|
+
output = STDOUT
|
57
|
+
end
|
48
58
|
|
49
|
-
|
50
|
-
output = File.open(options[:output_file], 'w')
|
51
|
-
else
|
52
|
-
output = STDOUT
|
53
|
-
end
|
59
|
+
files = get_file_listing(@args)
|
54
60
|
|
55
|
-
|
61
|
+
handles = files.map { |f| File.open(f) }
|
56
62
|
|
57
|
-
|
63
|
+
if options[:type] == :memory
|
58
64
|
|
59
|
-
|
65
|
+
options[:threshold] ||= 75
|
66
|
+
options[:threshold] *= 1024
|
60
67
|
|
61
|
-
|
62
|
-
options[:threshold] *= 1024
|
68
|
+
Oink::Reports::MemoryUsageReport.new(handles, options[:threshold], :format => options[:format]).print(output)
|
63
69
|
|
64
|
-
|
70
|
+
elsif options[:type] == :active_record
|
65
71
|
|
66
|
-
|
72
|
+
options[:threshold] ||= 500
|
67
73
|
|
68
|
-
|
74
|
+
Oink::Reports::ActiveRecordInstantiationReport.new(handles, options[:threshold], :format => options[:format]).print(output)
|
69
75
|
|
70
|
-
|
76
|
+
end
|
71
77
|
|
78
|
+
output.close
|
79
|
+
handles.each { |h| h.close }
|
72
80
|
end
|
73
81
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
listing += Dir.glob("#{file}/**")
|
88
|
-
else
|
89
|
-
listing << file
|
82
|
+
protected
|
83
|
+
|
84
|
+
def get_file_listing(args)
|
85
|
+
listing = []
|
86
|
+
args.each do |file|
|
87
|
+
unless File.exist?(file)
|
88
|
+
raise "Could not find \"#{file}\""
|
89
|
+
end
|
90
|
+
if File.directory?(file)
|
91
|
+
listing += Dir.glob("#{file}/**")
|
92
|
+
else
|
93
|
+
listing << file
|
94
|
+
end
|
90
95
|
end
|
96
|
+
listing
|
91
97
|
end
|
92
|
-
listing
|
93
|
-
end
|
94
98
|
|
99
|
+
end
|
95
100
|
end
|
96
101
|
|
97
|
-
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Oink
|
2
|
+
|
3
|
+
def self.extended_active_record?
|
4
|
+
@oink_extended_active_record
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.extended_active_record!
|
8
|
+
@oink_extended_active_record = true
|
9
|
+
end
|
10
|
+
|
11
|
+
module Instrumentation
|
12
|
+
module ActiveRecord
|
13
|
+
|
14
|
+
def self.included(klass)
|
15
|
+
klass.class_eval do
|
16
|
+
|
17
|
+
@@instantiated = {}
|
18
|
+
@@total = nil
|
19
|
+
|
20
|
+
def self.reset_instance_type_count
|
21
|
+
@@instantiated = {}
|
22
|
+
@@total = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.increment_instance_type_count
|
26
|
+
@@instantiated[base_class.name] ||= 0
|
27
|
+
@@instantiated[base_class.name] += 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.instantiated_hash
|
31
|
+
@@instantiated
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.total_objects_instantiated
|
35
|
+
@@total ||= @@instantiated.values.sum
|
36
|
+
end
|
37
|
+
|
38
|
+
unless Oink.extended_active_record?
|
39
|
+
class << self
|
40
|
+
alias_method :allocate_before_oink, :allocate
|
41
|
+
|
42
|
+
def allocate
|
43
|
+
value = allocate_before_oink
|
44
|
+
increment_instance_type_count
|
45
|
+
value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
alias_method :initialize_before_oink, :initialize
|
50
|
+
|
51
|
+
def initialize(*args, &block)
|
52
|
+
value = initialize_before_oink(*args, &block)
|
53
|
+
self.class.increment_instance_type_count
|
54
|
+
value
|
55
|
+
end
|
56
|
+
|
57
|
+
Oink.extended_active_record!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Oink
|
2
|
+
|
3
|
+
module Instrumentation
|
4
|
+
|
5
|
+
class MemorySnapshot
|
6
|
+
def self.memory
|
7
|
+
memory_snapshot_class.new.memory
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.memory_snapshot_class
|
11
|
+
@@memory_snapshot_class ||= begin
|
12
|
+
[WindowsMemorySnapshot,
|
13
|
+
StatmMemorySnapshot,
|
14
|
+
SmapsMemorySnapshot,
|
15
|
+
ProcessStatusMemorySnapshot].find { |snapshot_class| snapshot_class.available? }
|
16
|
+
end
|
17
|
+
|
18
|
+
raise MemoryDataUnavailableError if @@memory_snapshot_class.nil?
|
19
|
+
@@memory_snapshot_class
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class WindowsMemorySnapshot
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'win32ole'
|
27
|
+
rescue LoadError
|
28
|
+
end
|
29
|
+
|
30
|
+
def memory
|
31
|
+
wmi = WIN32OLE.connect("winmgmts:root/cimv2")
|
32
|
+
mem = 0
|
33
|
+
query = "select * from Win32_Process where ProcessID = #{$$}"
|
34
|
+
wmi.ExecQuery(query).each do |wproc|
|
35
|
+
mem = wproc.WorkingSetSize
|
36
|
+
end
|
37
|
+
mem.to_i / 1000
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.available?
|
41
|
+
defined? WIN32OLE
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class StatmMemorySnapshot
|
46
|
+
def memory
|
47
|
+
pages = File.read("/proc/self/statm")
|
48
|
+
pages.to_i * self.class.statm_page_size
|
49
|
+
end
|
50
|
+
|
51
|
+
# try to get and cache memory page size. falls back to 4096.
|
52
|
+
def self.statm_page_size
|
53
|
+
@statm_page_size ||= begin
|
54
|
+
sys_call = SystemCall.execute("getconf PAGESIZE")
|
55
|
+
if sys_call.success?
|
56
|
+
sys_call.stdout.strip.to_i / 1024
|
57
|
+
else
|
58
|
+
4
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.unset_statm_page_size
|
64
|
+
@statm_page_size = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.available?
|
68
|
+
File.exist?("/proc/self/statm")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class SmapsMemorySnapshot
|
73
|
+
def memory
|
74
|
+
proc_file = File.new("/proc/#{$$}/smaps")
|
75
|
+
proc_file.map do |line|
|
76
|
+
size = line[/Size: *(\d+)/, 1] and size.to_i
|
77
|
+
end.compact.sum
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.available?
|
81
|
+
File.exist?("/proc/#{$$}/smaps")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class ProcessStatusMemorySnapshot
|
86
|
+
def memory
|
87
|
+
SystemCall.execute("ps -o vsz= -p #{$$}").stdout.to_i
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.available?
|
91
|
+
SystemCall.execute("ps -o vsz= -p #{$$}").success?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class SystemCall
|
96
|
+
|
97
|
+
def initialize(cmd)
|
98
|
+
@stdout = `#{cmd}`
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.execute(cmd)
|
102
|
+
new(cmd)
|
103
|
+
end
|
104
|
+
|
105
|
+
def stdout
|
106
|
+
@stdout
|
107
|
+
end
|
108
|
+
|
109
|
+
def success?
|
110
|
+
$?.success?
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
class MemoryDataUnavailableError < StandardError; end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|