{"id":223,"date":"2010-03-07T20:40:47","date_gmt":"2010-03-08T03:40:47","guid":{"rendered":"http:\/\/blog.monnet-usa.com\/?p=223"},"modified":"2010-03-11T10:05:23","modified_gmt":"2010-03-11T17:05:23","slug":"visualize-application-metrics-on-newrelic-for-your-ruby-camping-application","status":"publish","type":"post","link":"https:\/\/blog.monnet-usa.com\/?p=223","title":{"rendered":"Visualize Application Metrics on NewRelic for your Ruby Camping Application"},"content":{"rendered":"<h3>Monitoring Web Applications<\/h3>\n<div>Traditionally monitoring a web application or a web site involves a lot of discrete activities such as<\/div>\n<ul>\n<li>Reviewing web server logs either manually for 500 errors<\/li>\n<li>Reviewing web application logs<\/li>\n<li>Reviewing database logs<\/li>\n<li>Reviewing stats produced by custom timing code around key transactions\n<\/ul>\n<div>The last point related to <b>&#8220;custom timing code&#8221;<\/b> is really hard and <b>time consuming<\/b> (ironically!). What would be best would be to leverage a framework which could <b>auto-magically<\/b> wrap specific call (or methods in the case of Ruby) with timing code. That code would also create <b>tracking data points<\/b> in a database for future graphing and analysis.<\/div>\n<p><\/p>\n<div>What would be better is an overall dashboard system presenting all key activities across service tiers (web tier, web services, database). And the value of such a system would be even greater if your app is hosted on a cloud across a server farm (like my application: <a href=\"#msm\">myskillsmap.com<\/a> on <a href=\"#hu\">Heroku<\/a>)<\/div>\n<h3>The NewRelic Instrumentation Framework<\/h3>\n<p>Enters <a href=\"#nr\">&#8220;NewRelic&#8221;<\/a>, an application performance management platform company. <a href=\"#nr\">NewRelic<\/a> has partnered with <a href=\"#hu\">Heroku<\/a> to provide various levels of service based on the sophistication you need. NewRelic&#8217;s instrumentation framework is called <a href=\"#rpm\">RPM<\/a> and has traditionally supported <\/a>Ruby On Rails<\/a>, as well as Rack and Sinatra on an experimental basis until last week&#8217;s new release.<\/p>\n<p>Since my current flagship site (<a href=\"#msm\">mySkillsMap<\/a>) is based on the <a href=\"#ca\">Ruby Camping <\/a> framework, a couple months ago I started to tinker with the <a href=\"\">RPM<\/a> framework to create an <a href=\"#rpmta\">experimental plug-in<\/a> for a simple Camping test app.<\/p>\n<p>The general idea is to attach the <b>RPM instrumentation<\/b> to all controllers of that Camping app.<\/p>\n<pre class=\"brush: ruby\">\r\n  class TodaysShoutoutsList < R '\/shoutouts'\r\n    def get\r\n\t\t@shoutouts = Campingdemo::Models::Shoutout.find :all,\r\n\t\t\t:conditions => [ 'created_at >= ?', Date.today.to_s ]\r\n\t\trender :list\r\n    end\r\n  end\r\n<\/pre>\n<p>In this example, I would like the <b>TodaysShoutoutsList<\/b> controller to be instrumented automatically.<br \/>\nSo I reviewed the basic implementation of the <b>Rack plugin<\/b> and looked into how I could <b>monkey-patch<\/b> the custom <a href='#rasvc'>service method defined by Rack<\/a> but overridden in the <a href='#rc'>Camping <\/a> framework.<br \/>\nWhat I needed was to have the <a href='#rasvc'>service method<\/a> be executed within the context of the NewRelic <b>perform_action_with_newrelic_trace<\/b> method.\n<\/p>\n<p> This turned out to be a pretty interesting exercise, as I had to really study the meta behavior in <a href='#rc'>Camping<\/a> as well as to leverage the power of the <a href='#me'>module_eval<\/a> Ruby method. Like everything in Ruby the final implementation always ends up being very concise:<\/p>\n<pre class=\"brush: ruby\">\r\nrequire 'new_relic\/agent\/instrumentation\/controller_instrumentation'\r\n\r\nmodule NewRelic\r\n  module Agent\r\n    module Instrumentation\r\n      module Camping\r\n\r\n        def self.included(mod)\r\n          (Kernel.const_get(mod.name)::Base).module_eval do\r\n            def service_with_newrelic(*args)\r\n              perform_action_with_newrelic_trace(:category => :rack) do\r\n                service_without_newrelic(*args)\r\n              end\r\n            end\r\n            \r\n            alias service_without_newrelic service\r\n            alias service service_with_newrelic\r\n          end\r\n        end\r\n        \r\n      end\r\n    end\r\n  end\r\nend\r\n  <\/pre>\n<p>Once experimental support was working and integrated in a test site,I submitted the plugin to NewRelic. Then NewRelic&#8217;s <b>Bill Kayser<\/b> merged it and tweaked it further to make it fit with the new <a href='#rpmc'>RPM_Contrib gem<\/a>.<\/p>\n<h3>Adding NewRelic Support to a Camping App, Step By Step<\/h3>\n<p>At a high-level, the implementation consists of the following steps:<\/p>\n<ol>\n<li>Get an RPM application account<\/li>\n<li>Install the RPM gems<\/li>\n<li>Configure RPM .yml file<\/li>\n<li>Integrate RPM&#8217;s plugin in your Camping app module<\/li>\n<\/ol>\n<p>Let&#8217;s get started!<\/p>\n<h5>Get an RPM application account<\/h5>\n<p>If you are an <a href='#hu'>Heroku<\/a> customer, just go to your application control panel and select the <a href='#hunr'>NewRelic add-on<\/a>. You can also do this from the command line:<\/p>\n<pre class=\"brush: bash\">\r\n$ heroku addons:add newrelic:bronze\r\n  <\/pre>\n<\/p>\n<p>If you are self-hosted or use another host, <a href='#tryrpm'>sign-up for RPM Lite<\/a>.<\/p>\n<h5>Install the RPM gems<\/h5>\n<p>If you are an <a href='#hu'>Heroku<\/a> customer, add the 2 NewRelic gems to your <b>.gems<\/b> file:<\/p>\n<pre class=\"brush: ruby\">\r\nnewrelic_rpm  --version '>= 2.10.6'\r\nrpm_contrib  --version '>= 1.0.2'\r\n  <\/pre>\n<\/p>\n<p>If you are self-hosted or use another host, install the 2 NewRelic gems the standard way:<\/p>\n<pre class=\"brush: ruby\">\r\ngem install newrelic_rpm  --version '>= 2.10.6'\r\ngem install rpm_contrib  --version '>= 1.0.2'\r\n  <\/pre>\n<\/p>\n<h5>Configure RPM .yml file<\/h5>\n<p>Before you proceed you need to find out what your license key is.<br \/>\nIf you are using Heroku run the following command:<\/p>\n<pre class=\"brush: ruby\">\r\nheroku config -all\r\n  <\/pre>\n<p>You will need the value of NEW_RELIC_LICENSE_KEY in a minute.\n<\/p>\n<p>RPM is configured using a newrelic.yml configuration file. If you don&#8217;t already have a <b>config<\/b> folder under your root application directory, create it now. Then create a <b>newrelic.yml<\/b> file with the following contents:<\/p>\n<pre class=\"brush: ruby\">\r\ncommon: &default_settings\r\n  license_key: 'PASTE THE VALUE OF NEW_RELIC_LICENSE_KEY HERE'\r\n  agent_enabled: true\r\n  app_name: PASTE THE NAME OF YOUR CAMPING APP HERE\r\n  enabled: true\r\n\r\nproduction:\r\n  <<: *default_settings\r\n  enabled: true\r\n\r\n  <\/pre>\n<p>Save the file.\n<\/p>\n<h5>Integrate RPM's plugin in your Camping app module<\/h5>\n<p>Now we just need a couple tweaks and we're on our way to get application monitoring!<\/p>\n<ol>\n<li>At the top of your Camping app source file, require the rpm_contrib gem\n<pre class=\"brush: ruby\">\r\nrequire \"rpm_contrib\"\r\n  <\/pre>\n<\/li>\n<li>Right, before the end of your main module add an include of the <b>RPMContrib::Instrumentation::Camping<\/b> module. Note that you may add a conditional statement to only include the module if you are running in your production environment.\n<pre class=\"brush: ruby\">\r\nmodule Campingdemo\r\n  #\r\n  # ...\r\n  #\r\n  include RPMContrib::Instrumentation::Camping\r\nend\r\n  <\/pre>\n<\/li>\n<li>At the end of your source file, add an instruction to start the agent:\n<pre class=\"brush: ruby\">\r\nmodule Campingdemo\r\n  # ...\r\nend\r\n# ... other modules ...\r\n\r\nNewRelic::Agent.manual_start \r\n  <\/pre>\n<p>Note that may also add a conditional statement to only start the agent if you are running in your production environment.\n     <\/li>\n<\/ol>\n<p>All right, now you're ready to push your updates to your production site and test the dashboard.<\/p>\n<h3>Testing the Dashboard<\/h3>\n<p>In my simple test app, I have 4 controllers, which I will test in order: , NewShoutout, AddShoutout, TodaysShoutoutsList. So <\/p>\n<ol>\n<li>Index - the main controller for the app<\/li>\n<li>NewShoutout - displays a form to enter a shoutout - it will POST to AddShoutOut<\/li>\n<li>AddShoutOut - inserts a new shoutout in the database and returns to the home page<\/li>\n<li>TodaysShoutoutsList - displays a table of all shoutouts created today<\/li>\n<\/ol>\n<div id=\"attachment_253\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-AddShoutOut.png\"><img loading=\"lazy\" decoding=\"async\" width=\"410\" height=\"215\" aria-describedby=\"caption-attachment-253\" src=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-AddShoutOut.png\" alt=\"\" title=\"AddShoutOut\"  class=\"size-medium wp-image-253\" srcset=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-AddShoutOut.png 410w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-AddShoutOut-300x157.png 300w\" sizes=\"auto, (max-width: 410px) 100vw, 410px\" \/><\/a><p id=\"caption-attachment-253\" class=\"wp-caption-text\">Testing the AddShoutOut controller<\/p><\/div>\n<div id=\"attachment_252\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-ViewShoutOuts.png\"><img loading=\"lazy\" decoding=\"async\" width=\"946\" height=\"172\" aria-describedby=\"caption-attachment-252\" src=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-ViewShoutOuts.png\" alt=\"\" title=\"TodaysShoutoutsList\"  class=\"size-medium wp-image-252\" srcset=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-ViewShoutOuts.png 946w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-ViewShoutOuts-300x54.png 300w\" sizes=\"auto, (max-width: 946px) 100vw, 946px\" \/><\/a><p id=\"caption-attachment-252\" class=\"wp-caption-text\">Testing the TodaysShoutoutsList controller<\/p><\/div>\n<p>Let's look at the Overview tab of the NewRelic dashboard. We should see the overall activity of the app during my test:<\/p>\n<div id=\"attachment_255\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Overview.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1172\" height=\"555\" aria-describedby=\"caption-attachment-255\" src=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Overview.png\" alt=\"\" title=\"NewRelic Overview\" class=\"size-medium wp-image-255\" srcset=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Overview.png 1172w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Overview-300x142.png 300w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Overview-1024x484.png 1024w\" sizes=\"auto, (max-width: 1172px) 100vw, 1172px\" \/><\/a><p id=\"caption-attachment-255\" class=\"wp-caption-text\">NewRelic Overview tab<\/p><\/div>\n<p>On the overview you can quickly see the response times of your app as well as the <a href='#rc'>Apdex score<\/a>, a useful metric measuring customer satisfaction-level performance.<\/p>\n<p>When selecting the Web Transactions tab, data associated with each controller is presented. You can choose from different types of stats, in this case I chose \"Most Time Consuming\".<\/p>\n<div id=\"attachment_256\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WebTransactionsOverview.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1172\" height=\"571\" aria-describedby=\"caption-attachment-256\" src=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WebTransactionsOverview.png\" alt=\"\" title=\"NewRelic Web Transactions Overview\" class=\"size-medium wp-image-256\" srcset=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WebTransactionsOverview.png 1172w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WebTransactionsOverview-300x146.png 300w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WebTransactionsOverview-1024x498.png 1024w\" sizes=\"auto, (max-width: 1172px) 100vw, 1172px\" \/><\/a><p id=\"caption-attachment-256\" class=\"wp-caption-text\">NewRelic Web Transactions Overview<\/p><\/div>\n<p>If I select the AddShoutOut controller, I will see the following detailed stats:<\/p>\n<div id=\"attachment_257\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1172\" height=\"455\" aria-describedby=\"caption-attachment-257\" src=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut1.png\" alt=\"\" title=\"AddShoutOut\"  class=\"size-medium wp-image-257\" srcset=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut1.png 1172w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut1-300x116.png 300w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut1-1024x397.png 1024w\" sizes=\"auto, (max-width: 1172px) 100vw, 1172px\" \/><\/a><p id=\"caption-attachment-257\" class=\"wp-caption-text\">Details for the AddShoutOut controller<\/p><\/div>\n<p><a href=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1172\" height=\"470\" src=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut2.png\" alt=\"\" title=\"AddShoutOut2\" class=\"alignnone size-medium wp-image-258\" srcset=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut2.png 1172w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut2-300x120.png 300w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-WT-AddShoutOut2-1024x410.png 1024w\" sizes=\"auto, (max-width: 1172px) 100vw, 1172px\" \/><\/a><\/p>\n<p>On these graphs you can see not only the performance and the Apdex data for the controller, but you can also drill down into the breakdown of the controller in terms of SQL calls.<\/p>\n<p>When selecting the Database tab, we can see performance data for each type of SQL queries performed by the controllers:<\/p>\n<div id=\"attachment_254\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Database-SQL.png\"><img loading=\"lazy\" decoding=\"async\" width=\"968\" height=\"520\" aria-describedby=\"caption-attachment-254\" src=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Database-SQL.png\" alt=\"\" title=\"NewRelic Database SQL\" class=\"size-medium wp-image-254\" srcset=\"https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Database-SQL.png 968w, https:\/\/blog.monnet-usa.com\/wp-content\/uploads\/2010\/03\/CampingDemo-NewRelic-Database-SQL-300x161.png 300w\" sizes=\"auto, (max-width: 968px) 100vw, 968px\" \/><\/a><p id=\"caption-attachment-254\" class=\"wp-caption-text\">NewRelic Database tab<\/p><\/div>\n<p>So as you can see in this brief demo, it is pretty easy to identify bottlenecks in your apps and tune them appropriately. Empowered with performance data across all tiers, you can also make appropriate decisions in terms of how to scale the cloud platform your application is running on.<\/p>\n<h3>So What?<\/h3>\n<p>NewRelic RPM makes it trivial to get powerful application performance monitoring. If you are hosting your app on <a href=\"#hu\">Heroku<\/a>, just add the NewRelic resource and update your app to integrate the <a href='#rpmca'>NewRelic Camping plugin<\/a>. If you are hosting your app somewhere else you can try <a href='#tryrpm'>RPM Lite<\/a> to experience its potential.<\/p>\n<p>You don't have to envy Rails, Rack, and Sinatra for getting NewRelic support. The <a href='#rpmca'>NewRelic Camping plugin<\/a> now allows you to integrate with NewRelic. So now you no longer have an excuse and you can finally enjoy the benefit of the application performance monitoring dashboard! :-)<\/p>\n<h3>References and Resources<\/h3>\n<ul>\n<li><a name='ax' href='http:\/\/www.apdex.org\/'>Apdex web site<\/a><\/li>\n<li><a name='rc' href='http:\/\/github.com\/camping\/camping'>Ruby Camping<\/a><\/li>\n<li><a name='hu' href='http:\/\/heroku.com\/'>Heroku Cloud Hosting for Ruby<\/a><\/li>\n<li><a name='msm' href='http:\/\/www.myskillsmap.com\/'>my Skills Map<\/a><\/li>\n<li><a name='nr' href='http:\/\/newrelic.com\/'>NewRelic<\/a><\/li>\n<li><a name='rpm' href='http:\/\/support.newrelic.com\/faqs'>NewRelic RPM Doc\/FAQ<\/a><\/li>\n<li><a name='me' href='http:\/\/ruby-doc.org\/docs\/ProgrammingRuby\/html\/ref_c_module.html#Module.module_eval'>module_eval in Ruby<\/a><\/li>\n<li><a name='rpmta' href='http:\/\/github.com\/techarch\/rpm\/blob\/master\/lib\/new_relic\/agent\/instrumentation\/camping.rb'>My experimental Camping NewRelic Plugin<\/a><\/li>\n<li><a name='rpmca' href='http:\/\/github.com\/newrelic\/rpm_contrib\/blob\/master\/lib\/rpm_contrib\/instrumentation\/camping.rb'>The new \"official\" Camping NewRelic Plugin<\/a><\/li>\n<li><a name='rpmc' href='http:\/\/github.com\/newrelic\/rpm_contrib'>NewRelic RPM_Contrib gem<\/a><\/li>\n<li><a name='tryrpm' href='http:\/\/newrelic.com\/get-RPM.html'>Try NewRelic RPM<\/a><\/li>\n<li><a name='hunr' href='http:\/\/docs.heroku.com\/newrelic'>NewRelic RPM Add-On For Heroku<\/a><\/li>\n<li><a name='rasvc' href='http:\/\/github.com\/rack\/rack\/blob\/master\/lib\/rack\/handler\/webrick.rb'>Rack service method<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Monitoring Web Applications Traditionally monitoring a web application or a web site involves a lot of discrete activities such as Reviewing web server logs either manually for 500 errors Reviewing web application logs Reviewing database logs Reviewing stats produced by custom timing code around key transactions The last point related to &#8220;custom timing code&#8221; is [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[25,3,28,4],"tags":[23,27,26,29,56],"class_list":["post-223","post","type-post","status-publish","format-standard","hentry","category-performance-mgt","category-ruby","category-ruby-camping","category-tools","tag-camping","tag-heroku","tag-newrelic","tag-performance-monitoring","tag-ruby"],"_links":{"self":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/223","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=223"}],"version-history":[{"count":45,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/223\/revisions"}],"predecessor-version":[{"id":274,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/223\/revisions\/274"}],"wp:attachment":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=223"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=223"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=223"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}