ghazel-newrelic_rpm 3.1.0.1 → 3.4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. data/CHANGELOG +120 -35
  2. data/LICENSE +29 -2
  3. data/README.rdoc +2 -2
  4. data/bin/mongrel_rpm +0 -0
  5. data/bin/newrelic +0 -0
  6. data/bin/newrelic_cmd +0 -0
  7. data/lib/new_relic/agent.rb +50 -38
  8. data/lib/new_relic/agent/agent.rb +459 -337
  9. data/lib/new_relic/agent/beacon_configuration.rb +71 -11
  10. data/lib/new_relic/agent/browser_monitoring.rb +73 -14
  11. data/lib/new_relic/agent/busy_calculator.rb +11 -3
  12. data/lib/new_relic/agent/chained_call.rb +2 -2
  13. data/lib/new_relic/agent/database.rb +223 -0
  14. data/lib/new_relic/agent/error_collector.rb +231 -183
  15. data/lib/new_relic/agent/instrumentation.rb +2 -2
  16. data/lib/new_relic/agent/instrumentation/active_merchant.rb +10 -2
  17. data/lib/new_relic/agent/instrumentation/active_record.rb +138 -0
  18. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +7 -1
  19. data/lib/new_relic/agent/instrumentation/authlogic.rb +6 -0
  20. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +46 -14
  21. data/lib/new_relic/agent/instrumentation/data_mapper.rb +8 -2
  22. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +11 -3
  23. data/lib/new_relic/agent/instrumentation/memcache.rb +49 -25
  24. data/lib/new_relic/agent/instrumentation/merb/controller.rb +7 -2
  25. data/lib/new_relic/agent/instrumentation/merb/errors.rb +7 -1
  26. data/lib/new_relic/agent/instrumentation/metric_frame.rb +31 -4
  27. data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +1 -5
  28. data/lib/new_relic/agent/instrumentation/net.rb +8 -2
  29. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +5 -2
  30. data/lib/new_relic/agent/instrumentation/queue_time.rb +1 -1
  31. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +66 -35
  32. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +7 -1
  33. data/lib/new_relic/agent/instrumentation/rails/errors.rb +7 -1
  34. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +121 -1
  35. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +7 -1
  36. data/lib/new_relic/agent/instrumentation/rainbows_instrumentation.rb +21 -0
  37. data/lib/new_relic/agent/instrumentation/resque.rb +80 -0
  38. data/lib/new_relic/agent/instrumentation/sinatra.rb +46 -20
  39. data/lib/new_relic/agent/instrumentation/sunspot.rb +6 -0
  40. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +7 -2
  41. data/lib/new_relic/agent/method_tracer.rb +205 -99
  42. data/lib/new_relic/agent/new_relic_service.rb +221 -0
  43. data/lib/new_relic/agent/pipe_channel_manager.rb +161 -0
  44. data/lib/new_relic/agent/pipe_service.rb +54 -0
  45. data/lib/new_relic/agent/samplers/delayed_job_sampler.rb +89 -0
  46. data/lib/new_relic/agent/samplers/memory_sampler.rb +6 -7
  47. data/lib/new_relic/agent/shim_agent.rb +5 -5
  48. data/lib/new_relic/agent/sql_sampler.rb +282 -0
  49. data/lib/new_relic/agent/stats_engine.rb +2 -0
  50. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +123 -0
  51. data/lib/new_relic/agent/stats_engine/metric_stats.rb +35 -30
  52. data/lib/new_relic/agent/stats_engine/samplers.rb +10 -4
  53. data/lib/new_relic/agent/stats_engine/transactions.rb +28 -87
  54. data/lib/new_relic/agent/transaction_info.rb +74 -0
  55. data/lib/new_relic/agent/transaction_sample_builder.rb +18 -3
  56. data/lib/new_relic/agent/transaction_sampler.rb +108 -20
  57. data/lib/new_relic/agent/worker_loop.rb +14 -6
  58. data/lib/new_relic/collection_helper.rb +19 -11
  59. data/lib/new_relic/command.rb +1 -1
  60. data/lib/new_relic/commands/deployments.rb +2 -2
  61. data/lib/new_relic/commands/install.rb +2 -13
  62. data/lib/new_relic/control.rb +2 -3
  63. data/lib/new_relic/control/class_methods.rb +12 -6
  64. data/lib/new_relic/control/configuration.rb +57 -8
  65. data/lib/new_relic/control/frameworks.rb +10 -0
  66. data/lib/new_relic/control/frameworks/external.rb +4 -4
  67. data/lib/new_relic/control/frameworks/merb.rb +2 -1
  68. data/lib/new_relic/control/frameworks/rails.rb +35 -22
  69. data/lib/new_relic/control/frameworks/rails3.rb +12 -7
  70. data/lib/new_relic/control/frameworks/ruby.rb +5 -5
  71. data/lib/new_relic/control/frameworks/sinatra.rb +1 -4
  72. data/lib/new_relic/control/instance_methods.rb +38 -12
  73. data/lib/new_relic/control/instrumentation.rb +23 -4
  74. data/lib/new_relic/control/logging_methods.rb +70 -15
  75. data/lib/new_relic/control/server_methods.rb +22 -9
  76. data/lib/new_relic/delayed_job_injection.rb +16 -3
  77. data/lib/new_relic/helper.rb +21 -0
  78. data/lib/new_relic/language_support.rb +95 -0
  79. data/lib/new_relic/local_environment.rb +92 -48
  80. data/lib/new_relic/metric_data.rb +7 -2
  81. data/lib/new_relic/metric_spec.rb +12 -9
  82. data/lib/new_relic/noticed_error.rb +6 -1
  83. data/lib/new_relic/rack/browser_monitoring.rb +18 -19
  84. data/lib/new_relic/rack/developer_mode.rb +3 -2
  85. data/lib/new_relic/recipes.rb +8 -4
  86. data/lib/new_relic/stats.rb +17 -60
  87. data/lib/new_relic/transaction_analysis.rb +2 -1
  88. data/lib/new_relic/transaction_analysis/segment_summary.rb +4 -2
  89. data/lib/new_relic/transaction_sample.rb +60 -75
  90. data/lib/new_relic/transaction_sample/segment.rb +31 -79
  91. data/lib/new_relic/version.rb +2 -2
  92. data/lib/newrelic_rpm.rb +1 -1
  93. data/newrelic.yml +2 -2
  94. data/newrelic_rpm.gemspec +46 -54
  95. data/test/active_record_fixtures.rb +3 -3
  96. data/test/config/newrelic.yml +1 -1
  97. data/test/fixtures/proc_cpuinfo.txt +575 -0
  98. data/test/new_relic/agent/agent/connect_test.rb +128 -25
  99. data/test/new_relic/agent/agent/start_test.rb +9 -94
  100. data/test/new_relic/agent/agent/start_worker_thread_test.rb +2 -4
  101. data/test/new_relic/agent/agent_test.rb +51 -78
  102. data/test/new_relic/agent/agent_test_controller.rb +1 -1
  103. data/test/new_relic/agent/agent_test_controller_test.rb +49 -33
  104. data/test/new_relic/agent/beacon_configuration_test.rb +12 -5
  105. data/test/new_relic/agent/browser_monitoring_test.rb +99 -50
  106. data/test/new_relic/agent/database_test.rb +161 -0
  107. data/test/new_relic/agent/error_collector_test.rb +47 -23
  108. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +96 -42
  109. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +0 -2
  110. data/test/new_relic/agent/instrumentation/instrumentation_test.rb +1 -1
  111. data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +3 -11
  112. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +9 -9
  113. data/test/new_relic/agent/instrumentation/queue_time_test.rb +6 -11
  114. data/test/new_relic/agent/memcache_instrumentation_test.rb +54 -18
  115. data/test/new_relic/agent/method_tracer/class_methods/add_method_tracer_test.rb +1 -1
  116. data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +1 -1
  117. data/test/new_relic/agent/method_tracer_test.rb +3 -2
  118. data/test/new_relic/agent/new_relic_service_test.rb +151 -0
  119. data/test/new_relic/agent/pipe_channel_manager_test.rb +114 -0
  120. data/test/new_relic/agent/pipe_service_test.rb +113 -0
  121. data/test/new_relic/agent/rpm_agent_test.rb +4 -31
  122. data/test/new_relic/agent/sql_sampler_test.rb +192 -0
  123. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +19 -18
  124. data/test/new_relic/agent/stats_engine_test.rb +41 -6
  125. data/test/new_relic/agent/transaction_info_test.rb +13 -0
  126. data/test/new_relic/agent/transaction_sample_builder_test.rb +27 -4
  127. data/test/new_relic/agent/transaction_sampler_test.rb +68 -46
  128. data/test/new_relic/agent/worker_loop_test.rb +3 -3
  129. data/test/new_relic/agent_test.rb +242 -0
  130. data/test/new_relic/collection_helper_test.rb +50 -28
  131. data/test/new_relic/control/configuration_test.rb +77 -0
  132. data/test/new_relic/control/logging_methods_test.rb +49 -21
  133. data/test/new_relic/control_test.rb +115 -54
  134. data/test/new_relic/delayed_job_injection_test.rb +21 -0
  135. data/test/new_relic/fake_collector.rb +210 -0
  136. data/test/new_relic/fake_service.rb +44 -0
  137. data/test/new_relic/local_environment_test.rb +14 -1
  138. data/test/new_relic/metric_parser/metric_parser_test.rb +11 -0
  139. data/test/new_relic/rack/browser_monitoring_test.rb +84 -23
  140. data/test/new_relic/rack/developer_mode_helper_test.rb +141 -0
  141. data/test/new_relic/rack/developer_mode_test.rb +31 -0
  142. data/test/new_relic/stats_test.rb +3 -18
  143. data/test/new_relic/transaction_analysis/segment_summary_test.rb +14 -0
  144. data/test/new_relic/transaction_analysis_test.rb +3 -3
  145. data/test/new_relic/transaction_sample/segment_test.rb +15 -80
  146. data/test/new_relic/transaction_sample_test.rb +25 -18
  147. data/test/script/build_test_gem.sh +51 -0
  148. data/test/script/ci.sh +140 -0
  149. data/test/script/ci_agent-tests_runner.sh +82 -0
  150. data/test/script/ci_bench.sh +52 -0
  151. data/test/script/ci_multiverse_runner.sh +63 -0
  152. data/test/test_contexts.rb +1 -0
  153. data/test/test_helper.rb +18 -5
  154. data/ui/helpers/developer_mode_helper.rb +14 -8
  155. data/ui/helpers/google_pie_chart.rb +0 -1
  156. data/ui/views/newrelic/index.rhtml +2 -2
  157. data/vendor/gems/dependency_detection-0.0.1.build/LICENSE +4 -18
  158. data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection.rb +10 -0
  159. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/mem_cache.rb +11 -11
  160. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/metric_parser.rb +17 -4
  161. data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/view.rb +4 -0
  162. metadata +50 -36
  163. data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +0 -108
  164. data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +0 -112
  165. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +0 -40
  166. data/lib/new_relic/data_serialization.rb +0 -84
  167. data/lib/new_relic/histogram.rb +0 -91
  168. data/lib/new_relic/rack/metric_app.rb +0 -65
  169. data/lib/new_relic/rack/mongrel_rpm.ru +0 -28
  170. data/lib/new_relic/rack/newrelic.yml +0 -27
  171. data/lib/new_relic/rack_app.rb +0 -6
  172. data/test/new_relic/data_serialization_test.rb +0 -70
  173. data/vendor/gems/dependency_detection-0.0.1.build/README +0 -0
  174. data/vendor/gems/metric_parser-0.1.0.pre1/LICENSE +0 -0
  175. data/vendor/gems/metric_parser-0.1.0.pre1/README +0 -0
@@ -0,0 +1,221 @@
1
+ module NewRelic
2
+ module Agent
3
+ class NewRelicService
4
+ # Specifies the version of the agent's communication protocol with
5
+ # the NewRelic hosted site.
6
+
7
+ PROTOCOL_VERSION = 8
8
+ # 14105: v8 (tag 2.10.3)
9
+ # (no v7)
10
+ # 10379: v6 (not tagged)
11
+ # 4078: v5 (tag 2.5.4)
12
+ # 2292: v4 (tag 2.3.6)
13
+ # 1754: v3 (tag 2.3.0)
14
+ # 534: v2 (shows up in 2.1.0, our first tag)
15
+
16
+ attr_accessor :request_timeout
17
+ attr_reader :collector
18
+ attr_accessor :agent_id
19
+
20
+ def initialize(license_key=control.license_key, collector=control.server)
21
+ @license_key = license_key
22
+ @collector = collector
23
+ @request_timeout = NewRelic::Control.instance.fetch('timeout', 2 * 60)
24
+ end
25
+
26
+ def connect(settings={})
27
+ host = get_redirect_host
28
+ @collector.name = host if host
29
+ response = invoke_remote(:connect, settings)
30
+ @agent_id = response['agent_run_id']
31
+ response
32
+ end
33
+
34
+ def get_redirect_host
35
+ invoke_remote(:get_redirect_host)
36
+ end
37
+
38
+ def shutdown(time)
39
+ invoke_remote(:shutdown, @agent_id, time) if @agent_id
40
+ end
41
+
42
+ def metric_data(last_harvest_time, now, unsent_timeslice_data)
43
+ invoke_remote(:metric_data, @agent_id, last_harvest_time, now,
44
+ unsent_timeslice_data)
45
+ end
46
+
47
+ def error_data(unsent_errors)
48
+ invoke_remote(:error_data, @agent_id, unsent_errors)
49
+ end
50
+
51
+ def transaction_sample_data(traces)
52
+ invoke_remote(:transaction_sample_data, @agent_id, traces)
53
+ end
54
+
55
+ def sql_trace_data(sql_traces)
56
+ invoke_remote(:sql_trace_data, sql_traces)
57
+ end
58
+
59
+ private
60
+
61
+ # A shorthand for NewRelic::Control.instance
62
+ def control
63
+ NewRelic::Control.instance
64
+ end
65
+
66
+ # Shorthand to the NewRelic::Agent.logger method
67
+ def log
68
+ NewRelic::Agent.logger
69
+ end
70
+
71
+ # The path on the server that we should post our data to
72
+ def remote_method_uri(method)
73
+ uri = "/agent_listener/#{PROTOCOL_VERSION}/#{@license_key}/#{method}"
74
+ uri << "?run_id=#{@agent_id}" if @agent_id
75
+ uri
76
+ end
77
+
78
+ # send a message via post to the actual server. This attempts
79
+ # to automatically compress the data via zlib if it is large
80
+ # enough to be worth compressing, and handles any errors the
81
+ # server may return
82
+ def invoke_remote(method, *args)
83
+ now = Time.now
84
+ #determines whether to zip the data or send plain
85
+ post_data, encoding = compress_data(args)
86
+
87
+ response = send_request(:uri => remote_method_uri(method),
88
+ :encoding => encoding,
89
+ :collector => @collector,
90
+ :data => post_data)
91
+
92
+ # raises the right exception if the remote server tells it to die
93
+ return check_for_exception(response)
94
+ rescue NewRelic::Agent::ForceRestartException => e
95
+ log.info e.message
96
+ raise
97
+ rescue SystemCallError, SocketError => e
98
+ # These include Errno connection errors
99
+ raise NewRelic::Agent::ServerConnectionException, "Recoverable error connecting to the server: #{e}"
100
+ ensure
101
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote').record_data_point((Time.now - now).to_f)
102
+ NewRelic::Agent.instance.stats_engine.get_stats_no_scope('Supportability/invoke_remote/' + method.to_s).record_data_point((Time.now - now).to_f)
103
+ end
104
+
105
+ # This method handles the compression of the request body that
106
+ # we are going to send to the server
107
+ #
108
+ # We currently optimize for CPU here since we get roughly a 10x
109
+ # reduction in message size with this, and CPU overhead is at a
110
+ # premium. For extra-large posts, we use the higher compression
111
+ # since otherwise it actually errors out.
112
+ #
113
+ # We do not compress if content is smaller than 64kb. There are
114
+ # problems with bugs in Ruby in some versions that expose us
115
+ # to a risk of segfaults if we compress aggressively.
116
+ #
117
+ # medium payloads get fast compression, to save CPU
118
+ # big payloads get all the compression possible, to stay under
119
+ # the 2,000,000 byte post threshold
120
+ def compress_data(object)
121
+ dump = Marshal.dump(object)
122
+
123
+ dump_size = dump.size
124
+
125
+ return [dump, 'identity'] if dump_size < (64*1024)
126
+
127
+ compressed_dump = Zlib::Deflate.deflate(dump, Zlib::DEFAULT_COMPRESSION)
128
+
129
+ # this checks to make sure mongrel won't choke on big uploads
130
+ check_post_size(compressed_dump)
131
+
132
+ [compressed_dump, 'deflate']
133
+ end
134
+
135
+ # Raises a PostTooBigException if the post_string is longer
136
+ # than the limit configured in the control object
137
+ def check_post_size(post_string)
138
+ # TODO: define this as a config option on the server side
139
+ return if post_string.size < control.post_size_limit
140
+ log.warn "Tried to send too much data: #{post_string.size} bytes"
141
+ raise PostTooBigException
142
+ end
143
+
144
+ # Posts to the specified server
145
+ #
146
+ # Options:
147
+ # - :uri => the path to request on the server (a misnomer of
148
+ # course)
149
+ # - :encoding => the encoding to pass to the server
150
+ # - :collector => a URI object that responds to the 'name' method
151
+ # and returns the name of the collector to
152
+ # contact
153
+ # - :data => the data to send as the body of the request
154
+ def send_request(opts)
155
+ request = Net::HTTP::Post.new(opts[:uri], 'CONTENT-ENCODING' => opts[:encoding], 'HOST' => opts[:collector].name)
156
+ request['user-agent'] = user_agent
157
+ request.content_type = "application/octet-stream"
158
+ request.body = opts[:data]
159
+
160
+ log.debug "Connect to #{opts[:collector]}#{opts[:uri]}"
161
+
162
+ response = nil
163
+ http = control.http_connection(@collector)
164
+ http.read_timeout = nil
165
+ begin
166
+ NewRelic::TimerLib.timeout(@request_timeout) do
167
+ response = http.request(request)
168
+ end
169
+ rescue Timeout::Error
170
+ log.warn "Timed out trying to post data to New Relic (timeout = #{@request_timeout} seconds)" unless @service.request_timeout < 30
171
+ raise
172
+ end
173
+ if response.is_a? Net::HTTPServiceUnavailable
174
+ raise NewRelic::Agent::ServerConnectionException, "Service unavailable (#{response.code}): #{response.message}"
175
+ elsif response.is_a? Net::HTTPGatewayTimeOut
176
+ log.debug("Timed out getting response: #{response.message}")
177
+ raise Timeout::Error, response.message
178
+ elsif response.is_a? Net::HTTPRequestEntityTooLarge
179
+ raise PostTooBigException
180
+ elsif !(response.is_a? Net::HTTPSuccess)
181
+ raise NewRelic::Agent::ServerConnectionException, "Unexpected response from server (#{response.code}): #{response.message}"
182
+ end
183
+ response
184
+ end
185
+
186
+ # Decompresses the response from the server, if it is gzip
187
+ # encoded, otherwise returns it verbatim
188
+ def decompress_response(response)
189
+ if response['content-encoding'] != 'gzip'
190
+ log.debug "Uncompressed content returned"
191
+ return response.body
192
+ end
193
+ log.debug "Decompressing return value"
194
+ i = Zlib::GzipReader.new(StringIO.new(response.body))
195
+ i.read
196
+ end
197
+
198
+ # unmarshals the response and raises it if it is an exception,
199
+ # so we can handle it in nonlocally
200
+ def check_for_exception(response)
201
+ dump = decompress_response(response)
202
+ value = Marshal.load(dump)
203
+ raise value if value.is_a? Exception
204
+ value
205
+ end
206
+
207
+ # Sets the user agent for connections to the server, to
208
+ # conform with the HTTP spec and allow for debugging. Includes
209
+ # the ruby version and also zlib version if available since
210
+ # that may cause corrupt compression if there is a problem.
211
+ def user_agent
212
+ ruby_description = ''
213
+ # note the trailing space!
214
+ ruby_description << "(ruby #{::RUBY_VERSION} #{::RUBY_PLATFORM}) " if defined?(::RUBY_VERSION) && defined?(::RUBY_PLATFORM)
215
+ zlib_version = ''
216
+ zlib_version << "zlib/#{Zlib.zlib_version}" if defined?(::Zlib) && Zlib.respond_to?(:zlib_version)
217
+ "NewRelic-RubyAgent/#{NewRelic::VERSION::STRING} #{ruby_description}#{zlib_version}"
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,161 @@
1
+ module NewRelic
2
+ module Agent
3
+ module PipeChannelManager
4
+ extend self
5
+
6
+ def register_report_channel(id)
7
+ listener.register_pipe(id)
8
+ end
9
+
10
+ def channels
11
+ listener.pipes
12
+ end
13
+
14
+ def listener
15
+ @listener ||= Listener.new
16
+ end
17
+
18
+ class Pipe
19
+ attr_accessor :in, :out
20
+ attr_reader :last_read
21
+
22
+ def initialize
23
+ @out, @in = IO.pipe
24
+ if defined?(::Encoding::ASCII_8BIT)
25
+ @in.set_encoding(::Encoding::ASCII_8BIT)
26
+ end
27
+ @last_read = Time.now
28
+ end
29
+
30
+ def close
31
+ @out.close unless @out.closed?
32
+ @in.close unless @in.closed?
33
+ end
34
+
35
+ def write(data)
36
+ @out.close unless @out.closed?
37
+ @in << Marshal.dump(data) + "\n\n"
38
+ end
39
+
40
+ def read
41
+ @in.close unless @in.closed?
42
+ @last_read = Time.now
43
+ @out.gets("\n\n")
44
+ end
45
+
46
+ def closed?
47
+ @out.closed? && @in.closed?
48
+ end
49
+ end
50
+
51
+ class Listener
52
+ attr_reader :thread
53
+ attr_accessor :pipes, :timeout, :select_timeout
54
+
55
+ def initialize
56
+ @pipes = {}
57
+ @timeout = 360
58
+ @select_timeout = 60
59
+ end
60
+
61
+ def register_pipe(id)
62
+ @pipes[id] = Pipe.new
63
+ wake.in << '.'
64
+ end
65
+
66
+ def start
67
+ return if @started == true
68
+ @started = true
69
+ @thread = Thread.new do
70
+ loop do
71
+ clean_up_pipes
72
+ pipes_to_listen_to = @pipes.values.map{|pipe| pipe.out} + [wake.out]
73
+ if ready = IO.select(pipes_to_listen_to, [], [], @select_timeout)
74
+ pipe = ready[0][0]
75
+ if pipe == wake.out
76
+ pipe.read(1)
77
+ else
78
+ merge_data_from_pipe(pipe)
79
+ end
80
+ end
81
+
82
+ break if !should_keep_listening?
83
+ end
84
+ end
85
+ @thread #.abort_on_exception = true
86
+ sleep 0.001 # give time for the thread to spawn
87
+ end
88
+
89
+ def stop
90
+ return unless @started == true
91
+ @started = false
92
+ wake.in << '.' unless wake.in.closed?
93
+ @thread.join # make sure we wait for the thread to exit
94
+ close_all_pipes
95
+ @wake.close
96
+ @wake = nil
97
+ end
98
+
99
+ def close_all_pipes
100
+ @pipes.each do |id, pipe|
101
+ pipe.close if pipe
102
+ end
103
+ @pipes = {}
104
+ end
105
+
106
+ def wake
107
+ @wake ||= Pipe.new
108
+ end
109
+
110
+ def started?
111
+ @started
112
+ end
113
+
114
+ protected
115
+
116
+ def merge_data_from_pipe(pipe_handle)
117
+ pipe = find_pipe_for_handle(pipe_handle)
118
+ got = pipe.read
119
+
120
+ if got && !got.empty?
121
+ payload = unmarshal(got)
122
+ if payload == 'EOF'
123
+ pipe.close
124
+ else
125
+ NewRelic::Agent.agent.merge_data_from([payload[:stats],
126
+ payload[:transaction_traces],
127
+ payload[:error_traces]])
128
+ end
129
+ end
130
+ end
131
+
132
+ def unmarshal(data)
133
+ if NewRelic::LanguageSupport.broken_gc?
134
+ NewRelic::LanguageSupport.with_disabled_gc do
135
+ Marshal.load(data)
136
+ end
137
+ else
138
+ Marshal.load(data)
139
+ end
140
+ end
141
+
142
+ def should_keep_listening?
143
+ @started || @pipes.values.find{|pipe| !pipe.in.closed?}
144
+ end
145
+
146
+ def clean_up_pipes
147
+ @pipes.values.each do |pipe|
148
+ if pipe.last_read.to_f + @timeout < Time.now.to_f
149
+ pipe.close unless pipe.closed?
150
+ end
151
+ end
152
+ @pipes.reject! {|id, pipe| pipe.out.closed? }
153
+ end
154
+
155
+ def find_pipe_for_handle(out_handle)
156
+ @pipes.values.find{|pipe| pipe.out == out_handle }
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,54 @@
1
+ module NewRelic
2
+ module Agent
3
+ class PipeService
4
+ attr_reader :channel_id, :buffer
5
+ attr_accessor :request_timeout, :agent_id, :collector
6
+
7
+ def initialize(channel_id)
8
+ @channel_id = channel_id
9
+ @collector = NewRelic::Control::Server.new(:name => 'parent',
10
+ :port => 0)
11
+ end
12
+
13
+ def connect(config)
14
+ nil
15
+ end
16
+
17
+ def metric_data(last_harvest_time, now, unsent_timeslice_data)
18
+ write_to_pipe(:stats => hash_from_metric_data(unsent_timeslice_data))
19
+ {}
20
+ end
21
+
22
+ def transaction_sample_data(transactions)
23
+ write_to_pipe(:transaction_traces => transactions) if transactions
24
+ end
25
+
26
+ def error_data(errors)
27
+ write_to_pipe(:error_traces => errors) if errors
28
+ end
29
+
30
+ def sql_trace_data(sql)
31
+ write_to_pipe(:sql_traces => sql) if sql
32
+ end
33
+
34
+ def shutdown(time)
35
+ write_to_pipe('EOF')
36
+ NewRelic::Agent::PipeChannelManager.channels[@channel_id].close
37
+ end
38
+
39
+ private
40
+
41
+ def hash_from_metric_data(metric_data)
42
+ metric_hash = {}
43
+ metric_data.each do |metric_entry|
44
+ metric_hash[metric_entry.metric_spec] = metric_entry
45
+ end
46
+ metric_hash
47
+ end
48
+
49
+ def write_to_pipe(data)
50
+ NewRelic::Agent::PipeChannelManager.channels[@channel_id].write(data)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,89 @@
1
+ require 'new_relic/agent/sampler'
2
+ require 'new_relic/delayed_job_injection'
3
+
4
+ module NewRelic
5
+ module Agent
6
+ module Samplers
7
+ # This sampler records the status of your delayed job table once a minute.
8
+ # It assumes jobs are cleared after being run, and failed jobs are not (otherwise
9
+ # the failed job metric is useless).
10
+ #
11
+ # In earlier versions it will break out the queue length by priority. In later
12
+ # versions of DJ where distinct queues are supported, it breaks it out by queue name.
13
+ #
14
+ class DelayedJobSampler < NewRelic::Agent::Sampler
15
+ def initialize
16
+ super :delayed_job_queue
17
+ raise Unsupported, "DJ instrumentation disabled" if NewRelic::Control.instance['disable_dj']
18
+ raise Unsupported, "No DJ worker present" unless NewRelic::DelayedJobInjection.worker_name
19
+ end
20
+
21
+ def error_stats
22
+ stats_engine.get_stats("Workers/DelayedJob/failed_jobs", false)
23
+ end
24
+ def locked_job_stats
25
+ stats_engine.get_stats("Workers/DelayedJob/locked_jobs", false)
26
+ end
27
+
28
+ def local_env
29
+ NewRelic::Control.instance.local_env
30
+ end
31
+
32
+ def worker_name
33
+ local_env.dispatcher_instance_id
34
+ end
35
+
36
+ def queued_jobs
37
+ Delayed::Job.count(:conditions => ['run_at < ? and failed_at is NULL', Time.now])
38
+ end
39
+ def failed_jobs
40
+ Delayed::Job.count(:conditions => 'failed_at is not NULL')
41
+ end
42
+ def locked_jobs
43
+ Delayed::Job.count(:conditions => 'locked_by is not NULL')
44
+ end
45
+
46
+ def self.supported_on_this_platform?
47
+ defined?(Delayed::Job)
48
+ end
49
+
50
+ def poll
51
+ record error_stats, failed_jobs
52
+ record locked_job_stats, locked_jobs
53
+
54
+ if @queue
55
+ record_queue_length_across_dimension('queue')
56
+ else
57
+ record_queue_length_across_dimension('priority')
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def record_queue_length_across_dimension(column)
64
+ all_count = 0
65
+ Delayed::Job.count(:group => column).each do | column_val, count |
66
+ all_count += count
67
+ record stats_engine.get_stats("Workers/DelayedJob/queue_length/#{column == 'queue' ? 'name' : column}/#{column_val}", false), count
68
+ end
69
+ record(stats_engine.get_stats("Workers/DelayedJob/queue_length/all", false), all_count)
70
+ end
71
+
72
+ # Figure out if we get the queues.
73
+ def setup
74
+ return unless @queue.nil?
75
+ @setup = true
76
+ columns = Delayed::Job.columns
77
+ columns.each do | c |
78
+ @queue = true if c.name.to_s == 'priority'
79
+ end
80
+ @queue ||= false
81
+ end
82
+
83
+ def record(stat, size)
84
+ stat.record_data_point size
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end