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.
Files changed (45) hide show
  1. data/README.rdoc +15 -29
  2. data/Rakefile +4 -6
  3. data/bin/oink +2 -2
  4. data/lib/oink.rb +1 -9
  5. data/lib/oink/cli.rb +71 -67
  6. data/lib/oink/instrumentation.rb +2 -0
  7. data/lib/oink/instrumentation/active_record.rb +63 -0
  8. data/lib/oink/instrumentation/memory_snapshot.rb +119 -0
  9. data/lib/oink/middleware.rb +48 -0
  10. data/lib/oink/rails/instance_type_counter.rb +8 -62
  11. data/lib/oink/rails/memory_usage_logger.rb +11 -33
  12. data/lib/oink/reports/active_record_instantiation_oinked_request.rb +13 -0
  13. data/lib/oink/reports/active_record_instantiation_report.rb +67 -0
  14. data/lib/oink/reports/base.rb +38 -0
  15. data/lib/oink/reports/memory_oinked_request.rb +13 -0
  16. data/lib/oink/reports/memory_usage_report.rb +71 -0
  17. data/lib/oink/reports/priority_queue.rb +41 -0
  18. data/lib/oink/reports/request.rb +20 -0
  19. data/lib/oink/utils/hash_utils.rb +9 -0
  20. data/spec/fakes/fake_application_controller.rb +30 -0
  21. data/spec/fakes/psuedo_output.rb +7 -0
  22. data/spec/helpers/database.rb +20 -0
  23. data/spec/{rails → oink/instrumentation}/instance_type_counter_spec.rb +11 -9
  24. data/spec/oink/instrumentation/memory_snapshot_spec.rb +84 -0
  25. data/spec/oink/middleware_spec.rb +73 -0
  26. data/spec/oink/rails/instance_type_counter_spec.rb +52 -0
  27. data/spec/oink/rails/memory_usage_logger_spec.rb +23 -0
  28. data/spec/oink/reports/active_record_instantiation_report_spec.rb +193 -0
  29. data/spec/oink/reports/memory_usage_report_spec.rb +267 -0
  30. data/spec/oink/reports/oinked_request_spec.rb +22 -0
  31. data/spec/oink/reports/priority_queue_spec.rb +74 -0
  32. data/spec/spec_helper.rb +10 -26
  33. metadata +158 -29
  34. data/lib/oink/active_record_instantiation_reporter.rb +0 -68
  35. data/lib/oink/base.rb +0 -40
  36. data/lib/oink/memory_usage_reporter.rb +0 -72
  37. data/lib/oink/oinked_request/oinked_ar_request.rb +0 -9
  38. data/lib/oink/oinked_request/oinked_memory_request.rb +0 -9
  39. data/lib/oink/oinked_request/oinked_request.rb +0 -16
  40. data/lib/oink/priority_queue.rb +0 -37
  41. data/spec/oink/active_record_instantiation_reporter_spec.rb +0 -191
  42. data/spec/oink/memory_usage_reporter_spec.rb +0 -265
  43. data/spec/oinked_request/oinked_request_spec.rb +0 -20
  44. data/spec/priority_queue/priority_queue_spec.rb +0 -75
  45. data/spec/rails/memory_usage_logger_spec.rb +0 -87
@@ -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 and configuration
29
+ === Installation
30
30
 
31
- To begin, you'll want to install it:
31
+ Add oink to your Gemfile
32
32
 
33
- gem install oink
33
+ gem "oink"
34
34
 
35
- Next, you'll need to update your config/environment.rb to include oink as a dependency, and to configure Rails to use the hodel_3000_compliant_logger:
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
- Rails::Initializer.run do |config|
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 allows you to add two type of statistics to your log output: memory usage and number of ActiveRecord objects instantiated. Here's an example for the output you can get:
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
- Feb 08 11:39:54 ey33-s00302 rails[9076]: Memory usage: 316516 | PID: 9076
52
- Feb 27 13:21:04 ey04-s00295 rails[11862]: Instantiation Breakdown: Total: 73 | User: 34 | Group: 20 | Medium: 20 | Sport: 10 | Post: 4 | Discussion: 2
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
- class ApplicationController
57
- include Oink::MemoryUsageLogger
58
- end
45
+ config.middleware.use "Oink::Middleware"
59
46
 
60
- For ActiveRecord objects instantiated, add this to your app/controllers/application_controller.rb:
47
+ or in an initializer, you can also include it:
61
48
 
62
- class ApplicationController
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
- == Analyizing logs
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 "spec/rake/spectask"
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 = Oink::Base::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
- Spec::Rake::SpecTask.new do |t|
27
- t.spec_opts == ["--color"]
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.dirname(__FILE__) + "/../lib/oink.rb"
4
- Cli.new(ARGV.dup).process
3
+ require File.dirname(__FILE__) + "/../lib/oink/cli.rb"
4
+ Oink::Cli.new(ARGV.dup).process
5
5
 
@@ -1,9 +1 @@
1
- $:.unshift(File.dirname(__FILE__ + '.rb') + '/../lib') unless $:.include?(File.dirname(__FILE__ + '.rb') + '/../lib')
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'
@@ -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
- class Cli
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
- op = OptionParser.new do |opts|
13
- opts.banner = "Usage: oink [options] files"
9
+ def initialize(args)
10
+ @args = args
11
+ end
14
12
 
15
- opts.on("-t", "--threshold [INTEGER]", Integer,
16
- "Memory threshold in MB") do |threshold|
17
- options[:threshold] = threshold
18
- end
13
+ def process
14
+ options = { :format => :short_summary, :type => :memory }
19
15
 
20
- opts.on("-f", "--file filepath", "Output to file") do |filename|
21
- options[:output_file] = filename
22
- end
16
+ op = OptionParser.new do |opts|
17
+ opts.banner = "Usage: oink [options] files"
23
18
 
24
- format_list = (Oink::MemoryUsageReporter::FORMAT_ALIASES.keys + Oink::MemoryUsageReporter::FORMATS).join(',')
25
- opts.on("--format FORMAT", Oink::MemoryUsageReporter::FORMATS, Oink::MemoryUsageReporter::FORMAT_ALIASES, "Select format",
26
- " (#{format_list})") do |format|
27
- options[:format] = format.to_sym
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
- opts.on("-m", "--memory", "Check for Memory Threshold (default)") do |v|
31
- options[:type] = :memory
32
- end
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
- end
44
+ op.parse!(@args)
39
45
 
40
- op.parse!(@args)
46
+ if @args.empty?
47
+ puts op
48
+ exit
49
+ end
41
50
 
42
- if @args.empty?
43
- puts op
44
- exit
45
- end
51
+ output = nil
46
52
 
47
- output = nil
53
+ if options[:output_file]
54
+ output = File.open(options[:output_file], 'w')
55
+ else
56
+ output = STDOUT
57
+ end
48
58
 
49
- if options[:output_file]
50
- output = File.open(options[:output_file], 'w')
51
- else
52
- output = STDOUT
53
- end
59
+ files = get_file_listing(@args)
54
60
 
55
- files = get_file_listing(@args)
61
+ handles = files.map { |f| File.open(f) }
56
62
 
57
- handles = files.map { |f| File.open(f) }
63
+ if options[:type] == :memory
58
64
 
59
- if options[:type] == :memory
65
+ options[:threshold] ||= 75
66
+ options[:threshold] *= 1024
60
67
 
61
- options[:threshold] ||= 75
62
- options[:threshold] *= 1024
68
+ Oink::Reports::MemoryUsageReport.new(handles, options[:threshold], :format => options[:format]).print(output)
63
69
 
64
- Oink::MemoryUsageReporter.new(handles, options[:threshold], :format => options[:format]).print(output)
70
+ elsif options[:type] == :active_record
65
71
 
66
- elsif options[:type] == :active_record
72
+ options[:threshold] ||= 500
67
73
 
68
- options[:threshold] ||= 500
74
+ Oink::Reports::ActiveRecordInstantiationReport.new(handles, options[:threshold], :format => options[:format]).print(output)
69
75
 
70
- Oink::ActiveRecordInstantiationReporter.new(handles, options[:threshold], :format => options[:format]).print(output)
76
+ end
71
77
 
78
+ output.close
79
+ handles.each { |h| h.close }
72
80
  end
73
81
 
74
- output.close
75
- handles.each { |h| h.close }
76
- end
77
-
78
- protected
79
-
80
- def get_file_listing(args)
81
- listing = []
82
- args.each do |file|
83
- unless File.exist?(file)
84
- raise "Could not find \"#{file}\""
85
- end
86
- if File.directory?(file)
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,2 @@
1
+ require 'oink/instrumentation/active_record'
2
+ require 'oink/instrumentation/memory_snapshot'
@@ -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