diff --git a/CHANGELOG b/CHANGELOG index fc92bb1..e5bacaa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,2 +1,7 @@ * Version 1.0.0 - - Initial Release + + Initial Release: + - Camping + - Authlogic + - DelayedJob + - Paperclip diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3b4e99 --- /dev/null +++ b/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. diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 7f60e85..0000000 --- a/README.rdoc +++ /dev/null @@ -1,17 +0,0 @@ -= rpm_contrib - -Description goes here. - -== Note on Patches/Pull Requests - -* Fork the project. -* Make your feature addition or bug fix. -* Add tests for it. This is important so I don't break it in a - future version unintentionally. -* Commit, do not mess with 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. - -== Copyright - -Copyright (c) 2010 New Relic. See LICENSE for details. diff --git a/lib/rpm_contrib.rb b/lib/rpm_contrib.rb index e94da41..e7df2fe 100644 --- a/lib/rpm_contrib.rb +++ b/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 diff --git a/lib/new_relic/agent/instrumentation/authlogic.rb b/lib/rpm_contrib/instrumentation/authlogic.rb similarity index 100% rename from lib/new_relic/agent/instrumentation/authlogic.rb rename to lib/rpm_contrib/instrumentation/authlogic.rb diff --git a/lib/rpm_contrib/instrumentation/camping.rb b/lib/rpm_contrib/instrumentation/camping.rb new file mode 100644 index 0000000..86b213d --- /dev/null +++ b/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 diff --git a/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb b/lib/rpm_contrib/instrumentation/delayed_job_instrumentation.rb similarity index 95% rename from lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb rename to lib/rpm_contrib/instrumentation/delayed_job_instrumentation.rb index d803398..dea9210 100644 --- a/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +++ b/lib/rpm_contrib/instrumentation/delayed_job_instrumentation.rb @@ -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 diff --git a/lib/rpm_contrib/instrumentation/paperclip.rb b/lib/rpm_contrib/instrumentation/paperclip.rb new file mode 100644 index 0000000..848680e --- /dev/null +++ b/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 + diff --git a/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb b/lib/rpm_contrib/samplers/delayed_job_lock_sampler.rb similarity index 100% rename from lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb rename to lib/rpm_contrib/samplers/delayed_job_lock_sampler.rb diff --git a/test/delayed_job_instrumentation/delayed_job_test.rb b/test/delayed_job_instrumentation/delayed_job_test.rb new file mode 100644 index 0000000..61b2844 --- /dev/null +++ b/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