├── init.rb ├── lib ├── oink.rb └── oink │ ├── rails.rb │ ├── instrumentation.rb │ ├── utils │ └── hash_utils.rb │ ├── reports │ ├── memory_oinked_request.rb │ ├── active_record_instantiation_oinked_request.rb │ ├── request.rb │ ├── priority_queue.rb │ ├── base.rb │ ├── active_record_instantiation_report.rb │ └── memory_usage_report.rb │ ├── rails │ ├── memory_usage_logger.rb │ └── instance_type_counter.rb │ ├── instrumentation │ ├── active_record.rb │ └── memory_snapshot.rb │ ├── middleware.rb │ └── cli.rb ├── .gitignore ├── spec ├── fakes │ ├── psuedo_output.rb │ └── fake_application_controller.rb ├── helpers │ └── database.rb ├── spec_helper.rb └── oink │ ├── reports │ ├── oinked_request_spec.rb │ ├── priority_queue_spec.rb │ ├── active_record_instantiation_report_spec.rb │ └── memory_usage_report_spec.rb │ ├── rails │ ├── memory_usage_logger_spec.rb │ └── instance_type_counter_spec.rb │ ├── instrumentation │ ├── instance_type_counter_spec.rb │ └── memory_snapshot_spec.rb │ ├── middleware_configuration_spec.rb │ └── middleware_spec.rb ├── bin └── oink ├── application_files └── script │ └── oink ├── uninstall.rb ├── .codeclimate.yml ├── install.rb ├── benchmark ├── oink.rb ├── result.txt └── instance_counter_performance.rb ├── Gemfile ├── History.txt ├── Rakefile ├── MIT-LICENSE ├── oink.gemspec ├── README.rdoc └── .rubocop.yml /init.rb: -------------------------------------------------------------------------------- 1 | require "oink/rails" 2 | -------------------------------------------------------------------------------- /lib/oink.rb: -------------------------------------------------------------------------------- 1 | require 'oink/middleware' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/* 2 | pkg 3 | .rvmrc 4 | .bundle 5 | .pairs 6 | Gemfile.lock 7 | -------------------------------------------------------------------------------- /lib/oink/rails.rb: -------------------------------------------------------------------------------- 1 | require "oink/rails/memory_usage_logger" 2 | require "oink/rails/instance_type_counter" 3 | -------------------------------------------------------------------------------- /lib/oink/instrumentation.rb: -------------------------------------------------------------------------------- 1 | require 'oink/instrumentation/active_record' 2 | require 'oink/instrumentation/memory_snapshot' -------------------------------------------------------------------------------- /spec/fakes/psuedo_output.rb: -------------------------------------------------------------------------------- 1 | class PsuedoOutput < Array 2 | 3 | def puts(line) 4 | self << line 5 | end 6 | 7 | end -------------------------------------------------------------------------------- /bin/oink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require File.dirname(__FILE__) + "/../lib/oink/cli.rb" 4 | Oink::Cli.new(ARGV.dup).process 5 | 6 | -------------------------------------------------------------------------------- /application_files/script/oink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | load File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/oink/bin/oink") -------------------------------------------------------------------------------- /uninstall.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | executable_file = File.expand_path("#{File.dirname(__FILE__)}/../../../script/oink") 4 | File.delete(executable_file) -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | rubocop: 4 | enabled: true 5 | fixme: 6 | enabled: true 7 | ratings: 8 | paths: 9 | - "**.rb" 10 | exclude_paths: 11 | - spec/**/* 12 | -------------------------------------------------------------------------------- /lib/oink/utils/hash_utils.rb: -------------------------------------------------------------------------------- 1 | module Oink 2 | module HashUtils 3 | 4 | def self.to_sorted_array(hsh) 5 | hsh.sort{ |a, b| b[1] <=>a [1] }.collect { |k,v| "#{k}: #{v}" } 6 | end 7 | 8 | end 9 | end -------------------------------------------------------------------------------- /lib/oink/reports/memory_oinked_request.rb: -------------------------------------------------------------------------------- 1 | require "oink/reports/request" 2 | 3 | module Oink 4 | module Reports 5 | class MemoryOinkedRequest < Request 6 | 7 | def display_oink_number 8 | "#{@oink_number} KB" 9 | end 10 | 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/oink/reports/active_record_instantiation_oinked_request.rb: -------------------------------------------------------------------------------- 1 | require "oink/reports/request" 2 | 3 | module Oink 4 | module Reports 5 | class ActiveRecordInstantiationOinkedRequest < Request 6 | 7 | def display_oink_number 8 | @oink_number 9 | end 10 | 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /install.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'ftools' 3 | 4 | template_executable_file = File.join(File.dirname(__FILE__), "application_files", "script", "oink") 5 | executable_file = File.expand_path("#{File.dirname(__FILE__)}/../../../script/oink") 6 | 7 | File.copy template_executable_file, executable_file 8 | File.chmod 0755, executable_file 9 | -------------------------------------------------------------------------------- /benchmark/oink.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'benchmark' 4 | 5 | require File.dirname(__FILE__) + "/../lib/oink.rb" 6 | 7 | Benchmark.bmbm(15) do |x| 8 | x.report("Running Oink") { 9 | f = File.open(File.expand_path(File.dirname(__FILE__) + "/../logs/production.log")) 10 | Oink::MemoryUsageReport.new([f], 75*1024).print(STDOUT) 11 | f.close 12 | } 13 | end 14 | 15 | -------------------------------------------------------------------------------- /benchmark/result.txt: -------------------------------------------------------------------------------- 1 | Version 0.1.0 2 | 3 | $ ./benchmark/oink.rb 4 | 5 | Rehearsal -------------------------------------------------- 6 | Running Oink 1.430000 0.040000 1.470000 ( 1.479934) 7 | ----------------------------------------- total: 1.470000sec 8 | 9 | user system total real 10 | Running Oink 1.430000 0.040000 1.470000 ( 1.477537) 11 | 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "hodel_3000_compliant_logger" 4 | gem "activerecord" 5 | 6 | group :development do 7 | gem "jeweler" 8 | gem "rspec" 9 | gem "sqlite3" 10 | gem "rack-test" 11 | gem "rake" 12 | gem "ruby-debug", :platforms => :ruby_18 13 | gem "debugger", :platforms => :ruby_19 14 | end 15 | 16 | gem "codeclimate-test-reporter", group: :test, require: nil, github: "codeclimate/ruby-test-reporter" 17 | -------------------------------------------------------------------------------- /spec/helpers/database.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | 3 | def setup_memory_database 4 | ActiveRecord::Base.establish_connection( 5 | :adapter => 'sqlite3', 6 | :database => ':memory:' 7 | ) 8 | 9 | ActiveRecord::Schema.define(:version => 1) do 10 | create_table "pigs", :force => true do |t| 11 | t.integer "pen_id" 12 | t.string "name" 13 | t.boolean "smells" 14 | end 15 | 16 | create_table "pens", :force => true do |t| 17 | t.string "location" 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /lib/oink/reports/request.rb: -------------------------------------------------------------------------------- 1 | module Oink 2 | module Reports 3 | class Request 4 | include Comparable 5 | 6 | attr_accessor :action, :datetime, :log_lines, :oink_number 7 | 8 | def initialize(action, datetime, log_lines, oink_number) 9 | @action = action 10 | @datetime = datetime 11 | @log_lines = log_lines 12 | @oink_number = oink_number 13 | end 14 | 15 | def <=>(other) 16 | self.oink_number <=> other.oink_number 17 | end 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "codeclimate-test-reporter" 2 | CodeClimate::TestReporter.start 3 | require "rspec" 4 | require "ostruct" 5 | require "logger" 6 | 7 | require 'helpers/database' 8 | require 'fakes/fake_application_controller' 9 | require 'fakes/psuedo_output' 10 | 11 | require 'oink/cli' 12 | require 'oink/rails' 13 | 14 | RSpec.configure do |config| 15 | 16 | config.before :suite do 17 | setup_memory_database 18 | Pig = Class.new(ActiveRecord::Base) 19 | Pen = Class.new(ActiveRecord::Base) 20 | Pig.belongs_to :pen 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /spec/fakes/fake_application_controller.rb: -------------------------------------------------------------------------------- 1 | class FakeApplicationController 2 | 3 | def initialize(logger = Logger.new(StringIO.new)) 4 | @logger = logger 5 | end 6 | 7 | class << self 8 | attr_accessor :around_filters 9 | 10 | def around_filter method 11 | (@around_filters ||= []) << method 12 | end 13 | end 14 | 15 | def index 16 | run_around_filters 17 | end 18 | 19 | def logger 20 | @logger 21 | end 22 | 23 | protected 24 | def run_around_filters 25 | self.class.around_filters.each { |filter| self.send(filter) { perform_action } } 26 | end 27 | 28 | def perform_action 29 | end 30 | end -------------------------------------------------------------------------------- /lib/oink/rails/memory_usage_logger.rb: -------------------------------------------------------------------------------- 1 | require 'oink/instrumentation/memory_snapshot' 2 | 3 | module Oink 4 | module MemoryUsageLogger 5 | def self.included(klass) 6 | klass.class_eval do 7 | around_filter :log_memory_usage 8 | end 9 | end 10 | 11 | private 12 | 13 | def log_memory_usage 14 | yield 15 | if logger 16 | begin 17 | memory_usage = Instrumentation::MemorySnapshot.memory 18 | logger.info("Memory usage: #{memory_usage} | PID: #{$$}") 19 | rescue Oink::Instrumentation::MemoryDataUnavailableError => e 20 | logger.error("Oink unable to retrieve memory on this system. See Oink::MemorySnapshot in source.") 21 | end 22 | end 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /lib/oink/reports/priority_queue.rb: -------------------------------------------------------------------------------- 1 | module Oink 2 | module Reports 3 | class PriorityQueue 4 | 5 | include Enumerable 6 | 7 | def initialize(size) 8 | @size = size 9 | @queue = [] 10 | end 11 | 12 | def push(item) 13 | if @queue.size < @size 14 | @queue << item 15 | elsif item > @queue.last 16 | @queue[@size - 1] = item 17 | end 18 | prioritize 19 | end 20 | 21 | def to_a 22 | @queue 23 | end 24 | 25 | def size 26 | @queue.size 27 | end 28 | 29 | def each 30 | @queue.each { |i| yield i } 31 | end 32 | 33 | protected 34 | 35 | def prioritize 36 | @queue.sort! { |a, b| b <=> a } 37 | end 38 | 39 | end 40 | end 41 | end -------------------------------------------------------------------------------- /spec/oink/reports/oinked_request_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | module Oink::Reports 4 | describe Request do 5 | it "should be comparable" do 6 | lr1 = Request.new("Controller#Action", "February 1 10:20", [], 10) 7 | lr2 = Request.new("Controller#Action", "February 1 10:20", [], 5) 8 | 9 | (lr1 > lr2).should == true 10 | (lr1 == lr2).should == false 11 | end 12 | 13 | it "should sort by memory used" do 14 | lr1 = Request.new("Controller#Action", "February 1 10:20", [], 10) 15 | lr2 = Request.new("Controller#Action", "February 1 10:20", [], 5) 16 | lr3 = Request.new("Controller#Action", "February 1 10:20", [], 30) 17 | 18 | [lr1, lr2, lr3].sort.should == [lr2, lr1, lr3] 19 | end 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | === 0.10.1 / 2013-01-30 2 | 3 | * (BUGFIX) Do not blow up if no active record objects were instantiated (gabecoyne) (Github Issue #23) 4 | 5 | === 0.10.0 / 2013-01-03 6 | 7 | * Additional summary information (average, max, min, total, number requests) (bjpirt) 8 | 9 | === 0.9.3 / 2011-07-08 10 | 11 | * (BUGFIX) Correctly log a controller action that lives in a module (wagner) 12 | * (BUGFIX) Log fix for Rails 2.3.x (yar) 13 | 14 | === 0.9.2 / 2011-04-05 15 | 16 | * More configurable middleware (noahd1, edsinclair) 17 | * Update license (still MIT) (noahd1) 18 | 19 | === 0.9.1 / 2011-03-23 20 | 21 | * Remove unnecessary dependency (noahd1) 22 | 23 | === 0.9.0 / 2011-03-23 24 | 25 | * Introduce middleware and deprecate controller modules (noahd1, edsinclair) 26 | 27 | === 0.1.2 / 2010-10-08 28 | 29 | * Prefer statm to smaps (jordan-brough) 30 | 31 | === 0.1.1 / 2010-09-30 32 | 33 | * Dependency fixes 34 | 35 | === 0.1.0 / 2009-02-09 36 | 37 | * 1st release 38 | 39 | -------------------------------------------------------------------------------- /spec/oink/rails/memory_usage_logger_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | class MemoryLoggerApplicationController < FakeApplicationController 4 | include Oink::MemoryUsageLogger 5 | end 6 | 7 | describe Oink::MemoryUsageLogger do 8 | it "should return memory usage info from the snapshot" do 9 | Oink::Instrumentation::MemorySnapshot.should_receive("memory").and_return(42) 10 | log_output = StringIO.new 11 | controller = MemoryLoggerApplicationController.new(Logger.new(log_output)) 12 | controller.index 13 | log_output.string.should include("Memory usage: 42 | PID: #{$$}") 14 | end 15 | 16 | it "should log an error message if cannot find a memory snapshot strategy" do 17 | Oink::Instrumentation::MemorySnapshot.should_receive("memory").and_raise(Oink::Instrumentation::MemoryDataUnavailableError) 18 | controller = MemoryLoggerApplicationController.new 19 | lambda { 20 | controller.index 21 | }.should_not raise_error 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /benchmark/instance_counter_performance.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'benchmark' 4 | require 'active_record' 5 | require 'sqlite3' 6 | require File.dirname(__FILE__) + "/../lib/oink.rb" 7 | require File.dirname(__FILE__) + "/../init" 8 | 9 | ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:' 10 | ActiveRecord::Migration.verbose = false 11 | 12 | ActiveRecord::Schema.define do 13 | create_table :users, :force => true do |t| 14 | t.timestamps 15 | end 16 | end 17 | 18 | class User < ActiveRecord::Base 19 | end 20 | 21 | Benchmark.bm(15) do |x| 22 | x.report "40,000 empty iterations" do 23 | 40_000.times {} 24 | end 25 | 26 | x.report "without instance type counter - instantiating 40,000 objects" do 27 | 40_000.times do 28 | User.new 29 | end 30 | end 31 | 32 | x.report "with instance type counter - instating 40,000 objects" do 33 | ActiveRecord::Base.send(:include, Oink::ActiveRecord) 34 | 35 | 40_000.times do 36 | User.new 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /lib/oink/rails/instance_type_counter.rb: -------------------------------------------------------------------------------- 1 | require 'oink/instrumentation/active_record' 2 | require 'oink/utils/hash_utils' 3 | 4 | module Oink 5 | 6 | module InstanceTypeCounter 7 | def self.included(klass) 8 | ActiveRecord::Base.send(:include, Oink::Instrumentation::ActiveRecord) 9 | 10 | klass.class_eval do 11 | around_filter :report_instance_type_count 12 | end 13 | end 14 | 15 | def before_report_active_record_count(instantiation_data) 16 | end 17 | 18 | private 19 | 20 | def report_instance_type_count 21 | sorted_list = Oink::HashUtils.to_sorted_array(ActiveRecord::Base.instantiated_hash) 22 | sorted_list.unshift("Total: #{ActiveRecord::Base.total_objects_instantiated}") 23 | breakdown = sorted_list.join(" | ") 24 | before_report_active_record_count(breakdown) 25 | if logger 26 | logger.info("Instantiation Breakdown: #{breakdown}") 27 | end 28 | ActiveRecord::Base.reset_instance_type_count 29 | end 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rubygems/package_task" 2 | require "rake/clean" 3 | require "rspec/core/rake_task" 4 | 5 | begin 6 | require 'jeweler' 7 | Jeweler::Tasks.new do |s| 8 | s.name = "oink" 9 | s.version = "0.10.1" 10 | s.author = "Noah Davis" 11 | s.email = "noahd1" + "@" + "yahoo.com" 12 | s.homepage = "http://github.com/noahd1/oink" 13 | s.license = "MIT" 14 | s.summary = "Log parser to identify actions which significantly increase VM heap size" 15 | s.description = s.summary 16 | s.executables = "oink" 17 | s.files = %w[History.txt MIT-LICENSE README.rdoc Rakefile] + Dir["bin/*"] + Dir["lib/**/*"] 18 | end 19 | Jeweler::GemcutterTasks.new 20 | rescue LoadError 21 | puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" 22 | end 23 | 24 | RSpec::Core::RakeTask.new do |t| 25 | t.rspec_opts == ["--color"] 26 | end 27 | 28 | desc "Run the specs" 29 | task :default => ["spec"] 30 | 31 | CLEAN.include ["pkg", "*.gem", "doc", "ri", "coverage"] 32 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Noah Davis 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/oink/rails/instance_type_counter_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | class ARCountApplicationController < FakeApplicationController 4 | include Oink::InstanceTypeCounter 5 | 6 | def no_pigs 7 | run_around_filters 8 | end 9 | 10 | def two_pigs_in_a_pen 11 | Pig.create! 12 | Pig.create! 13 | Pen.create! 14 | run_around_filters 15 | end 16 | 17 | def two_pigs 18 | Pig.create! 19 | Pig.create! 20 | run_around_filters 21 | end 22 | 23 | end 24 | 25 | describe Oink::InstanceTypeCounter do 26 | 27 | before do 28 | Pig.delete_all 29 | Pen.delete_all 30 | end 31 | 32 | let(:log_output) { StringIO.new } 33 | let(:logger) { Logger.new(log_output) } 34 | 35 | it "reports no AR objects instantiated" do 36 | controller = ARCountApplicationController.new(logger) 37 | controller.no_pigs 38 | log_output.string.should include("Instantiation Breakdown: Total: 0") 39 | end 40 | 41 | it "reports AR objects instantiated by type" do 42 | controller = ARCountApplicationController.new(logger) 43 | controller.two_pigs_in_a_pen 44 | log_output.string.should include("Instantiation Breakdown: Total: 3 | Pig: 2 | Pen: 1") 45 | end 46 | 47 | it "reports totals first even if its a tie" do 48 | controller = ARCountApplicationController.new(logger) 49 | controller.two_pigs 50 | log_output.string.should include("Instantiation Breakdown: Total: 2 | Pig: 2") 51 | end 52 | end -------------------------------------------------------------------------------- /spec/oink/instrumentation/instance_type_counter_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | describe Oink::Instrumentation::ActiveRecord do 4 | before do 5 | ActiveRecord::Base.reset_instance_type_count 6 | Pig.delete_all 7 | Pen.delete_all 8 | end 9 | 10 | describe "hash" do 11 | it "should not count objects not instantiated" do 12 | ActiveRecord::Base.instantiated_hash["Pig"].should == nil 13 | end 14 | 15 | it "should include the objects instantiated" do 16 | Pig.create(:name => "Babe") 17 | Pig.first 18 | ActiveRecord::Base.instantiated_hash["Pig"].should == 2 19 | end 20 | 21 | it "should count instantiations for multiple classes" do 22 | Pig.create(:name => "Babe") 23 | Pen.create(:location => "Backyard") 24 | Pig.first 25 | ActiveRecord::Base.instantiated_hash["Pen"].should == 1 26 | end 27 | 28 | it "should report the total number of objects instantiated" do 29 | Pig.create(:name => "Babe") 30 | Pen.create(:location => "Backyard") 31 | Pig.first 32 | ActiveRecord::Base.total_objects_instantiated.should == 3 33 | end 34 | end 35 | 36 | describe "reset" do 37 | it "should reset the total count" do 38 | Pig.create(:name => "Babe") 39 | ActiveRecord::Base.instantiated_hash["Pig"].should == 1 40 | ActiveRecord::Base.reset_instance_type_count 41 | ActiveRecord::Base.total_objects_instantiated.should == 0 42 | Pig.create(:name => "Babe") 43 | ActiveRecord::Base.total_objects_instantiated.should == 0 44 | end 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /spec/oink/reports/priority_queue_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | module Oink::Reports 4 | describe PriorityQueue do 5 | describe "size" do 6 | 7 | it "should report the right size" do 8 | pq = PriorityQueue.new(5) 9 | pq.push(1) 10 | pq.size.should == 1 11 | pq.push(2) 12 | pq.size.should == 2 13 | end 14 | 15 | it "should be limited to the size initialized with" do 16 | pq = PriorityQueue.new(5) 17 | pq.push(1) 18 | pq.push(2) 19 | pq.push(3) 20 | pq.push(4) 21 | pq.push(5) 22 | pq.push(6) 23 | pq.size.should == 5 24 | end 25 | 26 | end 27 | 28 | describe "order" do 29 | 30 | it "should be in order from highest to lowest" do 31 | pq = PriorityQueue.new(5) 32 | pq.push(1) 33 | pq.push(2) 34 | pq.push(3) 35 | pq.to_a.should == [3,2,1] 36 | end 37 | 38 | it "should throw out the lower value when adding a new value" do 39 | pq = PriorityQueue.new(3) 40 | pq.push(1) 41 | pq.push(2) 42 | pq.push(3) 43 | pq.push(4) 44 | pq.to_a.should == [4,3,2] 45 | end 46 | 47 | it "should not make it into the queue if it's smaller than the items in the queue" do 48 | pq = PriorityQueue.new(3) 49 | pq.push(2) 50 | pq.push(3) 51 | pq.push(4) 52 | pq.push(1) 53 | pq.to_a.should == [4,3,2] 54 | end 55 | 56 | end 57 | 58 | describe "each" do 59 | it "should return each item in turn" do 60 | arr = [] 61 | pq = PriorityQueue.new(5) 62 | pq.push(2) 63 | pq.push(3) 64 | pq.push(4) 65 | pq.push(1) 66 | pq.push(5) 67 | pq.each do |i| 68 | arr << i 69 | end 70 | arr.should == [5,4,3,2,1] 71 | end 72 | end 73 | end 74 | end -------------------------------------------------------------------------------- /spec/oink/middleware_configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") 2 | require "oink/middleware" 3 | require 'rack/test' 4 | 5 | describe "Oink::Middleware configuration" do 6 | include Rack::Test::Methods 7 | 8 | class SampleApplication 9 | def call(env) 10 | [200, {}, ""] 11 | end 12 | end 13 | 14 | let(:app) { Oink::Middleware.new(SampleApplication.new, oink_configuration) } 15 | let(:oink_configuration) { @oink_configuration || {} } 16 | 17 | context "instruments options" do 18 | before do 19 | @log_output = StringIO.new 20 | Hodel3000CompliantLogger.stub(:new => Hodel3000CompliantLogger.new(@log_output)) 21 | Oink::Instrumentation::MemorySnapshot.stub(:memory => 4092) 22 | end 23 | 24 | context "with the memory instrument specified" do 25 | before do 26 | @oink_configuration = { :instruments => :memory } 27 | end 28 | 29 | it "does log memory usage" do 30 | get "/" 31 | @log_output.string.should include("Memory usage: 4092 | PID: #{$$}") 32 | end 33 | 34 | it "does not log activerecord objects instantiated" do 35 | get "/" 36 | @log_output.string.should_not include("Instantiation Breakdown:") 37 | end 38 | 39 | it "does not monkeypatch activerecord" do 40 | ActiveRecord::Base.should_not_receive(:include) 41 | get "/" 42 | end 43 | 44 | it "does not call reset_instance_type_count" do 45 | ActiveRecord::Base.should_not_receive(:reset_instance_type_count) 46 | get "/" 47 | end 48 | end 49 | 50 | context "with the activerecord instrument specified" do 51 | before do 52 | @oink_configuration = { :instruments => :activerecord } 53 | get "/" 54 | end 55 | 56 | it "does not log memory usage" do 57 | @log_output.string.should_not include("Memory usage:") 58 | end 59 | 60 | it "does log activerecord objects instantiated" do 61 | @log_output.string.should include("Instantiation Breakdown:") 62 | end 63 | end 64 | end 65 | end -------------------------------------------------------------------------------- /lib/oink/instrumentation/active_record.rb: -------------------------------------------------------------------------------- 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 | def self.extend_active_record! 12 | ::ActiveRecord::Base.class_eval do 13 | include Instrumentation::ActiveRecord 14 | end 15 | end 16 | 17 | module Instrumentation 18 | module ActiveRecord 19 | 20 | def self.included(klass) 21 | klass.class_eval do 22 | 23 | def self.reset_instance_type_count 24 | self.instantiated_hash = {} 25 | Thread.current['oink.activerecord.instantiations_count'] = nil 26 | end 27 | 28 | def self.increment_instance_type_count 29 | self.instantiated_hash ||= {} 30 | self.instantiated_hash[base_class.name] ||= 0 31 | self.instantiated_hash[base_class.name] += 1 32 | end 33 | 34 | def self.instantiated_hash 35 | Thread.current['oink.activerecord.instantiations'] ||= {} 36 | end 37 | 38 | def self.instantiated_hash=(hsh) 39 | Thread.current['oink.activerecord.instantiations'] = hsh 40 | end 41 | 42 | def self.total_objects_instantiated 43 | Thread.current['oink.activerecord.instantiations_count'] ||= self.instantiated_hash.values.sum 44 | end 45 | 46 | unless Oink.extended_active_record? 47 | class << self 48 | alias_method :allocate_before_oink, :allocate 49 | 50 | def allocate 51 | value = allocate_before_oink 52 | increment_instance_type_count 53 | value 54 | end 55 | end 56 | 57 | alias_method :initialize_before_oink, :initialize 58 | 59 | def initialize(*args, &block) 60 | value = initialize_before_oink(*args, &block) 61 | self.class.increment_instance_type_count 62 | value 63 | end 64 | 65 | Oink.extended_active_record! 66 | end 67 | end 68 | end 69 | end 70 | end 71 | end -------------------------------------------------------------------------------- /lib/oink/middleware.rb: -------------------------------------------------------------------------------- 1 | require 'hodel_3000_compliant_logger' 2 | require 'oink/utils/hash_utils' 3 | require 'oink/instrumentation' 4 | 5 | module Oink 6 | class Middleware 7 | 8 | def initialize(app, options = {}) 9 | @app = app 10 | @logger = options[:logger] || Hodel3000CompliantLogger.new("log/oink.log") 11 | @instruments = options[:instruments] ? Array(options[:instruments]) : [:memory, :activerecord] 12 | 13 | Oink.extend_active_record! if @instruments.include?(:activerecord) 14 | end 15 | 16 | def call(env) 17 | status, headers, body = @app.call(env) 18 | 19 | log_routing(env) 20 | log_memory 21 | log_activerecord 22 | log_completed 23 | [status, headers, body] 24 | end 25 | 26 | def log_completed 27 | @logger.info("Oink Log Entry Complete") 28 | end 29 | 30 | def log_routing(env) 31 | routing_info = rails3_routing_info(env) || rails2_routing_info(env) 32 | if routing_info 33 | controller = routing_info['controller'] 34 | action = routing_info['action'] 35 | @logger.info("Oink Action: #{controller}##{action}") 36 | end 37 | end 38 | 39 | def log_memory 40 | if @instruments.include?(:memory) 41 | memory = Oink::Instrumentation::MemorySnapshot.memory 42 | @logger.info("Memory usage: #{memory} | PID: #{$$}") 43 | end 44 | end 45 | 46 | def log_activerecord 47 | if @instruments.include?(:activerecord) 48 | sorted_list = Oink::HashUtils.to_sorted_array(ActiveRecord::Base.instantiated_hash) 49 | sorted_list.unshift("Total: #{ActiveRecord::Base.total_objects_instantiated}") 50 | @logger.info("Instantiation Breakdown: #{sorted_list.join(' | ')}") 51 | reset_objects_instantiated 52 | end 53 | end 54 | 55 | private 56 | 57 | def rails3_routing_info(env) 58 | env['action_dispatch.request.parameters'] 59 | end 60 | 61 | def rails2_routing_info(env) 62 | env['action_controller.request.path_parameters'] 63 | end 64 | 65 | def reset_objects_instantiated 66 | ActiveRecord::Base.reset_instance_type_count 67 | end 68 | 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/oink/reports/base.rb: -------------------------------------------------------------------------------- 1 | module Oink 2 | module Reports 3 | class Base 4 | 5 | FORMATS = %w[verbose short-summary summary] 6 | FORMAT_ALIASES = { "v" => "verbose", "ss" => "short-summary", "s" => "summary" } 7 | HODEL_LOG_FORMAT_REGEX = /^(\w+ \d{2} \d{2}:\d{2}:\d{2})/ 8 | 9 | def initialize(input, threshold, options = {}) 10 | @inputs = Array(input) 11 | @threshold = threshold 12 | @format = options[:format] || :short_summary 13 | 14 | @pids = {} 15 | @bad_actions = {} 16 | @bad_actions_averaged = {} 17 | @bad_requests = PriorityQueue.new(10) 18 | end 19 | 20 | protected 21 | 22 | def print_summary(output) 23 | output.puts "\n-- SUMMARY --\n" 24 | output.puts "Worst Requests:" 25 | @bad_requests.each_with_index do |offender, index| 26 | output.puts "#{index + 1}. #{offender.datetime}, #{offender.display_oink_number}, #{offender.action}" 27 | if @format == :summary 28 | offender.log_lines.each { |b| output.puts b } 29 | output.puts "---------------------------------------------------------------------" 30 | end 31 | end 32 | output.puts "\nWorst Actions:" 33 | @bad_actions.sort{|a,b| b[1]<=>a[1]}.each { |elem| 34 | output.puts "#{elem[1]}, #{elem[0]}" 35 | } 36 | output.puts "\nAggregated Totals:\n" 37 | if @bad_actions_averaged.length > 0 38 | action_stats = @bad_actions_averaged.map { |action,values| 39 | total = values.inject(0){ |sum,x| sum+x } 40 | { 41 | :action => action, 42 | :total => total, 43 | :mean => total/values.length, 44 | :max => values.max, 45 | :min => values.min, 46 | :count => values.length, 47 | } 48 | } 49 | action_width = @bad_actions_averaged.keys.map{|k| k.length}.max 50 | output.puts "#{'Action'.ljust(action_width)}\tMax\tMean\tMin\tTotal\tNumber of requests" 51 | action_stats.sort{|a,b| b[:total]<=>a[:total]}.each do |action_stat| 52 | output.puts "#{action_stat[:action].ljust(action_width)}\t#{action_stat[:max]}\t#{action_stat[:mean]}\t#{action_stat[:min]}\t#{action_stat[:total]}\t#{action_stat[:count]}" 53 | end 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/oink/reports/active_record_instantiation_report.rb: -------------------------------------------------------------------------------- 1 | require "date" 2 | require "oink/reports/base" 3 | require "oink/reports/active_record_instantiation_oinked_request" 4 | require "oink/reports/priority_queue" 5 | 6 | module Oink 7 | module Reports 8 | class ActiveRecordInstantiationReport < Base 9 | 10 | def print(output) 11 | output.puts "---- OINK FOR ACTIVERECORD ----" 12 | output.puts "THRESHOLD: #{@threshold} Active Record objects per request\n" 13 | 14 | output.puts "\n-- REQUESTS --\n" if @format == :verbose 15 | 16 | @inputs.each do |input| 17 | input.each_line do |line| 18 | line = line.strip 19 | 20 | # Skip this line since we're only interested in the Hodel 3000 compliant lines 21 | next unless line =~ HODEL_LOG_FORMAT_REGEX 22 | 23 | if line =~ /rails\[(\d+)\]/ 24 | pid = $1 25 | @pids[pid] ||= { :buffer => [], :ar_count => -1, :action => "", :request_finished => true } 26 | @pids[pid][:buffer] << line 27 | end 28 | 29 | if line =~ /Oink Action: (([\w\/]+)#(\w+))/ 30 | 31 | @pids[pid][:action] = $1 32 | unless @pids[pid][:request_finished] 33 | @pids[pid][:buffer] = [line] 34 | end 35 | @pids[pid][:request_finished] = false 36 | 37 | elsif line =~ /Instantiation Breakdown: Total: (\d+)/ 38 | 39 | @pids[pid][:ar_count] = $1.to_i 40 | 41 | elsif line =~ /Oink Log Entry Complete/ 42 | 43 | if @pids[pid][:ar_count] > @threshold 44 | @bad_actions[@pids[pid][:action]] ||= 0 45 | @bad_actions[@pids[pid][:action]] = @bad_actions[@pids[pid][:action]] + 1 46 | date = HODEL_LOG_FORMAT_REGEX.match(line).captures[0] 47 | @bad_requests.push(ActiveRecordInstantiationOinkedRequest.new(@pids[pid][:action], date, @pids[pid][:buffer], @pids[pid][:ar_count])) 48 | if @format == :verbose 49 | @pids[pid][:buffer].each { |b| output.puts b } 50 | output.puts "---------------------------------------------------------------------" 51 | end 52 | end 53 | 54 | @pids[pid][:request_finished] = true 55 | @pids[pid][:buffer] = [] 56 | @pids[pid][:ar_count] = -1 57 | 58 | end # end elsif 59 | end # end each_line 60 | end # end each input 61 | 62 | print_summary(output) 63 | 64 | end 65 | end 66 | end 67 | end -------------------------------------------------------------------------------- /lib/oink/cli.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'oink/reports/base' 3 | require 'oink/reports/active_record_instantiation_report' 4 | require 'oink/reports/memory_usage_report' 5 | 6 | module Oink 7 | class Cli 8 | 9 | def initialize(args) 10 | @args = args 11 | end 12 | 13 | def process 14 | options = { :format => :short_summary, :type => :memory } 15 | 16 | op = OptionParser.new do |opts| 17 | opts.banner = "Usage: oink [options] files" 18 | 19 | opts.on("-t", "--threshold [INTEGER]", Integer, 20 | "Memory threshold in MB") do |threshold| 21 | options[:threshold] = threshold 22 | end 23 | 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 41 | 42 | end 43 | 44 | op.parse!(@args) 45 | 46 | if @args.empty? 47 | puts op 48 | exit 49 | end 50 | 51 | output = nil 52 | 53 | if options[:output_file] 54 | output = File.open(options[:output_file], 'w') 55 | else 56 | output = STDOUT 57 | end 58 | 59 | files = get_file_listing(@args) 60 | 61 | handles = files.map { |f| File.open(f) } 62 | 63 | if options[:type] == :memory 64 | 65 | options[:threshold] ||= 75 66 | options[:threshold] *= 1024 67 | 68 | Oink::Reports::MemoryUsageReport.new(handles, options[:threshold], :format => options[:format]).print(output) 69 | 70 | elsif options[:type] == :active_record 71 | 72 | options[:threshold] ||= 500 73 | 74 | Oink::Reports::ActiveRecordInstantiationReport.new(handles, options[:threshold], :format => options[:format]).print(output) 75 | 76 | end 77 | 78 | output.close 79 | handles.each { |h| h.close } 80 | end 81 | 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 95 | end 96 | listing 97 | end 98 | 99 | end 100 | end 101 | 102 | -------------------------------------------------------------------------------- /spec/oink/middleware_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") 2 | require "oink/middleware" 3 | require 'rack/test' 4 | 5 | describe Oink::Middleware do 6 | include Rack::Test::Methods 7 | 8 | class SampleApplication 9 | def call(env) 10 | case env['PATH_INFO'] 11 | when "/no_pigs" 12 | when "/two_pigs" 13 | Pig.create(:name => "Babe") 14 | Pig.first 15 | when "/two_pigs_in_a_pen" 16 | Pig.create(:name => "Babe") 17 | Pen.create(:location => "Backyard") 18 | Pig.first 19 | end 20 | [200, {}, ""] 21 | end 22 | end 23 | 24 | let(:log_output) { StringIO.new } 25 | let(:logger) { Hodel3000CompliantLogger.new(log_output) } 26 | let(:app) { Oink::Middleware.new(SampleApplication.new, :logger => logger) } 27 | 28 | before do 29 | Oink::Instrumentation::MemorySnapshot.stub(:memory => 4092) 30 | Pig.delete_all 31 | Pen.delete_all 32 | end 33 | 34 | context "support legacy rails log format in transition to oink's own log format" do 35 | it "writes rails[pid] to the log even if the app isn't a rails app (for now)" do 36 | get "/no_pigs" 37 | log_output.string.should include("rails[#{$$}]") 38 | end 39 | 40 | it "writes 'Oink Log Entry Complete' after the request has completed" do 41 | get "/no_pigs" 42 | log_output.string.should include("Oink Log Entry Complete") 43 | end 44 | 45 | it "logs the action and controller in rails 3.x" do 46 | get "/no_pigs", {}, {'action_dispatch.request.parameters' => {'controller' => 'oinkoink', 'action' => 'piggie'}} 47 | log_output.string.should include("Oink Action: oinkoink#piggie") 48 | end 49 | 50 | it "logs the action and controller in rails 2.3.x" do 51 | get "/no_pigs", {}, {'action_controller.request.path_parameters' => {'controller' => 'oinkoink', 'action' => 'piggie'}} 52 | log_output.string.should include("Oink Action: oinkoink#piggie") 53 | end 54 | 55 | it "logs the action and controller within a module" do 56 | get "/no_pigs", {}, {'action_dispatch.request.parameters' => {'controller' => 'oinkoink/admin', 'action' => 'piggie'}} 57 | log_output.string.should include("Oink Action: oinkoink/admin#piggie") 58 | end 59 | end 60 | 61 | it "reports 0 totals" do 62 | get "/no_pigs" 63 | log_output.string.should include("Instantiation Breakdown: Total: 0") 64 | end 65 | 66 | it "reports totals first even if it's a tie" do 67 | get "/two_pigs" 68 | log_output.string.should include("Instantiation Breakdown: Total: 2 | Pig: 2") 69 | end 70 | 71 | it "reports pigs and pens instantiated" do 72 | get "/two_pigs_in_a_pen" 73 | log_output.string.should include("Instantiation Breakdown: Total: 3 | Pig: 2 | Pen: 1") 74 | end 75 | 76 | it "logs memory usage" do 77 | Oink::Instrumentation::MemorySnapshot.should_receive(:memory).and_return(4092) 78 | get "/two_pigs_in_a_pen" 79 | log_output.string.should include("Memory usage: 4092 | PID: #{$$}") 80 | end 81 | 82 | end -------------------------------------------------------------------------------- /lib/oink/reports/memory_usage_report.rb: -------------------------------------------------------------------------------- 1 | require "date" 2 | require "oink/reports/base" 3 | require "oink/reports/memory_oinked_request" 4 | require "oink/reports/priority_queue" 5 | 6 | module Oink 7 | module Reports 8 | class MemoryUsageReport < Base 9 | def print(output) 10 | output.puts "---- MEMORY THRESHOLD ----" 11 | output.puts "THRESHOLD: #{@threshold/1024} MB\n" 12 | 13 | output.puts "\n-- REQUESTS --\n" if @format == :verbose 14 | 15 | @inputs.each do |input| 16 | input.each_line do |line| 17 | line = line.strip 18 | 19 | # Skip this line since we're only interested in the Hodel 3000 compliant lines 20 | next unless line =~ HODEL_LOG_FORMAT_REGEX 21 | 22 | if line =~ /rails\[(\d+)\]/ 23 | pid = $1 24 | @pids[pid] ||= { :buffer => [], :last_memory_reading => -1, :current_memory_reading => -1, :action => "", :request_finished => true } 25 | @pids[pid][:buffer] << line 26 | end 27 | 28 | if line =~ /Oink Action: (([\w\/]+)#(\w+))/ 29 | 30 | unless @pids[pid][:request_finished] 31 | @pids[pid][:last_memory_reading] = -1 32 | end 33 | @pids[pid][:action] = $1 34 | @pids[pid][:request_finished] = false 35 | 36 | elsif line =~ /Memory usage: (\d+) / 37 | 38 | memory_reading = $1.to_i 39 | @pids[pid][:current_memory_reading] = memory_reading 40 | 41 | elsif line =~ /Oink Log Entry Complete/ 42 | 43 | @pids[pid][:request_finished] = true 44 | unless @pids[pid][:current_memory_reading] == -1 || @pids[pid][:last_memory_reading] == -1 45 | memory_diff = @pids[pid][:current_memory_reading] - @pids[pid][:last_memory_reading] 46 | if memory_diff > @threshold 47 | @bad_actions[@pids[pid][:action]] ||= 0 48 | @bad_actions[@pids[pid][:action]] = @bad_actions[@pids[pid][:action]] + 1 49 | date = HODEL_LOG_FORMAT_REGEX.match(line).captures[0] 50 | @bad_requests.push(MemoryOinkedRequest.new(@pids[pid][:action], date, @pids[pid][:buffer], memory_diff)) 51 | if @format == :verbose 52 | @pids[pid][:buffer].each { |b| output.puts b } 53 | output.puts "---------------------------------------------------------------------" 54 | end 55 | @bad_actions_averaged[@pids[pid][:action]] ||= [] 56 | @bad_actions_averaged[@pids[pid][:action]] << memory_diff 57 | end 58 | end 59 | 60 | @pids[pid][:buffer] = [] 61 | @pids[pid][:last_memory_reading] = @pids[pid][:current_memory_reading] 62 | @pids[pid][:current_memory_reading] = -1 63 | 64 | end # end elsif 65 | end # end each_line 66 | end # end each input 67 | 68 | print_summary(output) 69 | 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/oink/instrumentation/memory_snapshot.rb: -------------------------------------------------------------------------------- 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 | lines = proc_file.lines 76 | lines.map do |line| 77 | size = line[/Size: *(\d+)/, 1] and size.to_i 78 | end.compact.sum 79 | end 80 | 81 | def self.available? 82 | File.exist?("/proc/#{$$}/smaps") 83 | end 84 | end 85 | 86 | class ProcessStatusMemorySnapshot 87 | def memory 88 | SystemCall.execute("ps -o vsz= -p #{$$}").stdout.to_i 89 | end 90 | 91 | def self.available? 92 | SystemCall.execute("ps -o vsz= -p #{$$}").success? 93 | end 94 | end 95 | 96 | class SystemCall 97 | 98 | def initialize(cmd) 99 | @stdout = `#{cmd}` 100 | @process_status = $? 101 | end 102 | 103 | def self.execute(cmd) 104 | new(cmd) 105 | end 106 | 107 | def stdout 108 | @stdout 109 | end 110 | 111 | def success? 112 | @process_status.success? 113 | end 114 | 115 | end 116 | 117 | class MemoryDataUnavailableError < StandardError; end 118 | 119 | end 120 | 121 | end 122 | -------------------------------------------------------------------------------- /spec/oink/instrumentation/memory_snapshot_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | module Oink 4 | module Instrumentation 5 | 6 | describe StatmMemorySnapshot do 7 | 8 | before do 9 | StatmMemorySnapshot.unset_statm_page_size 10 | end 11 | 12 | it "returns memory when pagesize is 4096" do 13 | pages = 6271 14 | statm_file = "#{pages} 1157 411 1 0 763 0\n" 15 | File.should_receive(:read).with("/proc/self/statm").and_return(statm_file) 16 | 17 | system_call = mock(SystemCall, :stdout => "4096\n", :success? => true) 18 | SystemCall.should_receive(:execute).with('getconf PAGESIZE').and_return(system_call) 19 | StatmMemorySnapshot.new.memory.should == (pages * 4) 20 | end 21 | 22 | it "falls back to a 4096 if getconf PAGESIZE is not available" do 23 | pages = 6271 24 | statm_file = "#{pages} 1157 411 1 0 763 0\n" 25 | File.should_receive(:read).with("/proc/self/statm").and_return(statm_file) 26 | system_call = mock(SystemCall, :stdout => "", :success? => false) 27 | SystemCall.should_receive(:execute).with('getconf PAGESIZE').and_return(system_call) 28 | StatmMemorySnapshot.new.memory.should == (pages * 4) 29 | end 30 | end 31 | 32 | describe SmapsMemorySnapshot do 33 | 34 | it "returns a sum of the sizes in the /proc/$$/smaps file" do 35 | proc_file = <<-STR 36 | Header 37 | 38 | Size: 25 39 | Size: 13 trailing 40 | 41 | leading Size: 4 42 | 43 | Footer 44 | 45 | STR 46 | File.should_receive(:new).with("/proc/#{$$}/smaps").and_return(proc_file) 47 | SmapsMemorySnapshot.new.memory.should == 42 48 | end 49 | 50 | end 51 | 52 | describe ProcessStatusMemorySnapshot do 53 | it "returns the result of a PS command" do 54 | system_call = mock(SystemCall, :stdout => "915") 55 | SystemCall.should_receive(:execute).with("ps -o vsz= -p #{$$}").and_return(system_call) 56 | ProcessStatusMemorySnapshot.new.memory.should == 915 57 | end 58 | 59 | describe "#available?" do 60 | it "returns true if ps succeeds" do 61 | system_call = mock(SystemCall, :success? => true) 62 | SystemCall.should_receive(:execute).with("ps -o vsz= -p #{$$}").and_return(system_call) 63 | ProcessStatusMemorySnapshot.available?.should be_true 64 | end 65 | end 66 | end 67 | 68 | describe MemorySnapshot do 69 | describe "#memory_snapshot_class" do 70 | it "raises an Oink::MemoryDataUnavailableError if not strategies can be found" do 71 | [WindowsMemorySnapshot, StatmMemorySnapshot, SmapsMemorySnapshot, ProcessStatusMemorySnapshot].each { |klass| klass.stub(:available? => false) } 72 | 73 | lambda { MemorySnapshot.memory_snapshot_class }.should raise_error(MemoryDataUnavailableError) 74 | end 75 | 76 | it "returns the first available memory snapshot strategy" do 77 | [WindowsMemorySnapshot, SmapsMemorySnapshot, ProcessStatusMemorySnapshot].each { |klass| klass.stub(:available? => false) } 78 | StatmMemorySnapshot.stub(:available? => true) 79 | MemorySnapshot.memory_snapshot_class.should == StatmMemorySnapshot 80 | end 81 | end 82 | end 83 | end 84 | end -------------------------------------------------------------------------------- /oink.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "oink" 8 | s.version = "0.10.1" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Noah Davis"] 12 | s.date = "2013-02-06" 13 | s.description = "Log parser to identify actions which significantly increase VM heap size" 14 | s.email = "noahd1@yahoo.com" 15 | s.executables = ["oink"] 16 | s.extra_rdoc_files = [ 17 | "README.rdoc" 18 | ] 19 | s.files = [ 20 | "History.txt", 21 | "MIT-LICENSE", 22 | "README.rdoc", 23 | "Rakefile", 24 | "bin/oink", 25 | "lib/oink.rb", 26 | "lib/oink/cli.rb", 27 | "lib/oink/instrumentation.rb", 28 | "lib/oink/instrumentation/active_record.rb", 29 | "lib/oink/instrumentation/memory_snapshot.rb", 30 | "lib/oink/middleware.rb", 31 | "lib/oink/rails.rb", 32 | "lib/oink/rails/instance_type_counter.rb", 33 | "lib/oink/rails/memory_usage_logger.rb", 34 | "lib/oink/reports/active_record_instantiation_oinked_request.rb", 35 | "lib/oink/reports/active_record_instantiation_report.rb", 36 | "lib/oink/reports/base.rb", 37 | "lib/oink/reports/memory_oinked_request.rb", 38 | "lib/oink/reports/memory_usage_report.rb", 39 | "lib/oink/reports/priority_queue.rb", 40 | "lib/oink/reports/request.rb", 41 | "lib/oink/utils/hash_utils.rb" 42 | ] 43 | s.homepage = "http://github.com/noahd1/oink" 44 | s.require_paths = ["lib"] 45 | s.rubygems_version = "1.8.24" 46 | s.summary = "Log parser to identify actions which significantly increase VM heap size" 47 | 48 | if s.respond_to? :specification_version then 49 | s.specification_version = 3 50 | 51 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 52 | s.add_runtime_dependency(%q, [">= 0"]) 53 | s.add_runtime_dependency(%q, [">= 0"]) 54 | s.add_development_dependency(%q, [">= 0"]) 55 | s.add_development_dependency(%q, [">= 0"]) 56 | s.add_development_dependency(%q, [">= 0"]) 57 | s.add_development_dependency(%q, [">= 0"]) 58 | s.add_development_dependency(%q, [">= 0"]) 59 | s.add_development_dependency(%q, [">= 0"]) 60 | s.add_development_dependency(%q, [">= 0"]) 61 | else 62 | s.add_dependency(%q, [">= 0"]) 63 | s.add_dependency(%q, [">= 0"]) 64 | s.add_dependency(%q, [">= 0"]) 65 | s.add_dependency(%q, [">= 0"]) 66 | s.add_dependency(%q, [">= 0"]) 67 | s.add_dependency(%q, [">= 0"]) 68 | s.add_dependency(%q, [">= 0"]) 69 | s.add_dependency(%q, [">= 0"]) 70 | s.add_dependency(%q, [">= 0"]) 71 | end 72 | else 73 | s.add_dependency(%q, [">= 0"]) 74 | s.add_dependency(%q, [">= 0"]) 75 | s.add_dependency(%q, [">= 0"]) 76 | s.add_dependency(%q, [">= 0"]) 77 | s.add_dependency(%q, [">= 0"]) 78 | s.add_dependency(%q, [">= 0"]) 79 | s.add_dependency(%q, [">= 0"]) 80 | s.add_dependency(%q, [">= 0"]) 81 | s.add_dependency(%q, [">= 0"]) 82 | end 83 | end 84 | 85 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | {}[https://codeclimate.com/github/noahd1/oink] 2 | 3 | === Oink 4 | 5 | * http://github.com/noahd1/oink 6 | 7 | === Description 8 | 9 | Rails plugin and log parser to help narrow down the source(s) of increased memory usage in rails applications. 10 | 11 | === Synopsis 12 | 13 | Oink adds memory and active record instantiation information to rails log during runtime and provides an executable to help digest the enhanced logs. 14 | 15 | Given a minimum threshold and a metric (memory or active record instantiation), the oink executable reports: 16 | 17 | 1. The top ten single requests which exceeded the threshold for the metric, ordered by the request which exceeded the threshold the most 18 | 2. The number of times each action exceeded the threshold for the metric, ordered by the action which exceeded the threshold the most 19 | 3. (in verbose mode) The log lines produced by requests which exceeded the threshold 20 | 21 | Many thanks to Ben Johnson for Memory Usage Logger (http://github.com/binarylogic/memory_usage_logger/tree/master) which is the basis of this plugin. 22 | 23 | == Dependencies 24 | 25 | Currently oink can only parse logs in the Hodel3000Logger format 26 | 27 | - http://github.com/topfunky/hodel_3000_compliant_logger 28 | 29 | When used as a gem, this is automatically brought in as a dependency. 30 | 31 | === Installation 32 | 33 | Add oink to your Gemfile 34 | 35 | gem "oink" 36 | 37 | In most rails environments this is sufficient and oink will be required for you via bundler. 38 | If not, add a require statement "require 'oink'" in your app. 39 | 40 | === Configuration 41 | 42 | Oink is middleware for instrumentation during the runtime of your application as well as log parsers for offline crunching of the logs generated by 43 | the middleware. 44 | 45 | The middleware class to include into your stack is "Oink::Middleware". For rails using an initializer is recommended: 46 | 47 | Rails.application.middleware.use Oink::Middleware 48 | 49 | Oink::Middleware writes log entries to log/oink.log in your application root directory by default. 50 | 51 | You can also initialize it with an optional logger instance enabling your application to write Oink log entries to your application's default log file: 52 | 53 | Rails.application.middleware.use( Oink::Middleware, :logger => Rails.logger ) 54 | 55 | (This setup enables Oink to work with Request Log Analyzer (https://github.com/wvanbergen/request-log-analyzer), which currently parses rails logs) 56 | 57 | Oink::Middleware logs memory and activerecord usage by default. 58 | 59 | You can configure which using the :instruments option. For memory only: 60 | 61 | Rails.application.middleware.use( Oink::Middleware, :instruments => :memory ) 62 | 63 | For activerecord instantiation counts only: 64 | 65 | Rails.application.middleware.use( Oink::Middleware, :instruments => :activerecord ) 66 | 67 | Note that the previous way of configuring oink, as a set of modules to include into rails controllers, is deprecated. 68 | 69 | == Analyzing logs 70 | 71 | After installing the plugin and aggregating some enhanced logs, run the 'oink' executable against your server logs. 72 | 73 | Usage: oink [options] files 74 | -t, --threshold [INTEGER] Memory threshold in MB 75 | -f, --file filepath Output to file 76 | --format FORMAT Select format 77 | (ss,v,s,verbose,short-summary,summary) 78 | -m, --memory Check for Memory Threshold (default) 79 | -r, --active-record Check for Active Record Threshold 80 | 81 | Oink hunts for requests which exceed a given threshold. In "memory" mode (the default), the threshold represents a megabyte memory increase from the previous request. In "active record" mode (turned on by passing the --active-record switch), the threshold represents the number of active record objects instantiated during a request. 82 | 83 | e.g. To find all actions which increase the heap size more than 75 MB, where log files are location in /tmp/logs/ 84 | 85 | $ oink --threshold=75 /tmp/logs/* 86 | ---- MEMORY THRESHOLD ---- 87 | THRESHOLD: 75 MB 88 | 89 | -- SUMMARY -- 90 | Worst Requests: 91 | 1. Feb 02 16:26:06, 157524 KB, SportsController#show 92 | 2. Feb 02 20:11:54, 134972 KB, DashboardsController#show 93 | 3. Feb 02 19:06:13, 131912 KB, DashboardsController#show 94 | 4. Feb 02 08:07:46, 115448 KB, GroupsController#show 95 | 5. Feb 02 12:19:53, 112924 KB, GroupsController#show 96 | 6. Feb 02 13:03:00, 112064 KB, ColorSchemesController#show 97 | 7. Feb 02 13:01:59, 109148 KB, SessionsController#create 98 | 8. Feb 02 06:11:17, 108456 KB, PublicPagesController#join 99 | 9. Feb 02 08:43:06, 94468 KB, CommentsController#create 100 | 10. Feb 02 20:49:44, 82340 KB, DashboardsController#show 101 | 102 | Worst Actions: 103 | 10, DashboardsController#show 104 | 9, GroupsController#show 105 | 5, PublicPagesController#show 106 | 5, UsersController#show 107 | 3, MediaController#show 108 | 2, SportsController#show 109 | 1, SessionsController#create 110 | 1, GroupInvitesController#by_email 111 | 1, MediaController#index 112 | 1, PostsController#show 113 | 1, PhotoVotesController#create 114 | 1, AlbumsController#index 115 | 1, SignupsController#new 116 | 1, ColorSchemesController#show 117 | 1, PublicPagesController#join 118 | 1, CommentsController#create 119 | 120 | Aggregated Totals: 121 | Action Max Mean Min Total Number of requests 122 | SportsController#show 101560 19754 4 5590540 283 123 | CommentsController#create 8344 701 4 253324 361 124 | ColorSchemesController#show 10124 739 4 68756 93 125 | PublicPagesController#join 9004 1346 8 51172 38 126 | DashboardsController#show 13696 2047 8 45036 22 127 | SessionsController#create 9220 528 8 17448 33 128 | GroupsController#show 10748 1314 8 15776 12 129 | 130 | e.g. In verbose mode, oink will print out all the log information from your logs about the actions which exceeded the threshold specified 131 | 132 | $ oink --format verbose --threshold=75 /tmp/logs/* 133 | 134 | --------------------------------------------------------------------- 135 | 136 | Feb 08 11:39:52 ey33-s00302 rails[9076]: Processing UsersController#show (for 11.187.34.45 at 2009-02-08 11:39:52) [GET] 137 | Feb 08 11:39:52 ey33-s00302 rails[9076]: Parameters: {"action"=>"show", "id"=>"45", "controller"=>"users"} 138 | Feb 08 11:39:52 ey33-s00302 rails[9076]: Rendering template within layouts/application 139 | Feb 08 11:39:52 ey33-s00302 rails[9076]: Rendering users/show 140 | Feb 08 11:39:54 ey33-s00302 rails[9076]: Completed in 2008ms (View: 1136, DB: 264) | 200 OK [http://www.example.com/users/45] 141 | Feb 08 11:39:52 ey33-s00302 rails[9076]: Oink Action: users#show 142 | Feb 08 11:39:54 ey33-s00302 rails[9076]: Memory usage: 316516 | PID: 9076 143 | Feb 08 11:39:54 ey33-s00302 rails[9076]: Oink Log Entry Complete 144 | --------------------------------------------------------------------- 145 | 146 | Verbose format prints the summary as well as each action which exceeded the threshold. 147 | 148 | === Authors 149 | 150 | - Maintained by Noah Davis 151 | - Thanks to Weplay (http://weplay.com) for sponsoring development and supporting open sourcing it from the start 152 | -------------------------------------------------------------------------------- /spec/oink/reports/active_record_instantiation_report_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | module Oink::Reports 4 | describe ActiveRecordInstantiationReport do 5 | 6 | describe "short summary with frequent offenders" do 7 | 8 | it "should report actions which exceed the threshold once" do 9 | str = <<-STR 10 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 11 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 12 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 13 | STR 14 | 15 | io = StringIO.new(str) 16 | output = PsuedoOutput.new 17 | ActiveRecordInstantiationReport.new(io, 50).print(output) 18 | output.should include("1, Users#show") 19 | end 20 | 21 | it "should not report actions which do not exceed the threshold" do 22 | str = <<-STR 23 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 24 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 50 | User: 50 25 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 26 | STR 27 | 28 | io = StringIO.new(str) 29 | output = PsuedoOutput.new 30 | ActiveRecordInstantiationReport.new(io, 50).print(output) 31 | output.should_not include("1, Users#show") 32 | end 33 | 34 | it "should report actions which exceed the threshold multiple times" do 35 | str = <<-STR 36 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 37 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 38 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 39 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 40 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 41 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 42 | STR 43 | 44 | io = StringIO.new(str) 45 | output = PsuedoOutput.new 46 | ActiveRecordInstantiationReport.new(io, 50).print(output) 47 | output.should include("2, Users#show") 48 | end 49 | 50 | it "should order actions by most exceeded" do 51 | str = <<-STR 52 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 53 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 54 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 55 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 56 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 57 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 58 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 59 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 60 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 61 | STR 62 | 63 | io = StringIO.new(str) 64 | output = PsuedoOutput.new 65 | ActiveRecordInstantiationReport.new(io, 50).print(output) 66 | output[8].should == "2, Media#show" 67 | output[9].should == "1, Users#show" 68 | end 69 | 70 | it "should not be bothered by incomplete requests" do 71 | str = <<-STR 72 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 73 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 24 | User: 24 74 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 75 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 76 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 77 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 78 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 79 | STR 80 | 81 | io = StringIO.new(str) 82 | output = PsuedoOutput.new 83 | ActiveRecordInstantiationReport.new(io, 50).print(output) 84 | output.should include("1, Users#show") 85 | end 86 | 87 | end 88 | 89 | describe "summary with top 10 offenses" do 90 | 91 | it "should only report requests over threshold" do 92 | str = <<-STR 93 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 94 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 95 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 96 | STR 97 | 98 | io = StringIO.new(str) 99 | output = PsuedoOutput.new 100 | ActiveRecordInstantiationReport.new(io, 50).print(output) 101 | output.should include("1. Feb 01 01:58:31, 51, Users#show") 102 | end 103 | 104 | it "should not include requests which are not over threshold" do 105 | str = <<-STR 106 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 107 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 50 | User: 50 108 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 109 | STR 110 | 111 | io = StringIO.new(str) 112 | output = PsuedoOutput.new 113 | ActiveRecordInstantiationReport.new(io, 50).print(output) 114 | output.should_not include("1. Feb 01 01:58:31, 50, Users#show") 115 | end 116 | 117 | it "should order offenses from biggest to smallest" do 118 | str = <<-STR 119 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Details#show 120 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 75 | User: 75 121 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 122 | Feb 01 01:58:32 ey04-s00297 rails[4413]: Oink Action: Media#show 123 | Feb 01 01:58:33 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 100 | User: 100 124 | Feb 01 01:58:34 ey04-s00297 rails[4413]: Oink Log Entry Complete 125 | STR 126 | 127 | io = StringIO.new(str) 128 | output = PsuedoOutput.new 129 | ActiveRecordInstantiationReport.new(io, 50).print(output) 130 | output[4].should == "1. Feb 01 01:58:34, 100, Media#show" 131 | output[5].should == "2. Feb 01 01:58:31, 75, Details#show" 132 | end 133 | 134 | end 135 | 136 | describe "verbose format" do 137 | it "should print the full lines of actions exceeding the threshold" do 138 | str = <<-STR 139 | Feb 01 01:58:32 ey04-s00297 rails[4413]: Oink Action: Media#show 140 | Feb 01 01:58:33 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 100 | User: 100 141 | Feb 01 01:58:34 ey04-s00297 rails[4413]: Oink Log Entry Complete 142 | STR 143 | io = StringIO.new(str) 144 | output = PsuedoOutput.new 145 | ActiveRecordInstantiationReport.new(io, 50, :format => :verbose).print(output) 146 | output[3..5].should == str.split("\n")[0..2].map { |o| o.strip } 147 | end 148 | 149 | it "should handle actions which do not complete properly" do 150 | str = <<-STR 151 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 152 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 24 | User: 24 153 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 154 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 155 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 156 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 51 | User: 51 157 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 158 | STR 159 | 160 | io = StringIO.new(str) 161 | output = PsuedoOutput.new 162 | ActiveRecordInstantiationReport.new(io, 50, :format => :verbose).print(output) 163 | output[3..5].should == str.split("\n")[4..6].map { |o| o.strip } 164 | end 165 | end 166 | 167 | describe "multiple io streams" do 168 | it "should accept multiple files" do 169 | 170 | str1 = <<-STR 171 | Feb 01 01:58:32 ey04-s00297 rails[4413]: Oink Action: Media#show 172 | Feb 01 01:58:33 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 100 | User: 100 173 | Feb 01 01:58:34 ey04-s00297 rails[4413]: Oink Log Entry Complete 174 | STR 175 | 176 | str2 = <<-STR 177 | Feb 01 01:58:32 ey04-s00297 rails[4413]: Oink Action: Media#show 178 | Feb 01 01:58:33 ey04-s00297 rails[4413]: Instantiation Breakdown: Total: 100 | User: 100 179 | Feb 01 01:58:34 ey04-s00297 rails[4413]: Oink Log Entry Complete 180 | STR 181 | 182 | io1 = StringIO.new(str1) 183 | io2 = StringIO.new(str2) 184 | output = PsuedoOutput.new 185 | ActiveRecordInstantiationReport.new([io1, io2], 50).print(output) 186 | output.should include("2, Media#show") 187 | end 188 | 189 | end 190 | 191 | 192 | end 193 | end 194 | -------------------------------------------------------------------------------- /spec/oink/reports/memory_usage_report_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") 2 | 3 | module Oink::Reports 4 | describe MemoryUsageReport do 5 | 6 | TEN_MEGS = 10 * 1024 7 | 8 | describe "short summary with frequent offenders" do 9 | 10 | it "should report actions which exceed the threshold once" do 11 | str = <<-STR 12 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 13 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 14 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 15 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 16 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 17 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 18 | STR 19 | 20 | io = StringIO.new(str) 21 | output = PsuedoOutput.new 22 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 23 | output.should include("1, Media#show") 24 | end 25 | 26 | it "should not report actions which do not exceed the threshold" do 27 | threshold = 10 28 | 29 | str = <<-STR 30 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 31 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 32 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 33 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 34 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS} | PID: 4413 35 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 36 | STR 37 | 38 | io = StringIO.new(str) 39 | output = PsuedoOutput.new 40 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 41 | output.should_not include("1, Media#show") 42 | end 43 | 44 | it "should report actions which exceed the threshold multiple times" do 45 | str = <<-STR 46 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 47 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 48 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 49 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 50 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 51 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 52 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 53 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{(TEN_MEGS * 2) + 2} | PID: 4413 54 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 55 | STR 56 | 57 | io = StringIO.new(str) 58 | output = PsuedoOutput.new 59 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 60 | output.should include("2, Media#show") 61 | end 62 | 63 | it "should order actions by most exceeded" do 64 | str = <<-STR 65 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 66 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 67 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 68 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 69 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 70 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 71 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 72 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{(TEN_MEGS * 2) + 2} | PID: 4413 73 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 74 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 75 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{(TEN_MEGS * 3) + 3} | PID: 4413 76 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 77 | STR 78 | 79 | io = StringIO.new(str) 80 | output = PsuedoOutput.new 81 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 82 | output[8].should == "2, Media#show" 83 | output[9].should == "1, Users#show" 84 | end 85 | 86 | it "should not report actions which do not complete properly" do 87 | threshold = 10 88 | 89 | str = <<-STR 90 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 91 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 92 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 93 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 94 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 95 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 96 | STR 97 | 98 | io = StringIO.new(str) 99 | output = PsuedoOutput.new 100 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 101 | output.should_not include("1, Media#show") 102 | end 103 | 104 | it "should not report actions from different pids" do 105 | str = <<-STR 106 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 107 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 108 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 109 | Feb 01 01:58:29 ey04-s00297 rails[5513]: Oink Action: Media#show 110 | Feb 01 01:58:30 ey04-s00297 rails[5513]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 111 | Feb 01 01:58:30 ey04-s00297 rails[5513]: Oink Log Entry Complete 112 | STR 113 | 114 | io = StringIO.new(str) 115 | output = PsuedoOutput.new 116 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 117 | output.should_not include("1, Media#show") 118 | end 119 | 120 | describe "summary with top 10 offenses" do 121 | 122 | it "should only report requests over threshold" do 123 | str = <<-STR 124 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 125 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 126 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 127 | Feb 01 01:58:32 ey04-s00297 rails[4413]: Oink Action: Media#show 128 | Feb 01 01:58:33 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 129 | Feb 01 01:58:34 ey04-s00297 rails[4413]: Oink Log Entry Complete 130 | STR 131 | 132 | io = StringIO.new(str) 133 | output = PsuedoOutput.new 134 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 135 | output.should include("1. Feb 01 01:58:34, #{TEN_MEGS + 1} KB, Media#show") 136 | end 137 | 138 | it "should not include requests which are not over the threshold" do 139 | str = <<-STR 140 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 141 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 142 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 143 | Feb 01 01:58:32 ey04-s00297 rails[4413]: Oink Action: Media#show 144 | Feb 01 01:58:33 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS} | PID: 4413 145 | Feb 01 01:58:34 ey04-s00297 rails[4413]: Oink Log Entry Complete 146 | STR 147 | 148 | io = StringIO.new(str) 149 | output = PsuedoOutput.new 150 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 151 | output.should_not include("1. Feb 01 01:58:34, #{TEN_MEGS + 1} KB, Media#show") 152 | end 153 | 154 | it "should order offenses from biggest to smallest" do 155 | str = <<-STR 156 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 157 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 158 | Feb 01 01:58:31 ey04-s00297 rails[4413]: Oink Log Entry Complete 159 | Feb 01 01:58:32 ey04-s00297 rails[4413]: Oink Action: Media#show 160 | Feb 01 01:58:33 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 161 | Feb 01 01:58:34 ey04-s00297 rails[4413]: Oink Log Entry Complete 162 | Feb 01 01:58:35 ey04-s00297 rails[4413]: Oink Action: Details#show 163 | Feb 01 01:58:36 ey04-s00297 rails[4413]: Memory usage: #{(TEN_MEGS * 2) + 2} | PID: 4413 164 | Feb 01 01:58:37 ey04-s00297 rails[4413]: Oink Log Entry Complete 165 | STR 166 | 167 | io = StringIO.new(str) 168 | output = PsuedoOutput.new 169 | MemoryUsageReport.new(io, TEN_MEGS).print(output) 170 | output[4].should == "1. Feb 01 01:58:34, #{TEN_MEGS + 1} KB, Media#show" 171 | output[5].should == "2. Feb 01 01:58:37, #{TEN_MEGS + 1} KB, Details#show" 172 | end 173 | 174 | end 175 | 176 | # it "should report the time span" do 177 | # str = <<-STR 178 | # Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 179 | # Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 180 | # Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 181 | # Mar 13 01:58:29 ey04-s00297 rails[5513]: Oink Action: Media#show 182 | # Mar 13 01:58:30 ey04-s00297 rails[5513]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 183 | # Mar 13 03:58:30 ey04-s00297 rails[5513]: Oink Log Entry Complete 184 | # STR 185 | # 186 | # io = StringIO.new(str) 187 | # output = PsuedoOutput.new 188 | # MemoryUsageReport.new(io, TEN_MEGS).each_line do |line| 189 | # output << line 190 | # end 191 | # output.first.should == "Feb 01 01:58:29 - Mar 13 03:58:30" 192 | # end 193 | 194 | end 195 | 196 | describe "verbose format" do 197 | it "should print the full lines of actions exceeding the threshold" do 198 | str = <<-STR 199 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 200 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"2332", "controller"=>"users"} 201 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 202 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 203 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 204 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"22900", "controller"=>"media"} 205 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 206 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 207 | STR 208 | io = StringIO.new(str) 209 | output = PsuedoOutput.new 210 | MemoryUsageReport.new(io, TEN_MEGS, :format => :verbose).print(output) 211 | output[3..6].should == str.split("\n")[4..7].map { |o| o.strip } 212 | end 213 | 214 | it "should handle actions which do not complete properly" do 215 | threshold = 10 216 | 217 | str = <<-STR 218 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 219 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 220 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 221 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 222 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 223 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 224 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Processing ActorController#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 225 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{(TEN_MEGS * 2) + 2} | PID: 4413 226 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 227 | STR 228 | 229 | io = StringIO.new(str) 230 | output = PsuedoOutput.new 231 | MemoryUsageReport.new(io, TEN_MEGS, :format => :verbose).print(output) 232 | output[3..5].should == str.split("\n")[6..8].map { |o| o.strip } 233 | end 234 | end 235 | 236 | describe "averaged format" do 237 | it "should correctly calculate the stats for the actions" do 238 | str = <<-STR 239 | Feb 01 01:59:29 ey04-s00297 rails[4413]: Processing Users#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 240 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 241 | Feb 01 01:57:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"2332", "controller"=>"users"} 242 | Feb 01 01:57:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 10} | PID: 4413 243 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 244 | Feb 01 01:57:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 245 | Feb 01 01:59:29 ey04-s00297 rails[4413]: Processing Users#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 246 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 247 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"22900", "controller"=>"media"} 248 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{(TEN_MEGS*3) + 40} | PID: 4413 249 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 250 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 251 | Feb 01 01:59:29 ey04-s00297 rails[4413]: Processing Users#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 252 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 253 | Feb 01 01:59:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"22900", "controller"=>"media"} 254 | Feb 01 01:59:30 ey04-s00297 rails[4413]: Memory usage: #{(TEN_MEGS*8) + 40} | PID: 4413 255 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 256 | Feb 01 01:59:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 257 | STR 258 | io = StringIO.new(str) 259 | output = PsuedoOutput.new 260 | MemoryUsageReport.new(io, TEN_MEGS, :format => :averaged).print(output) 261 | output.last.should == "Users#show\t51200\t35855\t20510\t71710\t2" 262 | end 263 | it "should sort by the total memory increase" do 264 | str = <<-STR 265 | Feb 01 01:57:29 ey04-s00297 rails[4413]: Processing Users#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 266 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 267 | Feb 01 01:57:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"2332", "controller"=>"users"} 268 | Feb 01 01:57:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 10} | PID: 4413 269 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 270 | Feb 01 01:57:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 271 | Feb 01 01:57:29 ey04-s00297 rails[4413]: Processing Users#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 272 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 273 | Feb 01 01:57:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"2332", "controller"=>"users"} 274 | Feb 01 01:57:30 ey04-s00297 rails[4413]: Memory usage: #{2*(TEN_MEGS + 10)} | PID: 4413 275 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 276 | Feb 01 01:57:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 277 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Processing Users#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 278 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 279 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"22900", "controller"=>"media"} 280 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{3*(TEN_MEGS + 10)} | PID: 4413 281 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 282 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 283 | Feb 01 02:57:29 ey04-s00297 rails[4413]: Processing Users2#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 284 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users2#show 285 | Feb 01 02:57:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"2332", "controller"=>"users"} 286 | Feb 01 02:57:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 20} | PID: 4413 287 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 288 | Feb 01 02:57:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 289 | Feb 01 02:58:29 ey04-s00297 rails[4413]: Processing Users2#show (for 92.84.151.171 at 2009-02-01 01:58:29) [GET] 290 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users2#show 291 | Feb 01 02:58:29 ey04-s00297 rails[4413]: Parameters: {"id"=>"22900", "controller"=>"media"} 292 | Feb 01 02:58:30 ey04-s00297 rails[4413]: Memory usage: #{2*(TEN_MEGS + 20)} | PID: 4413 293 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 294 | Feb 01 02:58:30 ey04-s00297 rails[4413]: Completed in 984ms (View: 840, DB: 4) | 200 OK 295 | STR 296 | io = StringIO.new(str) 297 | output = PsuedoOutput.new 298 | MemoryUsageReport.new(io, TEN_MEGS, :format => :averaged).print(output) 299 | output[12].should include('Users#show') 300 | output[13].should include('Users2#show') 301 | end 302 | end 303 | 304 | describe "multiple io streams" do 305 | it "should accept multiple files" do 306 | 307 | str1 = <<-STR 308 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 309 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 310 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 311 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 312 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 313 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 314 | STR 315 | 316 | str2 = <<-STR 317 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Users#show 318 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: 0 | PID: 4413 319 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 320 | Feb 01 01:58:29 ey04-s00297 rails[4413]: Oink Action: Media#show 321 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Memory usage: #{TEN_MEGS + 1} | PID: 4413 322 | Feb 01 01:58:30 ey04-s00297 rails[4413]: Oink Log Entry Complete 323 | STR 324 | 325 | io1 = StringIO.new(str1) 326 | io2 = StringIO.new(str2) 327 | output = PsuedoOutput.new 328 | MemoryUsageReport.new([io1, io2], TEN_MEGS).print(output) 329 | output.should include("2, Media#show") 330 | end 331 | 332 | end 333 | 334 | end 335 | end 336 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | # Disable all checks not explicitly referenced in this file 2 | # This is used to easily disable Style/* checks 3 | AllCops: 4 | DisabledByDefault: true 5 | 6 | ################## STYLE ################################# 7 | 8 | Style/AccessModifierIndentation: 9 | Description: Check indentation of private/protected visibility modifiers. 10 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected' 11 | Enabled: false 12 | 13 | Style/AccessorMethodName: 14 | Description: Check the naming of accessor methods for get_/set_. 15 | Enabled: false 16 | 17 | Style/Alias: 18 | Description: 'Use alias_method instead of alias.' 19 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' 20 | Enabled: false 21 | 22 | Style/AlignArray: 23 | Description: >- 24 | Align the elements of an array literal if they span more than 25 | one line. 26 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' 27 | Enabled: false 28 | 29 | Style/AlignHash: 30 | Description: >- 31 | Align the elements of a hash literal if they span more than 32 | one line. 33 | Enabled: false 34 | 35 | Style/AlignParameters: 36 | Description: >- 37 | Align the parameters of a method call if they span more 38 | than one line. 39 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' 40 | Enabled: false 41 | 42 | Style/AndOr: 43 | Description: 'Use &&/|| instead of and/or.' 44 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or' 45 | Enabled: false 46 | 47 | Style/ArrayJoin: 48 | Description: 'Use Array#join instead of Array#*.' 49 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' 50 | Enabled: false 51 | 52 | Style/AsciiComments: 53 | Description: 'Use only ascii symbols in comments.' 54 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' 55 | Enabled: false 56 | 57 | Style/AsciiIdentifiers: 58 | Description: 'Use only ascii symbols in identifiers.' 59 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' 60 | Enabled: false 61 | 62 | Style/Attr: 63 | Description: 'Checks for uses of Module#attr.' 64 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' 65 | Enabled: false 66 | 67 | Style/BeginBlock: 68 | Description: 'Avoid the use of BEGIN blocks.' 69 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks' 70 | Enabled: false 71 | 72 | Style/BarePercentLiterals: 73 | Description: 'Checks if usage of %() or %Q() matches configuration.' 74 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand' 75 | Enabled: false 76 | 77 | Style/BlockComments: 78 | Description: 'Do not use block comments.' 79 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments' 80 | Enabled: false 81 | 82 | Style/BlockEndNewline: 83 | Description: 'Put end statement of multiline block on its own line.' 84 | Enabled: false 85 | 86 | Style/BlockDelimiters: 87 | Description: >- 88 | Avoid using {...} for multi-line blocks (multiline chaining is 89 | always ugly). 90 | Prefer {...} over do...end for single-line blocks. 91 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' 92 | Enabled: false 93 | 94 | Style/BracesAroundHashParameters: 95 | Description: 'Enforce braces style around hash parameters.' 96 | Enabled: false 97 | 98 | Style/CaseEquality: 99 | Description: 'Avoid explicit use of the case equality operator(===).' 100 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' 101 | Enabled: false 102 | 103 | Style/CaseIndentation: 104 | Description: 'Indentation of when in a case/when/[else/]end.' 105 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case' 106 | Enabled: false 107 | 108 | Style/CharacterLiteral: 109 | Description: 'Checks for uses of character literals.' 110 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' 111 | Enabled: false 112 | 113 | Style/ClassAndModuleCamelCase: 114 | Description: 'Use CamelCase for classes and modules.' 115 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes' 116 | Enabled: false 117 | 118 | Style/ClassAndModuleChildren: 119 | Description: 'Checks style of children classes and modules.' 120 | Enabled: false 121 | 122 | Style/ClassCheck: 123 | Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' 124 | Enabled: false 125 | 126 | Style/ClassMethods: 127 | Description: 'Use self when defining module/class methods.' 128 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-class-methods' 129 | Enabled: false 130 | 131 | Style/ClassVars: 132 | Description: 'Avoid the use of class variables.' 133 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' 134 | Enabled: false 135 | 136 | Style/ClosingParenthesisIndentation: 137 | Description: 'Checks the indentation of hanging closing parentheses.' 138 | Enabled: false 139 | 140 | Style/ColonMethodCall: 141 | Description: 'Do not use :: for method call.' 142 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' 143 | Enabled: false 144 | 145 | Style/CommandLiteral: 146 | Description: 'Use `` or %x around command literals.' 147 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' 148 | Enabled: false 149 | 150 | Style/CommentAnnotation: 151 | Description: >- 152 | Checks formatting of special comments 153 | (TODO, FIXME, OPTIMIZE, HACK, REVIEW). 154 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' 155 | Enabled: false 156 | 157 | Style/CommentIndentation: 158 | Description: 'Indentation of comments.' 159 | Enabled: false 160 | 161 | Style/ConstantName: 162 | Description: 'Constants should use SCREAMING_SNAKE_CASE.' 163 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case' 164 | Enabled: false 165 | 166 | Style/DefWithParentheses: 167 | Description: 'Use def with parentheses when there are arguments.' 168 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' 169 | Enabled: false 170 | 171 | Style/DeprecatedHashMethods: 172 | Description: 'Checks for use of deprecated Hash methods.' 173 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' 174 | Enabled: false 175 | 176 | Style/Documentation: 177 | Description: 'Document classes and non-namespace modules.' 178 | Enabled: false 179 | 180 | Style/DotPosition: 181 | Description: 'Checks the position of the dot in multi-line method calls.' 182 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' 183 | Enabled: false 184 | 185 | Style/DoubleNegation: 186 | Description: 'Checks for uses of double negation (!!).' 187 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' 188 | Enabled: false 189 | 190 | Style/EachWithObject: 191 | Description: 'Prefer `each_with_object` over `inject` or `reduce`.' 192 | Enabled: false 193 | 194 | Style/ElseAlignment: 195 | Description: 'Align elses and elsifs correctly.' 196 | Enabled: false 197 | 198 | Style/EmptyElse: 199 | Description: 'Avoid empty else-clauses.' 200 | Enabled: false 201 | 202 | Style/EmptyLineBetweenDefs: 203 | Description: 'Use empty lines between defs.' 204 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods' 205 | Enabled: false 206 | 207 | Style/EmptyLines: 208 | Description: "Don't use several empty lines in a row." 209 | Enabled: false 210 | 211 | Style/EmptyLinesAroundAccessModifier: 212 | Description: "Keep blank lines around access modifiers." 213 | Enabled: false 214 | 215 | Style/EmptyLinesAroundBlockBody: 216 | Description: "Keeps track of empty lines around block bodies." 217 | Enabled: false 218 | 219 | Style/EmptyLinesAroundClassBody: 220 | Description: "Keeps track of empty lines around class bodies." 221 | Enabled: false 222 | 223 | Style/EmptyLinesAroundModuleBody: 224 | Description: "Keeps track of empty lines around module bodies." 225 | Enabled: false 226 | 227 | Style/EmptyLinesAroundMethodBody: 228 | Description: "Keeps track of empty lines around method bodies." 229 | Enabled: false 230 | 231 | Style/EmptyLiteral: 232 | Description: 'Prefer literals to Array.new/Hash.new/String.new.' 233 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' 234 | Enabled: false 235 | 236 | Style/EndBlock: 237 | Description: 'Avoid the use of END blocks.' 238 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks' 239 | Enabled: false 240 | 241 | Style/EndOfLine: 242 | Description: 'Use Unix-style line endings.' 243 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' 244 | Enabled: false 245 | 246 | Style/EvenOdd: 247 | Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' 248 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 249 | Enabled: false 250 | 251 | Style/ExtraSpacing: 252 | Description: 'Do not use unnecessary spacing.' 253 | Enabled: false 254 | 255 | Style/FileName: 256 | Description: 'Use snake_case for source file names.' 257 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' 258 | Enabled: false 259 | 260 | Style/InitialIndentation: 261 | Description: >- 262 | Checks the indentation of the first non-blank non-comment line in a file. 263 | Enabled: false 264 | 265 | Style/FirstParameterIndentation: 266 | Description: 'Checks the indentation of the first parameter in a method call.' 267 | Enabled: false 268 | 269 | Style/FlipFlop: 270 | Description: 'Checks for flip flops' 271 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' 272 | Enabled: false 273 | 274 | Style/For: 275 | Description: 'Checks use of for or each in multiline loops.' 276 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops' 277 | Enabled: false 278 | 279 | Style/FormatString: 280 | Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' 281 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' 282 | Enabled: false 283 | 284 | Style/GlobalVars: 285 | Description: 'Do not introduce global variables.' 286 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' 287 | Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html' 288 | Enabled: false 289 | 290 | Style/GuardClause: 291 | Description: 'Check for conditionals that can be replaced with guard clauses' 292 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 293 | Enabled: false 294 | 295 | Style/HashSyntax: 296 | Description: >- 297 | Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax 298 | { :a => 1, :b => 2 }. 299 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals' 300 | Enabled: false 301 | 302 | Style/IfUnlessModifier: 303 | Description: >- 304 | Favor modifier if/unless usage when you have a 305 | single-line body. 306 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' 307 | Enabled: false 308 | 309 | Style/IfWithSemicolon: 310 | Description: 'Do not use if x; .... Use the ternary operator instead.' 311 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' 312 | Enabled: false 313 | 314 | Style/IndentationConsistency: 315 | Description: 'Keep indentation straight.' 316 | Enabled: false 317 | 318 | Style/IndentationWidth: 319 | Description: 'Use 2 spaces for indentation.' 320 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' 321 | Enabled: false 322 | 323 | Style/IndentArray: 324 | Description: >- 325 | Checks the indentation of the first element in an array 326 | literal. 327 | Enabled: false 328 | 329 | Style/IndentHash: 330 | Description: 'Checks the indentation of the first key in a hash literal.' 331 | Enabled: false 332 | 333 | Style/InfiniteLoop: 334 | Description: 'Use Kernel#loop for infinite loops.' 335 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop' 336 | Enabled: false 337 | 338 | Style/Lambda: 339 | Description: 'Use the new lambda literal syntax for single-line blocks.' 340 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' 341 | Enabled: false 342 | 343 | Style/LambdaCall: 344 | Description: 'Use lambda.call(...) instead of lambda.(...).' 345 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' 346 | Enabled: false 347 | 348 | Style/LeadingCommentSpace: 349 | Description: 'Comments should start with a space.' 350 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space' 351 | Enabled: false 352 | 353 | Style/LineEndConcatenation: 354 | Description: >- 355 | Use \ instead of + or << to concatenate two string literals at 356 | line end. 357 | Enabled: false 358 | 359 | Style/MethodCallParentheses: 360 | Description: 'Do not use parentheses for method calls with no arguments.' 361 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens' 362 | Enabled: false 363 | 364 | Style/MethodDefParentheses: 365 | Description: >- 366 | Checks if the method definitions have or don't have 367 | parentheses. 368 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' 369 | Enabled: false 370 | 371 | Style/MethodName: 372 | Description: 'Use the configured style when naming methods.' 373 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' 374 | Enabled: false 375 | 376 | Style/ModuleFunction: 377 | Description: 'Checks for usage of `extend self` in modules.' 378 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' 379 | Enabled: false 380 | 381 | Style/MultilineBlockChain: 382 | Description: 'Avoid multi-line chains of blocks.' 383 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' 384 | Enabled: false 385 | 386 | Style/MultilineBlockLayout: 387 | Description: 'Ensures newlines after multiline block do statements.' 388 | Enabled: false 389 | 390 | Style/MultilineIfThen: 391 | Description: 'Do not use then for multi-line if/unless.' 392 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then' 393 | Enabled: false 394 | 395 | Style/MultilineOperationIndentation: 396 | Description: >- 397 | Checks indentation of binary operations that span more than 398 | one line. 399 | Enabled: false 400 | 401 | Style/MultilineTernaryOperator: 402 | Description: >- 403 | Avoid multi-line ?: (the ternary operator); 404 | use if/unless instead. 405 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' 406 | Enabled: false 407 | 408 | Style/NegatedIf: 409 | Description: >- 410 | Favor unless over if for negative conditions 411 | (or control flow or). 412 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' 413 | Enabled: false 414 | 415 | Style/NegatedWhile: 416 | Description: 'Favor until over while for negative conditions.' 417 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' 418 | Enabled: false 419 | 420 | Style/NestedTernaryOperator: 421 | Description: 'Use one expression per branch in a ternary operator.' 422 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary' 423 | Enabled: false 424 | 425 | Style/Next: 426 | Description: 'Use `next` to skip iteration instead of a condition at the end.' 427 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 428 | Enabled: false 429 | 430 | Style/NilComparison: 431 | Description: 'Prefer x.nil? to x == nil.' 432 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 433 | Enabled: false 434 | 435 | Style/NonNilCheck: 436 | Description: 'Checks for redundant nil checks.' 437 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks' 438 | Enabled: false 439 | 440 | Style/Not: 441 | Description: 'Use ! instead of not.' 442 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' 443 | Enabled: false 444 | 445 | Style/NumericLiterals: 446 | Description: >- 447 | Add underscores to large numeric literals to improve their 448 | readability. 449 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' 450 | Enabled: false 451 | 452 | Style/OneLineConditional: 453 | Description: >- 454 | Favor the ternary operator(?:) over 455 | if/then/else/end constructs. 456 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' 457 | Enabled: false 458 | 459 | Style/OpMethod: 460 | Description: 'When defining binary operators, name the argument other.' 461 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' 462 | Enabled: false 463 | 464 | Style/OptionalArguments: 465 | Description: >- 466 | Checks for optional arguments that do not appear at the end 467 | of the argument list 468 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#optional-arguments' 469 | Enabled: false 470 | 471 | Style/ParallelAssignment: 472 | Description: >- 473 | Check for simple usages of parallel assignment. 474 | It will only warn when the number of variables 475 | matches on both sides of the assignment. 476 | This also provides performance benefits 477 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment' 478 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#parallel-assignment-vs-sequential-assignment-code' 479 | Enabled: false 480 | 481 | Style/ParenthesesAroundCondition: 482 | Description: >- 483 | Don't use parentheses around the condition of an 484 | if/unless/while. 485 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if' 486 | Enabled: false 487 | 488 | Style/PercentLiteralDelimiters: 489 | Description: 'Use `%`-literal delimiters consistently' 490 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' 491 | Enabled: false 492 | 493 | Style/PercentQLiterals: 494 | Description: 'Checks if uses of %Q/%q match the configured preference.' 495 | Enabled: false 496 | 497 | Style/PerlBackrefs: 498 | Description: 'Avoid Perl-style regex back references.' 499 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' 500 | Enabled: false 501 | 502 | Style/PredicateName: 503 | Description: 'Check the names of predicate methods.' 504 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' 505 | Enabled: false 506 | 507 | Style/Proc: 508 | Description: 'Use proc instead of Proc.new.' 509 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' 510 | Enabled: false 511 | 512 | Style/RaiseArgs: 513 | Description: 'Checks the arguments passed to raise/fail.' 514 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' 515 | Enabled: false 516 | 517 | Style/RedundantBegin: 518 | Description: "Don't use begin blocks when they are not needed." 519 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit' 520 | Enabled: false 521 | 522 | Style/RedundantException: 523 | Description: "Checks for an obsolete RuntimeException argument in raise/fail." 524 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror' 525 | Enabled: false 526 | 527 | Style/RedundantReturn: 528 | Description: "Don't use return where it's not required." 529 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return' 530 | Enabled: false 531 | 532 | Style/RedundantSelf: 533 | Description: "Don't use self where it's not needed." 534 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required' 535 | Enabled: false 536 | 537 | Style/RegexpLiteral: 538 | Description: 'Use / or %r around regular expressions.' 539 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' 540 | Enabled: false 541 | 542 | Style/RescueEnsureAlignment: 543 | Description: 'Align rescues and ensures correctly.' 544 | Enabled: false 545 | 546 | Style/RescueModifier: 547 | Description: 'Avoid using rescue in its modifier form.' 548 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers' 549 | Enabled: false 550 | 551 | Style/SelfAssignment: 552 | Description: >- 553 | Checks for places where self-assignment shorthand should have 554 | been used. 555 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' 556 | Enabled: false 557 | 558 | Style/Semicolon: 559 | Description: "Don't use semicolons to terminate expressions." 560 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon' 561 | Enabled: false 562 | 563 | Style/SignalException: 564 | Description: 'Checks for proper usage of fail and raise.' 565 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method' 566 | Enabled: false 567 | 568 | Style/SingleLineBlockParams: 569 | Description: 'Enforces the names of some block params.' 570 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' 571 | Enabled: false 572 | 573 | Style/SingleLineMethods: 574 | Description: 'Avoid single-line methods.' 575 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' 576 | Enabled: false 577 | 578 | Style/SingleSpaceBeforeFirstArg: 579 | Description: >- 580 | Checks that exactly one space is used between a method name 581 | and the first argument for method calls without parentheses. 582 | Enabled: false 583 | 584 | Style/SpaceAfterColon: 585 | Description: 'Use spaces after colons.' 586 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 587 | Enabled: false 588 | 589 | Style/SpaceAfterComma: 590 | Description: 'Use spaces after commas.' 591 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 592 | Enabled: false 593 | 594 | Style/SpaceAfterControlKeyword: 595 | Description: 'Use spaces after if/elsif/unless/while/until/case/when.' 596 | Enabled: false 597 | 598 | Style/SpaceAfterMethodName: 599 | Description: >- 600 | Do not put a space between a method name and the opening 601 | parenthesis in a method definition. 602 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' 603 | Enabled: false 604 | 605 | Style/SpaceAfterNot: 606 | Description: Tracks redundant space after the ! operator. 607 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang' 608 | Enabled: false 609 | 610 | Style/SpaceAfterSemicolon: 611 | Description: 'Use spaces after semicolons.' 612 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 613 | Enabled: false 614 | 615 | Style/SpaceBeforeBlockBraces: 616 | Description: >- 617 | Checks that the left block brace has or doesn't have space 618 | before it. 619 | Enabled: false 620 | 621 | Style/SpaceBeforeComma: 622 | Description: 'No spaces before commas.' 623 | Enabled: false 624 | 625 | Style/SpaceBeforeComment: 626 | Description: >- 627 | Checks for missing space between code and a comment on the 628 | same line. 629 | Enabled: false 630 | 631 | Style/SpaceBeforeSemicolon: 632 | Description: 'No spaces before semicolons.' 633 | Enabled: false 634 | 635 | Style/SpaceInsideBlockBraces: 636 | Description: >- 637 | Checks that block braces have or don't have surrounding space. 638 | For blocks taking parameters, checks that the left brace has 639 | or doesn't have trailing space. 640 | Enabled: false 641 | 642 | Style/SpaceAroundBlockParameters: 643 | Description: 'Checks the spacing inside and after block parameters pipes.' 644 | Enabled: false 645 | 646 | Style/SpaceAroundEqualsInParameterDefault: 647 | Description: >- 648 | Checks that the equals signs in parameter default assignments 649 | have or don't have surrounding space depending on 650 | configuration. 651 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals' 652 | Enabled: false 653 | 654 | Style/SpaceAroundOperators: 655 | Description: 'Use a single space around operators.' 656 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 657 | Enabled: false 658 | 659 | Style/SpaceBeforeModifierKeyword: 660 | Description: 'Put a space before the modifier keyword.' 661 | Enabled: false 662 | 663 | Style/SpaceInsideBrackets: 664 | Description: 'No spaces after [ or before ].' 665 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' 666 | Enabled: false 667 | 668 | Style/SpaceInsideHashLiteralBraces: 669 | Description: "Use spaces inside hash literal braces - or don't." 670 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 671 | Enabled: false 672 | 673 | Style/SpaceInsideParens: 674 | Description: 'No spaces after ( or before ).' 675 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' 676 | Enabled: false 677 | 678 | Style/SpaceInsideRangeLiteral: 679 | Description: 'No spaces inside range literals.' 680 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals' 681 | Enabled: false 682 | 683 | Style/SpaceInsideStringInterpolation: 684 | Description: 'Checks for padding/surrounding spaces inside string interpolation.' 685 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#string-interpolation' 686 | Enabled: false 687 | 688 | Style/SpecialGlobalVars: 689 | Description: 'Avoid Perl-style global variables.' 690 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' 691 | Enabled: false 692 | 693 | Style/StringLiterals: 694 | Description: 'Checks if uses of quotes match the configured preference.' 695 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' 696 | Enabled: false 697 | 698 | Style/StringLiteralsInInterpolation: 699 | Description: >- 700 | Checks if uses of quotes inside expressions in interpolated 701 | strings match the configured preference. 702 | Enabled: false 703 | 704 | Style/StructInheritance: 705 | Description: 'Checks for inheritance from Struct.new.' 706 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new' 707 | Enabled: false 708 | 709 | Style/SymbolLiteral: 710 | Description: 'Use plain symbols instead of string symbols when possible.' 711 | Enabled: false 712 | 713 | Style/SymbolProc: 714 | Description: 'Use symbols as procs instead of blocks when possible.' 715 | Enabled: false 716 | 717 | Style/Tab: 718 | Description: 'No hard tabs.' 719 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' 720 | Enabled: false 721 | 722 | Style/TrailingBlankLines: 723 | Description: 'Checks trailing blank lines and final newline.' 724 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof' 725 | Enabled: false 726 | 727 | Style/TrailingComma: 728 | Description: 'Checks for trailing comma in parameter lists and literals.' 729 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' 730 | Enabled: false 731 | 732 | Style/TrailingWhitespace: 733 | Description: 'Avoid trailing whitespace.' 734 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' 735 | Enabled: false 736 | 737 | Style/TrivialAccessors: 738 | Description: 'Prefer attr_* methods to trivial readers/writers.' 739 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' 740 | Enabled: false 741 | 742 | Style/UnlessElse: 743 | Description: >- 744 | Do not use unless with else. Rewrite these with the positive 745 | case first. 746 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless' 747 | Enabled: false 748 | 749 | Style/UnneededCapitalW: 750 | Description: 'Checks for %W when interpolation is not needed.' 751 | Enabled: false 752 | 753 | Style/UnneededPercentQ: 754 | Description: 'Checks for %q/%Q when single quotes or double quotes would do.' 755 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' 756 | Enabled: false 757 | 758 | Style/TrailingUnderscoreVariable: 759 | Description: >- 760 | Checks for the usage of unneeded trailing underscores at the 761 | end of parallel variable assignment. 762 | Enabled: false 763 | 764 | Style/VariableInterpolation: 765 | Description: >- 766 | Don't interpolate global, instance and class variables 767 | directly in strings. 768 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' 769 | Enabled: false 770 | 771 | Style/VariableName: 772 | Description: 'Use the configured style when naming variables.' 773 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' 774 | Enabled: false 775 | 776 | Style/WhenThen: 777 | Description: 'Use when x then ... for one-line cases.' 778 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' 779 | Enabled: false 780 | 781 | Style/WhileUntilDo: 782 | Description: 'Checks for redundant do after while or until.' 783 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do' 784 | Enabled: false 785 | 786 | Style/WhileUntilModifier: 787 | Description: >- 788 | Favor modifier while/until usage when you have a 789 | single-line body. 790 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' 791 | Enabled: false 792 | 793 | Style/WordArray: 794 | Description: 'Use %w or %W for arrays of words.' 795 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' 796 | Enabled: false 797 | 798 | ########################################################## 799 | Metrics/AbcSize: 800 | Description: >- 801 | A calculated magnitude based on number of assignments, 802 | branches, and conditions. 803 | Reference: 'http://c2.com/cgi/wiki?AbcMetric' 804 | Enabled: true 805 | Max: 20 806 | 807 | Metrics/BlockNesting: 808 | Description: 'Avoid excessive block nesting' 809 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' 810 | Enabled: true 811 | Max: 4 812 | 813 | Metrics/ClassLength: 814 | Description: 'Avoid classes longer than 100 lines of code.' 815 | Enabled: true 816 | Max: 150 817 | 818 | Metrics/ModuleLength: 819 | Description: 'Avoid modules longer than 100 lines of code.' 820 | Enabled: true 821 | Max: 150 822 | 823 | Metrics/CyclomaticComplexity: 824 | Description: >- 825 | A complexity metric that is strongly correlated to the number 826 | of test cases needed to validate a method. 827 | Enabled: false 828 | 829 | Metrics/LineLength: 830 | Description: 'Limit lines to 80 characters.' 831 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' 832 | Enabled: false 833 | 834 | Metrics/MethodLength: 835 | Description: 'Avoid methods longer than 10 lines of code.' 836 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' 837 | Enabled: false 838 | 839 | Metrics/ParameterLists: 840 | Description: 'Avoid parameter lists longer than three or four parameters.' 841 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' 842 | Enabled: true 843 | 844 | Metrics/PerceivedComplexity: 845 | Description: >- 846 | A complexity metric geared towards measuring complexity for a 847 | human reader. 848 | Enabled: false 849 | 850 | #################### Lint ################################ 851 | ### Warnings 852 | 853 | Lint/AmbiguousOperator: 854 | Description: >- 855 | Checks for ambiguous operators in the first argument of a 856 | method invocation without parentheses. 857 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' 858 | Enabled: true 859 | 860 | Lint/AmbiguousRegexpLiteral: 861 | Description: >- 862 | Checks for ambiguous regexp literals in the first argument of 863 | a method invocation without parenthesis. 864 | Enabled: true 865 | 866 | Lint/AssignmentInCondition: 867 | Description: "Don't use assignment in conditions." 868 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' 869 | Enabled: true 870 | 871 | Lint/BlockAlignment: 872 | Description: 'Align block ends correctly.' 873 | Enabled: true 874 | 875 | Lint/CircularArgumentReference: 876 | Description: "Don't refer to the keyword argument in the default value." 877 | Enabled: true 878 | 879 | Lint/ConditionPosition: 880 | Description: >- 881 | Checks for condition placed in a confusing position relative to 882 | the keyword. 883 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' 884 | Enabled: true 885 | 886 | Lint/Debugger: 887 | Description: 'Check for debugger calls.' 888 | Enabled: true 889 | 890 | Lint/DefEndAlignment: 891 | Description: 'Align ends corresponding to defs correctly.' 892 | Enabled: true 893 | 894 | Lint/DeprecatedClassMethods: 895 | Description: 'Check for deprecated class method calls.' 896 | Enabled: true 897 | 898 | Lint/DuplicateMethods: 899 | Description: 'Check for duplicate methods calls.' 900 | Enabled: true 901 | 902 | Lint/EachWithObjectArgument: 903 | Description: 'Check for immutable argument given to each_with_object.' 904 | Enabled: true 905 | 906 | Lint/ElseLayout: 907 | Description: 'Check for odd code arrangement in an else block.' 908 | Enabled: true 909 | 910 | Lint/EmptyEnsure: 911 | Description: 'Checks for empty ensure block.' 912 | Enabled: true 913 | 914 | Lint/EmptyInterpolation: 915 | Description: 'Checks for empty string interpolation.' 916 | Enabled: true 917 | 918 | Lint/EndAlignment: 919 | Description: 'Align ends correctly.' 920 | Enabled: true 921 | 922 | Lint/EndInMethod: 923 | Description: 'END blocks should not be placed inside method definitions.' 924 | Enabled: true 925 | 926 | Lint/EnsureReturn: 927 | Description: 'Do not use return in an ensure block.' 928 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure' 929 | Enabled: true 930 | 931 | Lint/Eval: 932 | Description: 'The use of eval represents a serious security risk.' 933 | Enabled: true 934 | 935 | Lint/FormatParameterMismatch: 936 | Description: 'The number of parameters to format/sprint must match the fields.' 937 | Enabled: true 938 | 939 | Lint/HandleExceptions: 940 | Description: "Don't suppress exception." 941 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' 942 | Enabled: true 943 | 944 | Lint/InvalidCharacterLiteral: 945 | Description: >- 946 | Checks for invalid character literals with a non-escaped 947 | whitespace character. 948 | Enabled: true 949 | 950 | Lint/LiteralInCondition: 951 | Description: 'Checks of literals used in conditions.' 952 | Enabled: true 953 | 954 | Lint/LiteralInInterpolation: 955 | Description: 'Checks for literals used in interpolation.' 956 | Enabled: true 957 | 958 | Lint/Loop: 959 | Description: >- 960 | Use Kernel#loop with break rather than begin/end/until or 961 | begin/end/while for post-loop tests. 962 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' 963 | Enabled: true 964 | 965 | Lint/NestedMethodDefinition: 966 | Description: 'Do not use nested method definitions.' 967 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-methods' 968 | Enabled: true 969 | 970 | Lint/NonLocalExitFromIterator: 971 | Description: 'Do not use return in iterator to cause non-local exit.' 972 | Enabled: true 973 | 974 | Lint/ParenthesesAsGroupedExpression: 975 | Description: >- 976 | Checks for method calls with a space before the opening 977 | parenthesis. 978 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' 979 | Enabled: true 980 | 981 | Lint/RequireParentheses: 982 | Description: >- 983 | Use parentheses in the method call to avoid confusion 984 | about precedence. 985 | Enabled: true 986 | 987 | Lint/RescueException: 988 | Description: 'Avoid rescuing the Exception class.' 989 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' 990 | Enabled: true 991 | 992 | Lint/ShadowingOuterLocalVariable: 993 | Description: >- 994 | Do not use the same name as outer local variable 995 | for block arguments or block local variables. 996 | Enabled: true 997 | 998 | Lint/SpaceBeforeFirstArg: 999 | Description: >- 1000 | Put a space between a method name and the first argument 1001 | in a method call without parentheses. 1002 | Enabled: true 1003 | 1004 | Lint/StringConversionInInterpolation: 1005 | Description: 'Checks for Object#to_s usage in string interpolation.' 1006 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s' 1007 | Enabled: true 1008 | 1009 | Lint/UnderscorePrefixedVariableName: 1010 | Description: 'Do not use prefix `_` for a variable that is used.' 1011 | Enabled: true 1012 | 1013 | Lint/UnneededDisable: 1014 | Description: >- 1015 | Checks for rubocop:disable comments that can be removed. 1016 | Note: this cop is not disabled when disabling all cops. 1017 | It must be explicitly disabled. 1018 | Enabled: true 1019 | 1020 | Lint/UnusedBlockArgument: 1021 | Description: 'Checks for unused block arguments.' 1022 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 1023 | Enabled: true 1024 | 1025 | Lint/UnusedMethodArgument: 1026 | Description: 'Checks for unused method arguments.' 1027 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 1028 | Enabled: true 1029 | 1030 | Lint/UnreachableCode: 1031 | Description: 'Unreachable code.' 1032 | Enabled: true 1033 | 1034 | Lint/UselessAccessModifier: 1035 | Description: 'Checks for useless access modifiers.' 1036 | Enabled: true 1037 | 1038 | Lint/UselessAssignment: 1039 | Description: 'Checks for useless assignment to a local variable.' 1040 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 1041 | Enabled: true 1042 | 1043 | Lint/UselessComparison: 1044 | Description: 'Checks for comparison of something with itself.' 1045 | Enabled: true 1046 | 1047 | Lint/UselessElseWithoutRescue: 1048 | Description: 'Checks for useless `else` in `begin..end` without `rescue`.' 1049 | Enabled: true 1050 | 1051 | Lint/UselessSetterCall: 1052 | Description: 'Checks for useless setter call to a local variable.' 1053 | Enabled: true 1054 | 1055 | Lint/Void: 1056 | Description: 'Possible use of operator/literal/variable in void context.' 1057 | Enabled: true 1058 | 1059 | ##################### Performance ############################# 1060 | 1061 | Performance/Count: 1062 | Description: >- 1063 | Use `count` instead of `select...size`, `reject...size`, 1064 | `select...count`, `reject...count`, `select...length`, 1065 | and `reject...length`. 1066 | Enabled: true 1067 | 1068 | Performance/Detect: 1069 | Description: >- 1070 | Use `detect` instead of `select.first`, `find_all.first`, 1071 | `select.last`, and `find_all.last`. 1072 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' 1073 | Enabled: true 1074 | 1075 | Performance/FlatMap: 1076 | Description: >- 1077 | Use `Enumerable#flat_map` 1078 | instead of `Enumerable#map...Array#flatten(1)` 1079 | or `Enumberable#collect..Array#flatten(1)` 1080 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' 1081 | Enabled: true 1082 | EnabledForFlattenWithoutParams: false 1083 | # If enabled, this cop will warn about usages of 1084 | # `flatten` being called without any parameters. 1085 | # This can be dangerous since `flat_map` will only flatten 1 level, and 1086 | # `flatten` without any parameters can flatten multiple levels. 1087 | 1088 | Performance/ReverseEach: 1089 | Description: 'Use `reverse_each` instead of `reverse.each`.' 1090 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code' 1091 | Enabled: true 1092 | 1093 | Performance/Sample: 1094 | Description: >- 1095 | Use `sample` instead of `shuffle.first`, 1096 | `shuffle.last`, and `shuffle[Fixnum]`. 1097 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code' 1098 | Enabled: true 1099 | 1100 | Performance/Size: 1101 | Description: >- 1102 | Use `size` instead of `count` for counting 1103 | the number of elements in `Array` and `Hash`. 1104 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code' 1105 | Enabled: true 1106 | 1107 | Performance/StringReplacement: 1108 | Description: >- 1109 | Use `tr` instead of `gsub` when you are replacing the same 1110 | number of characters. Use `delete` instead of `gsub` when 1111 | you are deleting characters. 1112 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code' 1113 | Enabled: true 1114 | --------------------------------------------------------------------------------