newrelic_rpm 2.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (107) hide show
  1. data/LICENSE +37 -0
  2. data/README +93 -0
  3. data/Rakefile +38 -0
  4. data/install.rb +37 -0
  5. data/lib/new_relic/agent.rb +26 -0
  6. data/lib/new_relic/agent/agent.rb +762 -0
  7. data/lib/new_relic/agent/chained_call.rb +13 -0
  8. data/lib/new_relic/agent/collection_helper.rb +81 -0
  9. data/lib/new_relic/agent/error_collector.rb +105 -0
  10. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +95 -0
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +151 -0
  12. data/lib/new_relic/agent/instrumentation/data_mapper.rb +90 -0
  13. data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +105 -0
  14. data/lib/new_relic/agent/instrumentation/memcache.rb +18 -0
  15. data/lib/new_relic/agent/instrumentation/merb/controller.rb +17 -0
  16. data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +15 -0
  17. data/lib/new_relic/agent/instrumentation/merb/errors.rb +6 -0
  18. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +35 -0
  19. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +27 -0
  20. data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +30 -0
  21. data/lib/new_relic/agent/instrumentation/rails/errors.rb +23 -0
  22. data/lib/new_relic/agent/instrumentation/rails/rails.rb +6 -0
  23. data/lib/new_relic/agent/method_tracer.rb +171 -0
  24. data/lib/new_relic/agent/patch_const_missing.rb +31 -0
  25. data/lib/new_relic/agent/samplers/cpu.rb +29 -0
  26. data/lib/new_relic/agent/samplers/memory.rb +55 -0
  27. data/lib/new_relic/agent/samplers/mongrel.rb +26 -0
  28. data/lib/new_relic/agent/stats_engine.rb +241 -0
  29. data/lib/new_relic/agent/synchronize.rb +40 -0
  30. data/lib/new_relic/agent/transaction_sampler.rb +281 -0
  31. data/lib/new_relic/agent/worker_loop.rb +128 -0
  32. data/lib/new_relic/api/deployments.rb +92 -0
  33. data/lib/new_relic/config.rb +194 -0
  34. data/lib/new_relic/config/merb.rb +35 -0
  35. data/lib/new_relic/config/rails.rb +113 -0
  36. data/lib/new_relic/config/ruby.rb +9 -0
  37. data/lib/new_relic/local_environment.rb +108 -0
  38. data/lib/new_relic/merbtasks.rb +6 -0
  39. data/lib/new_relic/metric_data.rb +26 -0
  40. data/lib/new_relic/metric_spec.rb +39 -0
  41. data/lib/new_relic/metrics.rb +7 -0
  42. data/lib/new_relic/noticed_error.rb +21 -0
  43. data/lib/new_relic/shim_agent.rb +95 -0
  44. data/lib/new_relic/stats.rb +359 -0
  45. data/lib/new_relic/transaction_analysis.rb +122 -0
  46. data/lib/new_relic/transaction_sample.rb +499 -0
  47. data/lib/new_relic/version.rb +111 -0
  48. data/lib/new_relic_api.rb +275 -0
  49. data/lib/newrelic_rpm.rb +27 -0
  50. data/lib/tasks/agent_tests.rake +14 -0
  51. data/lib/tasks/all.rb +4 -0
  52. data/lib/tasks/install.rake +7 -0
  53. data/newrelic.yml +137 -0
  54. data/recipes/newrelic.rb +46 -0
  55. data/test/config/newrelic.yml +26 -0
  56. data/test/config/test_config.rb +9 -0
  57. data/test/new_relic/agent/mock_ar_connection.rb +40 -0
  58. data/test/new_relic/agent/mock_scope_listener.rb +23 -0
  59. data/test/new_relic/agent/model_fixture.rb +17 -0
  60. data/test/new_relic/agent/tc_active_record.rb +91 -0
  61. data/test/new_relic/agent/tc_agent.rb +112 -0
  62. data/test/new_relic/agent/tc_collection_helper.rb +104 -0
  63. data/test/new_relic/agent/tc_controller.rb +98 -0
  64. data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +52 -0
  65. data/test/new_relic/agent/tc_error_collector.rb +127 -0
  66. data/test/new_relic/agent/tc_method_tracer.rb +306 -0
  67. data/test/new_relic/agent/tc_stats_engine.rb +218 -0
  68. data/test/new_relic/agent/tc_synchronize.rb +37 -0
  69. data/test/new_relic/agent/tc_transaction_sample.rb +175 -0
  70. data/test/new_relic/agent/tc_transaction_sample_builder.rb +200 -0
  71. data/test/new_relic/agent/tc_transaction_sampler.rb +305 -0
  72. data/test/new_relic/agent/tc_worker_loop.rb +101 -0
  73. data/test/new_relic/agent/testable_agent.rb +13 -0
  74. data/test/new_relic/tc_config.rb +36 -0
  75. data/test/new_relic/tc_deployments_api.rb +37 -0
  76. data/test/new_relic/tc_environment.rb +94 -0
  77. data/test/new_relic/tc_metric_spec.rb +150 -0
  78. data/test/new_relic/tc_shim_agent.rb +9 -0
  79. data/test/new_relic/tc_stats.rb +141 -0
  80. data/test/test_helper.rb +39 -0
  81. data/test/ui/tc_newrelic_helper.rb +44 -0
  82. data/ui/controllers/newrelic_controller.rb +200 -0
  83. data/ui/helpers/google_pie_chart.rb +55 -0
  84. data/ui/helpers/newrelic_helper.rb +286 -0
  85. data/ui/views/layouts/newrelic_default.rhtml +49 -0
  86. data/ui/views/newrelic/_explain_plans.rhtml +27 -0
  87. data/ui/views/newrelic/_sample.rhtml +12 -0
  88. data/ui/views/newrelic/_segment.rhtml +28 -0
  89. data/ui/views/newrelic/_segment_row.rhtml +14 -0
  90. data/ui/views/newrelic/_show_sample_detail.rhtml +22 -0
  91. data/ui/views/newrelic/_show_sample_sql.rhtml +19 -0
  92. data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
  93. data/ui/views/newrelic/_sql_row.rhtml +11 -0
  94. data/ui/views/newrelic/_stack_trace.rhtml +30 -0
  95. data/ui/views/newrelic/_table.rhtml +12 -0
  96. data/ui/views/newrelic/explain_sql.rhtml +45 -0
  97. data/ui/views/newrelic/images/arrow-close.png +0 -0
  98. data/ui/views/newrelic/images/arrow-open.png +0 -0
  99. data/ui/views/newrelic/images/blue_bar.gif +0 -0
  100. data/ui/views/newrelic/images/gray_bar.gif +0 -0
  101. data/ui/views/newrelic/index.rhtml +37 -0
  102. data/ui/views/newrelic/javascript/transaction_sample.js +107 -0
  103. data/ui/views/newrelic/sample_not_found.rhtml +2 -0
  104. data/ui/views/newrelic/show_sample.rhtml +62 -0
  105. data/ui/views/newrelic/show_source.rhtml +3 -0
  106. data/ui/views/newrelic/stylesheets/style.css +394 -0
  107. metadata +180 -0
@@ -0,0 +1,128 @@
1
+
2
+ # A worker loop executes a set of registered tasks on a single thread.
3
+ # A task is a proc or block with a specified call period in seconds.
4
+ module NewRelic::Agent
5
+
6
+ class WorkerLoop
7
+ include(Synchronize)
8
+
9
+ attr_reader :log
10
+ attr_reader :pid
11
+
12
+ def initialize(log = Logger.new(STDERR))
13
+ @tasks = []
14
+ @log = log
15
+ @should_run = true
16
+ @pid = $$
17
+ end
18
+
19
+ # run infinitely, calling the registered tasks at their specified
20
+ # call periods. The caller is responsible for creating the thread
21
+ # that runs this worker loop
22
+ def run
23
+ while keep_running do
24
+ run_next_task
25
+ end
26
+ end
27
+
28
+
29
+ def keep_running
30
+ @should_run && (@pid == $$)
31
+ end
32
+
33
+
34
+ def stop
35
+ @should_run = false
36
+ end
37
+
38
+ MIN_CALL_PERIOD = 0.1
39
+
40
+ # add a task to the worker loop. The task will be called approximately once
41
+ # every call_period seconds. The task is passed as a block
42
+ def add_task(call_period, &task_proc)
43
+ if call_period < MIN_CALL_PERIOD
44
+ raise ArgumentError, "Invalid Call Period (must be > #{MIN_CALL_PERIOD}): #{call_period}"
45
+ end
46
+
47
+ synchronize do
48
+ @tasks << LoopTask.new(call_period, &task_proc)
49
+ end
50
+ end
51
+
52
+ private
53
+ def get_next_task
54
+ synchronize do
55
+ return @tasks.inject do |soonest, task|
56
+ (task.next_invocation_time < soonest.next_invocation_time) ? task : soonest
57
+ end
58
+ end
59
+ end
60
+
61
+ def run_next_task
62
+ if @tasks.empty?
63
+ sleep 1.0
64
+ return
65
+ end
66
+
67
+ # get the next task to be executed, which is the task with the lowest (ie, soonest)
68
+ # next invocation time.
69
+ task = get_next_task
70
+
71
+
72
+ # sleep in chunks no longer than 1 second
73
+ while Time.now < task.next_invocation_time
74
+
75
+ # sleep until this next task's scheduled invocation time
76
+ sleep_time = [task.next_invocation_time - Time.now, 0.000001].max
77
+ sleep_time = (sleep_time > 1) ? 1 : sleep_time
78
+
79
+ sleep sleep_time
80
+
81
+ return if !keep_running
82
+ end
83
+
84
+ begin
85
+ # wrap task execution in a block that won't collect a TT
86
+ NewRelic::Agent.disable_transaction_tracing do
87
+ task.execute
88
+ end
89
+ rescue ServerError => e
90
+ log.debug "Server Error: #{e}"
91
+ rescue RuntimeError => e
92
+ # This is probably a server error which has been logged in the server along
93
+ # with your account name. Check and see if the agent listener is in the
94
+ # stack trace and log it quietly if it is.
95
+ message = "Error running task in worker loop, likely a server error (#{e})"
96
+ if e.backtrace.grep(/agent_listener/).empty?
97
+ log.error message
98
+ else
99
+ log.debug message
100
+ log.debug e.backtrace.join("\n")
101
+ end
102
+ rescue Timeout::Error, NewRelic::Agent::IgnoreSilentlyException
103
+ # Want to ignore these because they are handled already
104
+ rescue ScriptError, StandardError => e
105
+ log.error "Error running task in Agent Worker Loop (#{e.class}): #{e} "
106
+ log.debug e.backtrace.join("\n")
107
+ end
108
+ end
109
+
110
+ class LoopTask
111
+
112
+ def initialize(call_period, &task_proc)
113
+ @call_period = call_period
114
+ @last_invocation_time = Time.now
115
+ @task = task_proc
116
+ end
117
+
118
+ def next_invocation_time
119
+ @last_invocation_time + @call_period
120
+ end
121
+
122
+ def execute
123
+ @last_invocation_time = Time.now
124
+ @task.call
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,92 @@
1
+ # This is a class for executing commands related to deployment
2
+ # events
3
+
4
+ require 'optparse'
5
+
6
+ module NewRelic::API
7
+
8
+ class Deployments
9
+
10
+ def self.command; "deployments"; end
11
+
12
+ # Initialize the deployment uploader with command line args.
13
+ # Use -h to see options. Will possibly exit the VM
14
+ def initialize command_line_args
15
+ @application_id = NewRelic::Config.instance.app_name || RAILS_ENV
16
+ @user = ENV['USER']
17
+ @description = options.parse(command_line_args).join " "
18
+ end
19
+
20
+ # Run the Deployment upload in RPM via Active Resource.
21
+ # Will possibly print errors and exit the VM
22
+ def run
23
+ begin
24
+ @description = nil if @description.blank?
25
+ create_params = {
26
+ :application_id => @application_id,
27
+ :host => Socket.gethostname,
28
+ :description => @description,
29
+ :user => @user,
30
+ :revision => @revision,
31
+ :changelog => @changelog
32
+ }
33
+ d = NewRelicApi::Deployment.create(create_params)
34
+ rescue Exception => e
35
+ err "Attempting to connect to #{NewRelicApi::BaseResource.site_url}\nUnable to upload deployment (#{e.message})"
36
+ info e.backtrace.join("\n")
37
+ just_exit 1
38
+ end
39
+ if d.nil?
40
+ err "No value returned from create!"
41
+ just_exit 1
42
+ elsif d.valid?
43
+ puts "Recorded deployment to NewRelic RPM (#{d.description})"
44
+ else
45
+ err "Could not record deployment to NewRelic RPM:"
46
+ err d.errors.full_messages.join("\n")
47
+ just_exit 1
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def options
54
+ OptionParser.new "Usage: #{self.class.command} [OPTIONS] [description] ", 40 do |o|
55
+ o.separator "OPTIONS:"
56
+ o.on("-a", "--appname=DIR", String,
57
+ "Specify an application name.",
58
+ "Default: #{@application_id}") { |@application_id| }
59
+ o.on("-u", "--user=USER", String,
60
+ "Specify the user deploying.",
61
+ "Default: #{ENV['USER']}") { |@user| }
62
+ o.on("-r", "--revision=REV", String,
63
+ "Specify the revision being deployed") { |@revision | }
64
+ o.on("-c", "--changes",
65
+ "Read in a change log from the standard input") { @changelog = STDIN.read }
66
+ o.on("-?", "Print this help") { info o.help; just_exit }
67
+ o.separator ""
68
+ o.separator 'description = "short text"'
69
+ end
70
+ end
71
+
72
+ def help(message)
73
+ if message
74
+ err message
75
+ info options.help
76
+ just_exit 1
77
+ else
78
+ info options
79
+ just_exit 0
80
+ end
81
+ end
82
+ def info message
83
+ STDOUT.puts message
84
+ end
85
+ def err message
86
+ STDERR.puts message
87
+ end
88
+ def just_exit status=0
89
+ exit status
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,194 @@
1
+ require 'yaml'
2
+ #require 'new_relic/version'
3
+ require 'singleton'
4
+ require 'new_relic/agent'
5
+ require 'erb'
6
+
7
+ # Configuration supports the behavior of the agent which is dependent
8
+ # on what environment is being monitored: rails, merb, ruby, etc
9
+ # It is an abstract factory with concrete implementations under
10
+ # the config folder.
11
+ module NewRelic
12
+ class Config
13
+
14
+ def self.instance
15
+ @instance ||= new_instance
16
+ end
17
+
18
+ attr_reader :settings
19
+
20
+
21
+ @settings = nil
22
+
23
+ # Initialize the agent: install instrumentation and start the agent if
24
+ # appropriate. Subclasses may have different arguments for this when
25
+ # they are being called from different locations.
26
+ def start_plugin(*args)
27
+ if tracers_enabled?
28
+ start_agent
29
+ else
30
+ require 'new_relic/shim_agent'
31
+ end
32
+ end
33
+
34
+ # Get the app config info. It should already have been collected but
35
+ # if not we will memoize it to be safe.
36
+ def app_config_info
37
+ @app_config_info ||= gather_info
38
+ end
39
+
40
+ def [](key)
41
+ fetch(key)
42
+ end
43
+
44
+ def fetch(key, default=nil)
45
+ @settings[key].nil? ? default : @settings[key]
46
+ end
47
+
48
+ ###################################
49
+ # Agent config conveniences
50
+
51
+ def newrelic_root
52
+ File.expand_path(File.join(__FILE__, "..","..",".."))
53
+ end
54
+ def connect_to_server?
55
+ fetch('enabled', nil)
56
+ end
57
+ def developer_mode?
58
+ fetch('developer', nil)
59
+ end
60
+ def tracers_enabled?
61
+ !(ENV['NEWRELIC_ENABLE'].to_s =~ /false|off|no/i) &&
62
+ (developer_mode? || connect_to_server?)
63
+ end
64
+ def app_name
65
+ fetch('app_name', nil)
66
+ end
67
+
68
+ def to_s
69
+ puts self.inspect
70
+ "Config[#{self.app}]"
71
+ end
72
+ def log
73
+ # If we try to get a log before one has been set up, return a stdout log
74
+ unless @log
75
+ @log = Logger.new(STDOUT)
76
+ @log.level = Logger::WARN
77
+ end
78
+ @log
79
+ end
80
+
81
+ def setup_log(identifier)
82
+ log_file = "#{log_path}/#{log_file_name(identifier)}"
83
+ @log = Logger.new log_file
84
+
85
+ # change the format just for our logger
86
+
87
+ def @log.format_message(severity, timestamp, progname, msg)
88
+ "[#{timestamp.strftime("%m/%d/%y %H:%M:%S")} (#{$$})] #{severity} : #{msg}\n"
89
+ end
90
+
91
+ # set the log level as specified in the config file
92
+ case fetch("log_level","info").downcase
93
+ when "debug": @log.level = Logger::DEBUG
94
+ when "info": @log.level = Logger::INFO
95
+ when "warn": @log.level = Logger::WARN
96
+ when "error": @log.level = Logger::ERROR
97
+ when "fatal": @log.level = Logger::FATAL
98
+ else @log.level = Logger::INFO
99
+ end
100
+ log! "New Relic RPM Agent #{NewRelic::VERSION::STRING} Initialized: pid = #{$$}"
101
+ log! "Agent Log is found in #{log_file}"
102
+ @log
103
+ end
104
+
105
+ def local_env
106
+ @env ||= NewRelic::LocalEnvironment.new
107
+ end
108
+
109
+ # send the given message to STDERR so that it shows
110
+ # up in the console. This should be used for important informational messages at boot.
111
+ # The to_stderr may be implemented differently by different config subclasses.
112
+ # This will NOT print anything if the environment is unknown because this is
113
+ # probably not an environment the agent will be running in.
114
+ def log!(msg, level=:info)
115
+ return if @settings && !tracers_enabled?
116
+ to_stderr msg
117
+ log.send level, msg if log
118
+ end
119
+
120
+ protected
121
+ # Collect miscellaneous interesting info about the environment
122
+ # Called when the agent is started
123
+ def gather_info
124
+ [[:app, app]]
125
+ end
126
+
127
+ def to_stderr(msg)
128
+ STDERR.puts "** [NewRelic] " + msg
129
+ end
130
+
131
+ def start_agent
132
+ NewRelic::Agent::Agent.instance.start(local_env.environment, local_env.identifier)
133
+ end
134
+
135
+ def config_file
136
+ File.expand_path(File.join(root,"config","newrelic.yml"))
137
+ end
138
+
139
+ def log_path
140
+ path = File.join(root,'log')
141
+ unless File.directory? path
142
+ path = '.'
143
+ end
144
+ File.expand_path(path)
145
+ end
146
+
147
+ def log_file_name(identifier="")
148
+ "newrelic_agent.#{identifier.gsub(/[^-\w.]/, '_')}.log"
149
+ end
150
+
151
+ # Create the concrete class for environment specific behavior:
152
+ def self.new_instance
153
+ case
154
+ when defined? NewRelic::TEST
155
+ require 'config/test_config'
156
+ NewRelic::Config::Test.new
157
+ when defined? Merb::Plugins then
158
+ require 'new_relic/config/merb'
159
+ NewRelic::Config::Merb.new
160
+ when defined? Rails then
161
+ require 'new_relic/config/rails'
162
+ NewRelic::Config::Rails.new
163
+ else
164
+ require 'new_relic/config/ruby'
165
+ NewRelic::Config::Ruby.new
166
+ end
167
+ end
168
+
169
+ # Return a hash of settings you want to override in the newrelic.yml
170
+ # file. Maybe just for testing.
171
+
172
+ def initialize
173
+ newrelic_file = config_file
174
+ # Next two are for populating the newrelic.yml via erb binding, necessary
175
+ # when using the default newrelic.yml file
176
+ generated_for_user = ''
177
+ license_key=''
178
+ if !File.exists?(config_file)
179
+ yml_file = File.expand_path(File.join(__FILE__,"..","..","..","newrelic.yml"))
180
+ yaml = ::ERB.new(File.read(yml_file)).result(binding)
181
+ log! "Cannot find newrelic.yml file at #{config_file}."
182
+ log! "Using #{yml_file} file."
183
+ log! "Signup at rpm.newrelic.com to get a newrelic.yml file configured for a free Lite account."
184
+ else
185
+ yaml = ERB.new(File.read(config_file)).result(binding)
186
+ end
187
+ @settings = YAML.load(yaml)[env] || {}
188
+ rescue ScriptError, StandardError => e
189
+ puts e
190
+ puts e.backtrace.join("\n")
191
+ raise "Error reading newrelic.yml file: #{e}"
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,35 @@
1
+ class NewRelic::Config::Merb < NewRelic::Config
2
+
3
+ def app; :merb; end
4
+
5
+ def env
6
+ ::Merb.env
7
+ end
8
+ def root
9
+ ::Merb.root
10
+ end
11
+
12
+ def to_stderr(msg)
13
+ STDERR.puts "NewRelic ~ " + msg
14
+ end
15
+
16
+ def start_plugin
17
+ ::Merb::Plugins.add_rakefiles File.join(newrelic_root,"lib/tasks/all.rb")
18
+
19
+ # Merb gives you a Merb::Plugins.config hash...feel free to put your stuff in your piece of it
20
+ ::Merb::Plugins.config[:newrelic] = {
21
+ :config => self
22
+ }
23
+
24
+ ::Merb::BootLoader.before_app_loads do
25
+ # require code that must be loaded before the application
26
+ end
27
+
28
+ if tracers_enabled?
29
+ ::Merb::BootLoader.after_app_loads do
30
+ start_agent
31
+ end
32
+ end
33
+
34
+ end
35
+ end