trashed 2.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +34 -0
- data/Rakefile +22 -0
- data/init.rb +1 -0
- data/lib/trashed.rb +5 -0
- data/lib/trashed/meter.rb +40 -0
- data/lib/trashed/new_relic.rb +47 -0
- data/lib/trashed/rack.rb +66 -0
- data/lib/trashed/railtie.rb +61 -0
- data/lib/trashed/resource_usage.rb +48 -0
- data/test/lib/trashed/test_helper.rb +3 -0
- data/test/meter_test.rb +15 -0
- data/test/rack_test.rb +18 -0
- data/trashed.gemspec +12 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: abfbb3ba8576bac0d578d9821479635dda52e57b
|
4
|
+
data.tar.gz: b5439fd5e664b3f439860b6fd6dd548248d1594c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7748860b9937a18abcffb575cb51a2cfb0ed352b9d1ffb824dceafa67a47c22afbfd4bfd1d4e89974f20e5137996cd4ae65cc7285bc3e9c7eef117fb92a8cbf9
|
7
|
+
data.tar.gz: 6af120ce83a1048254fc0df2d41f1cef0c32bfd99c9ea81eb5bd1ef063562c447f32bf28e137e23d92f8e712f11536bc4f85b5d49c7d7f9451c86edd2e306349
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 37signals, LLC
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
## Trashed
|
2
|
+
# Keep an eye on resource usage.
|
3
|
+
|
4
|
+
|
5
|
+
- Logs per-request object counts, heap growth, and GC time.
|
6
|
+
- Sends periodic resource usage snapshots to StatsD & NewRelic.
|
7
|
+
- Requires Ruby 1.9 or REE.
|
8
|
+
|
9
|
+
|
10
|
+
## Setup
|
11
|
+
|
12
|
+
### Rails 3
|
13
|
+
|
14
|
+
On Rails 3, add this to the top of `config/application.rb`:
|
15
|
+
|
16
|
+
require 'trashed/railtie'
|
17
|
+
|
18
|
+
And in the body of your app config:
|
19
|
+
|
20
|
+
module YourApp
|
21
|
+
class Application < Rails::Application
|
22
|
+
config.trashed[:statsd] = YourApp.statsd
|
23
|
+
|
24
|
+
|
25
|
+
### Rails 2
|
26
|
+
|
27
|
+
On Rails 2, add the middleware to `config/environment.rb`:
|
28
|
+
|
29
|
+
Rails::Initializer.run do |config|
|
30
|
+
config.middleware.use Trashed::Rack::MeasureResourceUsage, :statsd => YourApp.statsd
|
31
|
+
|
32
|
+
And set up the sampler in `config/initializers/trashed.rb`:
|
33
|
+
|
34
|
+
Trashed::Newrelic.sample Trashed::ResourceUsage, :statsd => YourApp.statsd
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'rdoc/task'
|
3
|
+
|
4
|
+
desc 'Default: run unit tests'
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
desc 'Run unit tests'
|
8
|
+
Rake::TestTask.new :test do |t|
|
9
|
+
t.libs << 'test/lib'
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Generate RDoc documentation'
|
15
|
+
Rake::RDocTask.new :rdoc do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'Trashed'
|
18
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
19
|
+
rdoc.rdoc_files.include('MIT-LICENSE')
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'trashed'
|
data/lib/trashed.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Trashed
|
2
|
+
class Meter
|
3
|
+
attr_reader :counters, :gauges
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
@counters, @gauges = {}, {}
|
7
|
+
instance_eval(&block) if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def counts(name, &block) @counters[name] = block end
|
11
|
+
def count; read @counters end
|
12
|
+
|
13
|
+
def gauges(name, &block) @gauges[name] = block end
|
14
|
+
def gauge; read @gauges end
|
15
|
+
|
16
|
+
def instrument
|
17
|
+
before = count
|
18
|
+
yield
|
19
|
+
delta before, count
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def delta(before, after)
|
25
|
+
delta = {}
|
26
|
+
after.each { |k, v| delta[k] = v - before[k] }
|
27
|
+
delta
|
28
|
+
end
|
29
|
+
|
30
|
+
def read(measures)
|
31
|
+
data = {}
|
32
|
+
measures.each do |name, measure|
|
33
|
+
measure.call.each do |key, value|
|
34
|
+
data["#{name}.#{key}"] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
data
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Trashed
|
2
|
+
module NewRelic
|
3
|
+
def self.sample(meter, options = {})
|
4
|
+
::NewRelic::Agent.instance.stats_engine.add_sampler Sampler.new(meter, options)
|
5
|
+
end
|
6
|
+
|
7
|
+
class Sampler
|
8
|
+
attr_accessor :stats_engine
|
9
|
+
|
10
|
+
def initialize(meter, options = {})
|
11
|
+
@meter = meter
|
12
|
+
@label = options[:label] || 'Custom/%s'
|
13
|
+
@statsd = options[:statsd]
|
14
|
+
end
|
15
|
+
|
16
|
+
def poll
|
17
|
+
record @meter.count
|
18
|
+
record @meter.gauge
|
19
|
+
end
|
20
|
+
|
21
|
+
def record(data)
|
22
|
+
data.each do |name, value|
|
23
|
+
record_newrelic name, value
|
24
|
+
record_statsd name, value if @statsd
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def record_statsd(name, value)
|
31
|
+
@statsd.timing name, value
|
32
|
+
end
|
33
|
+
|
34
|
+
def record_newrelic(name, value)
|
35
|
+
stats_for(label_for(name)).record_data_point(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def stats_for(metric)
|
39
|
+
stats_engine.get_stats(metric, false)
|
40
|
+
end
|
41
|
+
|
42
|
+
def label_for(name)
|
43
|
+
@label % name.gsub('.', '/')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/trashed/rack.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Trashed
|
2
|
+
module Rack
|
3
|
+
class MeasureResourceUsage
|
4
|
+
def initialize(app, options = {})
|
5
|
+
@app = app
|
6
|
+
@debug, @logger, @statsd, @sample_rate = options.values_at(:debug, :logger, :statsd, :sample_rate)
|
7
|
+
@sample_rate ||= 0.1
|
8
|
+
|
9
|
+
@request_namespaces, @sampler_namespaces = options.values_at(:statsd_request_namespaces, :statsd_sampler_namespaces)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
response = nil
|
14
|
+
instrument(env) { response = @app.call(env) }
|
15
|
+
response
|
16
|
+
end
|
17
|
+
|
18
|
+
def instrument(env, &block)
|
19
|
+
change = ResourceUsage.instrument(&block)
|
20
|
+
usage = ResourceUsage.gauge
|
21
|
+
record env, change, usage
|
22
|
+
end
|
23
|
+
|
24
|
+
def record(env, change, usage)
|
25
|
+
record_env env, change, usage
|
26
|
+
record_logger env, change, usage if @logger
|
27
|
+
record_statsd env, change, usage if @statsd
|
28
|
+
end
|
29
|
+
|
30
|
+
def record_env(env, change, usage)
|
31
|
+
env['trashed.change'] = change
|
32
|
+
env['trashed.usage'] = usage
|
33
|
+
end
|
34
|
+
|
35
|
+
def record_logger(env, change, usage)
|
36
|
+
@logger.info "Rack handled in %dms (GC runs: %d)" % change.values_at('Time.wall', 'GC.count')
|
37
|
+
record_debug_logger env, change, usage if @debug
|
38
|
+
end
|
39
|
+
|
40
|
+
def record_debug_logger(env, change, usage)
|
41
|
+
@logger.debug "Changes: #{change.to_yaml}"
|
42
|
+
@logger.debug "Usage: #{usage.to_yaml}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def record_statsd(env, change, usage)
|
46
|
+
record_statsd_timing change, :'Rack.Request.All'
|
47
|
+
record_statsd_timing usage, :'Rack.Server.All'
|
48
|
+
|
49
|
+
Array(@request_namespaces.call(env)).each do |namespace|
|
50
|
+
record_statsd_timing change, "Rack.Request.#{namespace}"
|
51
|
+
end if @request_namespaces
|
52
|
+
|
53
|
+
Array(@sampler_namespaces.call(env)).each do |namespace|
|
54
|
+
record_statsd_timing usage, "Rack.Server.#{namespace}"
|
55
|
+
end if @sampler_namespaces
|
56
|
+
end
|
57
|
+
|
58
|
+
def record_statsd_timing(data, namespace = nil)
|
59
|
+
data.each do |name, value|
|
60
|
+
name = "#{namespace}.#{name}" if namespace
|
61
|
+
@statsd.timing name, value, @sample_rate
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'trashed'
|
2
|
+
require 'rails/railtie'
|
3
|
+
require 'active_support/ordered_options'
|
4
|
+
|
5
|
+
module Trashed
|
6
|
+
class Railtie < ::Rails::Railtie
|
7
|
+
config.trashed = ActiveSupport::OrderedOptions.new
|
8
|
+
config.trashed.statsd = ActiveSupport::OrderedOptions.new
|
9
|
+
|
10
|
+
initializer 'trashed' do |app|
|
11
|
+
# Debug data sent to statsd. Class-level config only :/
|
12
|
+
Statsd.logger = app.config.trashed.logger if app.config.trashed.debug
|
13
|
+
|
14
|
+
app.config.trashed.sample_rate ||= 0.1
|
15
|
+
app.config.trashed.logger = Rails.logger
|
16
|
+
app.config.trashed.statsd = connect_to_statsd(app.config.trashed.statsd)
|
17
|
+
|
18
|
+
app.config.trashed.statsd_request_namespaces = lambda do |env|
|
19
|
+
# Rails 3.2. Record request controller, action, and format.
|
20
|
+
if controller = env['action_controller.instance']
|
21
|
+
name = controller.controller_name
|
22
|
+
action = controller.action_name
|
23
|
+
format = controller.request.format.try(:to_sym)
|
24
|
+
[ "Controllers.#{name}",
|
25
|
+
"Formats.#{format}",
|
26
|
+
"Actions.#{name}.#{action}.#{format}" ]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
hostname = `hostname -s`.chomp
|
31
|
+
app.config.trashed.statsd_sampler_namespaces = lambda do |env|
|
32
|
+
# Rails 3.2. Record hostname.
|
33
|
+
[ "Hosts.#{hostname}" ]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
initializer 'trashed.middleware', :after => 'trashed', :before => 'trashed.newrelic' do |app|
|
38
|
+
app.middleware.insert_after 'Rack::Runtime', Trashed::Rack::MeasureResourceUsage, app.config.trashed
|
39
|
+
end
|
40
|
+
|
41
|
+
initializer 'trashed.newrelic', :after => 'newrelic_rpm.start_plugin' do |app|
|
42
|
+
if defined?(NewRelic::Control) && NewRelic::Control.instance.agent_enabled?
|
43
|
+
require 'trashed/new_relic'
|
44
|
+
Trashed::NewRelic.sample ResourceUsage, app.config.trashed
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def connect_to_statsd(options)
|
49
|
+
require 'statsd'
|
50
|
+
|
51
|
+
case options
|
52
|
+
when Statsd
|
53
|
+
options
|
54
|
+
when Hash
|
55
|
+
Statsd.new(options[:host], options[:port]).tap do |statsd|
|
56
|
+
statsd.namespace = options[:namespace]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Trashed
|
2
|
+
ResourceUsage = Meter.new do
|
3
|
+
|
4
|
+
# Wall clock time, in milliseconds since epoch
|
5
|
+
counts :Time do
|
6
|
+
{ :wall => (Time.now.to_f * 1000).to_i }
|
7
|
+
end
|
8
|
+
|
9
|
+
# RailsBench GC patch / REE 1.8
|
10
|
+
if GC.respond_to? :enable_stats
|
11
|
+
GC.enable_stats
|
12
|
+
|
13
|
+
counts :Objects do
|
14
|
+
{ :total => ObjectSpace.allocated_objects }
|
15
|
+
end
|
16
|
+
|
17
|
+
gauges :Objects do
|
18
|
+
{ :live => ObjectSpace.live_objects }
|
19
|
+
end
|
20
|
+
|
21
|
+
counts :GC do
|
22
|
+
{ :count => GC.collections, :elapsed => GC.time, :memory => GC.allocated_size }
|
23
|
+
end
|
24
|
+
|
25
|
+
gauges :GC do
|
26
|
+
{ :growth => GC.growth }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Ruby 1.9+
|
30
|
+
elsif ObjectSpace.respond_to? :count_objects
|
31
|
+
counts :Objects do
|
32
|
+
{ :total => ObjectSpace.count_objects[:TOTAL] }
|
33
|
+
end
|
34
|
+
|
35
|
+
gauges :Objects do
|
36
|
+
ObjectSpace.count_objects
|
37
|
+
end
|
38
|
+
|
39
|
+
counts :GC do
|
40
|
+
{ :count => GC.stat[:count] }
|
41
|
+
end
|
42
|
+
|
43
|
+
gauges :GC do
|
44
|
+
GC.stat
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/test/meter_test.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'trashed/test_helper'
|
2
|
+
|
3
|
+
class MeterTest < MiniTest::Unit::TestCase
|
4
|
+
def test_count
|
5
|
+
time = Trashed::ResourceUsage.count['Time.wall']
|
6
|
+
refute_nil time
|
7
|
+
assert_in_delta time, (Time.now.to_f * 1000), 1000
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_instrument
|
11
|
+
elapsed = Trashed::ResourceUsage.instrument { nil }['Time.wall']
|
12
|
+
refute_nil elapsed
|
13
|
+
assert_in_delta elapsed, 0, 1000
|
14
|
+
end
|
15
|
+
end
|
data/test/rack_test.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'trashed/test_helper'
|
2
|
+
|
3
|
+
class RackTest < MiniTest::Unit::TestCase
|
4
|
+
Hello = lambda { |env| [200, {}, %w(hello)] }
|
5
|
+
|
6
|
+
def test_instruments_app_and_stores_in_env
|
7
|
+
env = {}
|
8
|
+
|
9
|
+
response = Trashed::Rack::MeasureResourceUsage.new(Hello).call(env)
|
10
|
+
|
11
|
+
refute_nil env['trashed.change']
|
12
|
+
refute_nil env['trashed.usage']
|
13
|
+
|
14
|
+
elapsed = env['trashed.change']['Time.wall']
|
15
|
+
refute_nil elapsed
|
16
|
+
assert_in_delta elapsed, 0, 1000
|
17
|
+
end
|
18
|
+
end
|
data/trashed.gemspec
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'trashed'
|
3
|
+
s.version = '2.0.5'
|
4
|
+
s.author = 'Jeremy Kemper'
|
5
|
+
s.email = 'jeremy@bitsweat.net'
|
6
|
+
s.homepage = 'https://github.com/37signals/trashed'
|
7
|
+
s.summary = 'Keep tabs on Ruby garbage collection: object counts, allocated bytes, GC time.'
|
8
|
+
|
9
|
+
s.add_dependency 'statsd-ruby', '>= 0.4'
|
10
|
+
|
11
|
+
s.files = Dir["#{File.dirname(__FILE__)}/**/*"]
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trashed
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Kemper
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: statsd-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.4'
|
27
|
+
description:
|
28
|
+
email: jeremy@bitsweat.net
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- ./MIT-LICENSE
|
34
|
+
- ./README.md
|
35
|
+
- ./Rakefile
|
36
|
+
- ./init.rb
|
37
|
+
- ./lib/trashed.rb
|
38
|
+
- ./lib/trashed/meter.rb
|
39
|
+
- ./lib/trashed/new_relic.rb
|
40
|
+
- ./lib/trashed/rack.rb
|
41
|
+
- ./lib/trashed/railtie.rb
|
42
|
+
- ./lib/trashed/resource_usage.rb
|
43
|
+
- ./test/lib/trashed/test_helper.rb
|
44
|
+
- ./test/meter_test.rb
|
45
|
+
- ./test/rack_test.rb
|
46
|
+
- ./trashed.gemspec
|
47
|
+
homepage: https://github.com/37signals/trashed
|
48
|
+
licenses: []
|
49
|
+
metadata: {}
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 2.2.2
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: ! 'Keep tabs on Ruby garbage collection: object counts, allocated bytes,
|
70
|
+
GC time.'
|
71
|
+
test_files: []
|