Skip to content
This repository has been archived by the owner on Apr 1, 2020. It is now read-only.

Commit

Permalink
First pass at new instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Bill Kayser committed Jan 28, 2010
1 parent 725bea7 commit 873b2a3
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 20 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG
@@ -1,2 +1,7 @@
* Version 1.0.0
- Initial Release

Initial Release:
- Camping
- Authlogic
- DelayedJob
- Paperclip
34 changes: 34 additions & 0 deletions README.md
@@ -0,0 +1,34 @@
# The RPM Contrib Gem

The `rpm_contrib` gem contains instrumentation for the New Relic RPM agent
contributed by the community of RPM users. It requires the RPM Agent
to run.

We encourage contributions to this project and will provide whatever
assistance we can to those wishing to develop instrumentation for
other open source Ruby libraries.

## Note on Patches/Pull Requests

* Fork the http://www.github.com/newrelic/rpm_contrib project.
* Add instrumentation files to `lib/rpm_contrib/instrumentation`. These
files will be loaded when the RPM agent is initialized.
* Add samplers to `lib/rpm_contrib/samplers`. These classes are
installed automatically when the RPM agent is initialized.
* Add tests.
* Commit, do not mess with the Rakefile, version, or history. (if you
want to have your own version, that is fine but bump version in a
commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.

## Further Information

See http://newrlic.github.com/rpm for API documentation on the Agent.

See http://support.newrelic.com/faqs for additional tips and documentation.

Contact support@newrelic.com for help.

== Copyright

Copyright (c) 2010 New Relic. See LICENSE for details.
17 changes: 0 additions & 17 deletions README.rdoc

This file was deleted.

15 changes: 14 additions & 1 deletion lib/rpm_contrib.rb
@@ -1,4 +1,17 @@
# Hook instrumentation and samplers in this gem into the normal RPM
# start up sequence.
require 'newrelic_rpm'
module RPMContrib
VERSION = File.read(File.dirname(__FILE__)+"/../CHANGELOG")[/Version ([\d\.]+)$/, 1]

lib_root = File.dirname(__FILE__)

# Tell the agent to load all the files in the rpm_contrib/instrumentation directory.

NewRelic::Agent.add_instrumentation(lib_root+"/rpm_contrib/instrumentation/**/*.rb")

# Load all the Sampler class definitions. These will register
# automatically with the agent.
Dir.glob(lib_root + "/rpm_contrib/samplers/**/*.rb") { |file| require file }

VERSION = File.read(lib_root+"/../CHANGELOG")[/Version ([\d\.]+)$/, 1]
end
74 changes: 74 additions & 0 deletions lib/rpm_contrib/instrumentation/camping.rb
@@ -0,0 +1,74 @@
require 'new_relic/agent/instrumentation/controller_instrumentation'

module RPMContrib
module Instrumentation
# == Instrumentation for Camping
# To instrument all controllers do the following:
# 1. Add the necessary NewRelic-specific requires in your require section
# 2. Add an include at the end of your main Camping app module
# 3. Add a call to NewRelic::Agent.manual_start at the end of the file to start the agent
# 4. Run the following command to get the NewRelic license key to use: heroku config -all
# 5. Create a newrelic.yml under the /config folder with the following content:
#
# common: &default_settings
# license_key: 'PASTE THE VALUE OF NEW_RELIC_LICENSE_KEY HERE'
# agent_enabled: true
# app_name: PASTE THE NAME OF YOUR CAMPING APP HERE
# enabled: true
#
# production:
# <<: *default_settings
# enabled: true
#
# Camping code example:
# --------------------------------------------------------------------------------------
# require "newrelic_rpm"
# require 'new_relic/agent/agent'
# require 'new_relic/agent/instrumentation/controller_instrumentation'
# require 'new_relic/agent/instrumentation/camping'
#
# Camping.goes :NewRelicCampingTest
#
# module NewRelicCampingTest
# # your code
#
# include NewRelic::Agent::Instrumentation::ControllerInstrumentation
# include NewRelic::Agent::Instrumentation::Camping
# end
#
# NewRelic::Agent.manual_start
#

module Camping

def self.included(mod)

# Since the Camping::Base module is essentially copied
# into the main module (the mod passed in) of a Camping app
# using the Camping.goes :NewRelicCampingTest syntax
# we need to evaluate "weld" the NewRelic plugin in the context of the new Base

(Kernel.const_get(mod.name)::Base).module_eval do

# Add the new method to the Camping app's Base module
# since the Camping::Base module is being included
# in every Camping controller

def service_with_newrelic(*args)
perform_action_with_newrelic_trace(:category => :rack) do
service_without_newrelic(*args)
end
end

# Alias the "standard" service method
# so we can provide a level of indirection
# to perform the tracing for NewRelic

alias service_without_newrelic service
alias service service_with_newrelic
end
end

end #RPMContrib::Instrumentation::Camping
end
end
@@ -1,4 +1,4 @@
module NewRelic::Agent::Instrumentation::DelayedJobInstrumentation
module RPMContrib::Instrumentation::DelayedJobInstrumentation
extend self
Delayed::Job.class_eval do
include NewRelic::Agent::Instrumentation::ControllerInstrumentation
Expand Down
22 changes: 22 additions & 0 deletions lib/rpm_contrib/instrumentation/paperclip.rb
@@ -0,0 +1,22 @@
# Paperclip Instrumentation.

if defined? ::Paperclip

::Paperclip::Attachment.class_eval do
add_method_tracer :save, 'Paperclip/#{name}/save'
add_method_tracer :assign, 'Paperclip/#{name}/assign'
add_method_tracer :post_process, 'Paperclip/#{name}/post_process'
end

::Paperclip::Storage::Filesystem.class_eval do
add_method_tracer :flush_deletes, 'Paperclip/Storage/flush_deletes'
add_method_tracer :flush_writes, 'Paperclip/Storage/flush_writes'
end

::Paperclip::Storage::S3.class_eval do
add_method_tracer :flush_deletes, 'Paperclip/Storage/flush_deletes'
add_method_tracer :flush_writes, 'Paperclip/Storage/flush_writes'
end

end

108 changes: 108 additions & 0 deletions test/delayed_job_instrumentation/delayed_job_test.rb
@@ -0,0 +1,108 @@
module DelayedJobInstrumentation
require File.expand_path(File.join(File.dirname(__FILE__),'/../test_helper'))

class LongRunningJob
def perform
sleep 5
end
end

class NamedJob
def display_name
'some custom name'
end
def perform
true
end
end

class DelayedJobTest < Test::Unit::TestCase
def local_env
NewRelic::Control.instance.local_env
end

def worker_name
local_env.dispatcher_instance_id
end

def lock_n_jobs(n=1)
n.times do
job = Delayed::Job.create
job.update_attributes({
:locked_at => Time.now,
:locked_by => worker_name
})
end
end

def setup
NewRelic::Agent.manual_start
@agent = NewRelic::Agent.instance

@agent.transaction_sampler.harvest
@agent.stats_engine.clear_stats
end

def teardown
@agent.instance_variable_set("@histogram", NewRelic::Histogram.new)
end

def test_job_instrumentation
job = Delayed::Job.new(:payload_object => LongRunningJob.new)
job_name = "Controller/Task/Delayed::Job/LongRunningJob"

job.invoke_job
job_stats = @agent.stats_engine.get_stats(job_name)

assert @agent.stats_engine.metrics.include?(job_name)
assert_equal 1, job_stats.call_count
end

def test_custom_name
job = Delayed::Job.new(:payload_object => NamedJob.new)
job_name = "Controller/Task/Delayed::Job/some custom name"

job.invoke_job
job_stats = @agent.stats_engine.get_stats(job_name)

assert @agent.stats_engine.metrics.include?(job_name)
assert_equal 1, job_stats.call_count
end

def test_lock_sampler
stats_engine = NewRelic::Agent::StatsEngine.new
sampler = NewRelic::Agent::Samplers::DelayedJobLockSampler.new
sampler.stats_engine = stats_engine

lock_n_jobs(1)
sampler.poll

assert_equal 1, sampler.stats.data_point_count
assert_equal 1, sampler.stats.min_call_time
assert_equal 1, sampler.stats.max_call_time

lock_n_jobs(4)
sampler.poll

assert_equal 2, sampler.stats.data_point_count
assert_equal 1, sampler.stats.min_call_time
assert_equal 5, sampler.stats.max_call_time

lock_n_jobs(5)
sampler.poll
sampler.poll

assert_equal 4, sampler.stats.data_point_count
assert_equal 1, sampler.stats.min_call_time
assert_equal 10, sampler.stats.max_call_time

Delayed::Job.destroy_all
sampler.poll

assert_equal 5, sampler.stats.data_point_count
assert_equal 0, sampler.stats.min_call_time
assert_equal 10, sampler.stats.max_call_time
end

end
end if defined? Delayed::Job

0 comments on commit 873b2a3

Please sign in to comment.