tingyun_rpm 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ting_yun/agent.rb +6 -25
  3. data/lib/ting_yun/agent/agent.rb +12 -1
  4. data/lib/ting_yun/agent/collector/error_collector.rb +2 -2
  5. data/lib/ting_yun/agent/collector/error_collector/noticed_error.rb +2 -1
  6. data/lib/ting_yun/agent/collector/middle_ware_collector/cpu_sampler.rb +1 -1
  7. data/lib/ting_yun/agent/collector/sql_sampler.rb +1 -1
  8. data/lib/ting_yun/agent/cross_app/cross_app_tracing.rb +1 -0
  9. data/lib/ting_yun/agent/datastore/metric_helper.rb +14 -7
  10. data/lib/ting_yun/agent/instance_methods/connect.rb +4 -0
  11. data/lib/ting_yun/agent/instance_methods/container_data_manager.rb +2 -1
  12. data/lib/ting_yun/agent/instance_methods/start.rb +5 -2
  13. data/lib/ting_yun/agent/transaction.rb +18 -36
  14. data/lib/ting_yun/agent/transaction/attributes.rb +5 -1
  15. data/lib/ting_yun/agent/transaction/request_attributes.rb +7 -17
  16. data/lib/ting_yun/agent/transaction/trace.rb +9 -6
  17. data/lib/ting_yun/agent/transaction/transaction_sample_builder.rb +36 -3
  18. data/lib/ting_yun/configuration/default_source.rb +23 -2
  19. data/lib/ting_yun/frameworks/instance_methods.rb +1 -1
  20. data/lib/ting_yun/instrumentation/rails3/action_controller.rb +1 -1
  21. data/lib/ting_yun/instrumentation/rails4/action_controller_subscriber.rb +4 -2
  22. data/lib/ting_yun/instrumentation/rake.rb +111 -0
  23. data/lib/ting_yun/instrumentation/support/parameter_filtering.rb +21 -0
  24. data/lib/ting_yun/instrumentation/support/split_controller.rb +3 -9
  25. data/lib/ting_yun/instrumentation/thrift.rb +76 -51
  26. data/lib/ting_yun/logger/agent_logger.rb +2 -2
  27. data/lib/ting_yun/metrics/metric_data.rb +5 -5
  28. data/lib/ting_yun/ting_yun_service/upload_service.rb +5 -4
  29. data/lib/ting_yun/version.rb +3 -1
  30. data/lib/tingyun_rpm.rb +6 -3
  31. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e8dd7aa76a5fc1e81648a4240e704b5c7402587
4
- data.tar.gz: df892c7c3fc9223d1348f8961a77c53d5c794196
3
+ metadata.gz: 76cf4f6fc864adb7857d36f09fad87c9a107767f
4
+ data.tar.gz: 8f56531a2195255e42711438b01d2cf1ca834d06
5
5
  SHA512:
6
- metadata.gz: 3b9f5b9fd4a36429475e707ad24a6733c2b40f7fadfe9e01df0c4b814cd77b2b4b72725231457871a2ed4f8daad2e2679e565424fe54ce0c174c19865df2ff05
7
- data.tar.gz: 44f175d0d5bb19c57d4b2867c90dc6d2fb97047715e8055afd9182ebac0f5be29789bdf458ec7ccda8831b6e73153cf342b03702aae5e24cf6612db6f97f10a6
6
+ metadata.gz: 030c3c16cf62bdb0dade22e4c65ddf17af9170e537a0b9cffb68d61ecda6e9f6348deb923cfd9e6025c44a7649c628e57fba770be7acef42bd2ef2d8328f0d9a
7
+ data.tar.gz: 600a8d6e656add40a7f6f1eb898347d4c366598c9266a4dbb6d3434df64d23d2e134f846fdf02ef527f5f063d81e7d850d72ba47ac9e4d100d30f2f84e5363a1
@@ -23,7 +23,6 @@ module TingYun
23
23
  def agent
24
24
  return @agent if @agent
25
25
  TingYun::Agent.logger.warn("Agent unavailable as it hasn't been started.")
26
- TingYun::Agent.logger.warn(caller.join("\n"))
27
26
  nil
28
27
  end
29
28
 
@@ -173,35 +172,17 @@ module TingYun
173
172
 
174
173
 
175
174
 
176
- # Register this method as a callback for processes that fork
177
- # jobs.
178
- #
179
- # If the master/parent connects to the agent prior to forking the
180
- # agent in the forked process will use that agent_run. Otherwise
181
- # the forked process will establish a new connection with the
182
- # server.
183
- #
184
- # Use this especially when you fork the process to run background
185
- # jobs or other work. If you are doing this with a web dispatcher
186
- # that forks worker processes then you will need to force the
187
- # agent to reconnect, which it won't do by default. Passenger and
188
- # Rainbows and Unicorn are already handled, nothing special needed for them.
175
+
176
+ # Shutdown the agent. Call this before exiting. Sends any queued data
177
+ # and kills the background thread.
189
178
  #
190
- # Options:
191
- # * <tt>:force_reconnect => true</tt> to force the spawned process to
192
- # establish a new connection, such as when forking a long running process.
193
- # The default is false--it will only connect to the server if the parent
194
- # had not connected.
195
- # * <tt>:keep_retrying => false</tt> if we try to initiate a new
196
- # connection, this tells me to only try it once so this method returns
197
- # quickly if there is some kind of latency with the server.
179
+ # @param options [Hash] Unused options Hash, for back compatibility only
198
180
  #
199
181
  # @api public
200
182
  #
201
- def after_fork(options={})
202
- agent.after_fork(options) if agent
183
+ def shutdown
184
+ agent.shutdown if agent
203
185
  end
204
186
 
205
-
206
187
  end
207
188
  end
@@ -61,6 +61,7 @@ module TingYun
61
61
  TingYun::Agent.logger.info "Starting Agent shutdown"
62
62
 
63
63
  stop_event_loop
64
+ untraced_graceful_disconnect
64
65
  reset_to_default_configuration
65
66
 
66
67
  @started = nil
@@ -122,7 +123,17 @@ module TingYun
122
123
  defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION.match(/^1\.9/)
123
124
  end
124
125
 
125
-
126
+ def untraced_graceful_disconnect
127
+ begin
128
+ TingYun::Agent.disable_all_tracing do
129
+ if connected?
130
+ transmit_data
131
+ end
132
+ end
133
+ rescue => e
134
+ ::TingYun::Agent.logger.error e
135
+ end
136
+ end
126
137
  end
127
138
  end
128
139
  end
@@ -54,7 +54,7 @@ module TingYun
54
54
  metric_names
55
55
  end
56
56
 
57
- def action_metric_name(txn,options)
57
+ def action_metric_name(txn)
58
58
  "#{ERRORS_ACTION}#{txn.best_name}" if txn
59
59
  end
60
60
 
@@ -90,7 +90,7 @@ module TingYun
90
90
 
91
91
  metric_names = aggregated_metric_names(txn)
92
92
 
93
- action_metric = action_metric_name(txn, options)
93
+ action_metric = action_metric_name(txn)
94
94
  metric_names << action_metric if action_metric
95
95
 
96
96
  stats_engine = TingYun::Agent.agent.stats_engine
@@ -119,7 +119,8 @@ module TingYun
119
119
  end
120
120
 
121
121
  def request_params
122
- attributes.agent_attributes[:request_params]
122
+ return {} unless TingYun::Agent.config['nbs.capture_params']
123
+ attributes.request_params
123
124
  end
124
125
 
125
126
  end
@@ -54,7 +54,7 @@ module TingYun
54
54
  # Calculate the true utilization by taking cpu times and dividing by
55
55
  # elapsed time X processor_count.
56
56
 
57
- record_user_util(usertime / (elapsed * @processor_count))
57
+ record_user_util((usertime * 100) / (elapsed * @processor_count))
58
58
  # record_system_util(systemtime / (elapsed * @processor_count))
59
59
  end
60
60
  @last_utime = t.utime
@@ -286,7 +286,7 @@ module TingYun
286
286
  @slow_sql.start_time,
287
287
  string(@action_metric_name),
288
288
  string(@slow_sql.metric_name),
289
- string(@uri),
289
+ string(@uri||@action_metric_name),
290
290
  string(@sql),
291
291
  int(@call_count),
292
292
  TingYun::Helper.time_to_millis(@total_call_time),
@@ -32,6 +32,7 @@ module TingYun
32
32
  t0 = Time.now.to_f
33
33
  state = TingYun::Agent::TransactionState.tl_get
34
34
  return yield unless state.execution_traced?
35
+ return yield unless state.current_transaction #如果还没有创建Transaction,就发生跨应用,就直接先跳过跟踪。
35
36
 
36
37
  begin
37
38
  node = start_trace(state, t0, request)
@@ -12,17 +12,15 @@ module TingYun
12
12
 
13
13
  NOSQL = %w(MongoDB Redis Memcached).freeze
14
14
 
15
+ CACHE = %w(Redis Memcached).freeze
16
+
15
17
  def self.checkNosql(product)
16
18
  NOSQL.include?(product)
17
19
  end
18
20
 
19
21
  def self.metric_name(product, collection, operation)
20
22
  if checkNosql(product)
21
- if product == 'MongoDB'
22
23
  "#{product}/#{collection}/#{operation}"
23
- else
24
- "#{product}/NULL/#{operation}"
25
- end
26
24
  else
27
25
  "Database #{product}/#{collection}/#{operation}"
28
26
  end
@@ -46,16 +44,25 @@ module TingYun
46
44
  collection = overrides[1] || collection
47
45
  end
48
46
  end
49
- metrics = [ALL_WEB,ALL]
50
- metrics << operation
47
+ metrics = [operation]
48
+ if TingYun::Agent::Transaction.recording_web_transaction?
49
+ metrics = metrics + [ALL_WEB,ALL]
50
+ else
51
+ metrics = metrics + [ALL_BACKGROUND,ALL]
52
+ end
53
+
54
+
51
55
  metrics = metrics.map do |suffix|
52
56
  product_suffixed_rollup(product,suffix)
53
57
  end
54
58
 
55
- metrics.unshift metric_name(product, collection, operation)
59
+ metrics.unshift metric_name(product, collection, operation) if collection
56
60
  metrics
57
61
  end
58
62
 
63
+ def self.include_database?(name)
64
+ CACHE.include?(name)
65
+ end
59
66
  # Allow Transaction#with_database_metric_name to override our
60
67
  # collection and operation
61
68
  def self.overridden_operation_and_collection #THREAD_LOCAL_ACCESS
@@ -158,6 +158,10 @@ module TingYun
158
158
  ::TingYun::Agent.logger.send(message['level'].downcase, message['message'])
159
159
  end
160
160
  end
161
+
162
+ def connect_in_sync
163
+ TingYun::Agent.disable_all_tracing { connect!(:keep_retrying => false) }
164
+ end
161
165
  end
162
166
  end
163
167
  end
@@ -125,8 +125,9 @@ module TingYun
125
125
  rescue => e
126
126
  TingYun::Agent.logger.info("Unable to send #{endpoint} data, will try again later. Error: ", e)
127
127
  # container.merge!(items)
128
+ raise
128
129
  ensure
129
- items = nil # take the initiative to GC
130
+ items = nil # to GC
130
131
  end
131
132
 
132
133
 
@@ -165,8 +165,11 @@ module TingYun
165
165
  install_exit_handler
166
166
  cpu_and_memory
167
167
 
168
- start_worker_thread(options)
169
-
168
+ if TingYun::Agent.config[:sync_startup]
169
+ connect_in_sync
170
+ else
171
+ start_worker_thread(options)
172
+ end
170
173
  end
171
174
 
172
175
  # This method should be called in a forked process after a fork.
@@ -16,12 +16,13 @@ module TingYun
16
16
  APDEX_TXN_METRIC_PREFIX = 'Apdex/'.freeze
17
17
  SUBTRANSACTION_PREFIX = 'Nested/'.freeze
18
18
  CONTROLLER_PREFIX = 'WebAction/'.freeze
19
+ RAKE_TRANSACTION_PREFIX = 'BackgroundAction/Rake'.freeze
19
20
  TASK_PREFIX = 'OtherTransaction/Background/'.freeze
20
21
  RACK_PREFIX = 'Rack/'.freeze
21
22
  SINATRA_PREFIX = 'Sinatra/'.freeze
22
23
  MIDDLEWARE_PREFIX = 'Middleware/Rack/'.freeze
23
24
  GRAPE_PREFIX = 'Grape/'.freeze
24
- RAKE_PREFIX = 'OtherTransaction/Rake/'.freeze
25
+ RAKE_PREFIX = 'Rake'.freeze
25
26
  WEB_TRANSACTION_CATEGORIES = [:controller, :uri, :rack, :sinatra, :grape, :middleware, :thrift].freeze
26
27
  EMPTY_SUMMARY_METRICS = [].freeze
27
28
  MIDDLEWARE_SUMMARY_METRICS = ['Middleware/all'.freeze].freeze
@@ -31,9 +32,6 @@ module TingYun
31
32
  NESTED_TRACE_STOP_OPTIONS = {:metric => true}.freeze
32
33
 
33
34
 
34
- WEB_SUMMARY_METRIC = 'HttpDispatcher'.freeze
35
- OTHER_SUMMARY_METRIC = 'OtherTransaction/all'.freeze
36
-
37
35
  # A Time instance for the start time, never nil
38
36
  attr_accessor :start_time
39
37
 
@@ -45,7 +43,6 @@ module TingYun
45
43
  :category,
46
44
  :frame_stack,
47
45
  :exceptions,
48
- :filtered_params,
49
46
  :default_name,
50
47
  :metrics,
51
48
  :http_response_code,
@@ -64,7 +61,6 @@ module TingYun
64
61
  @start_time = Time.now
65
62
  @apdex_start = options[:apdex_start_time] || @start_time
66
63
  @frame_stack = []
67
- @filtered_params = options[:filtered_params] || {}
68
64
  @frozen_name = nil
69
65
  @default_name = TingYun::Helper.correctly_encoded(options[:transaction_name])
70
66
  @metrics = TingYun::Agent::TransactionMetrics.new
@@ -73,6 +69,7 @@ module TingYun
73
69
 
74
70
  @attributes = TingYun::Agent::Transaction::Attributes.new
75
71
 
72
+
76
73
  if request = options[:request]
77
74
  @request_attributes = TingYun::Agent::Transaction::RequestAttributes.new request
78
75
  else
@@ -109,12 +106,15 @@ module TingYun
109
106
  category ||= :controller
110
107
  txn = state.current_transaction
111
108
  options[:client_transaction_id] = state.client_transaction_id
112
- if txn && options[:transaction_name]
109
+ if txn
113
110
  txn.create_nested_frame(state, category, options)
114
111
  else
115
112
  txn = start_new_transaction(state, category, options)
116
113
  end
117
114
 
115
+ # merge params every step into here
116
+ txn.attributes.merge_request_parameters(options[:filtered_params])
117
+
118
118
  txn
119
119
  rescue => e
120
120
  TingYun::Agent.logger.error("Exception during Transaction.start", e)
@@ -135,6 +135,7 @@ module TingYun
135
135
  TingYun::Agent.instance.events.notify(:start_transaction)
136
136
  frame_stack.push TingYun::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, Time.now.to_f)
137
137
  name_last_frame @default_name
138
+ freeze_name_and_execute if @default_name.start_with?(RAKE_TRANSACTION_PREFIX)
138
139
  end
139
140
 
140
141
  def create_nested_frame(state, category, options)
@@ -154,6 +155,7 @@ module TingYun
154
155
  end
155
156
  end
156
157
 
158
+
157
159
  def self.stop(state, end_time = Time.now)
158
160
 
159
161
  txn = state.current_transaction
@@ -228,7 +230,7 @@ module TingYun
228
230
  end
229
231
 
230
232
  def self.nested_transaction_name(name)
231
- if name.start_with?(CONTROLLER_PREFIX)
233
+ if name.start_with?(CONTROLLER_PREFIX) || name.start_with?(RAKE_TRANSACTION_PREFIX)
232
234
  "#{SUBTRANSACTION_PREFIX}#{name}"
233
235
  else
234
236
  name
@@ -236,6 +238,7 @@ module TingYun
236
238
  end
237
239
 
238
240
  def commit!(state, end_time, outermost_node_name)
241
+
239
242
  assign_agent_attributes
240
243
 
241
244
 
@@ -243,13 +246,19 @@ module TingYun
243
246
 
244
247
  sql_sampler.on_finishing_transaction(state, @frozen_name)
245
248
 
246
-
247
249
  record_summary_metrics(outermost_node_name, end_time)
248
250
  record_apdex(state, end_time)
249
251
  record_exceptions
250
252
  merge_metrics
251
253
  end
252
254
 
255
+
256
+ def record_summary_metrics(outermost_node_name,end_time)
257
+ unless @frozen_name == outermost_node_name
258
+ @metrics.record_unscoped(@frozen_name, TingYun::Helper.time_to_millis(end_time.to_f - start_time.to_f))
259
+ end
260
+ end
261
+
253
262
  def assign_agent_attributes
254
263
 
255
264
  add_agent_attribute(:threadName, "pid-#{$$}");
@@ -412,33 +421,6 @@ module TingYun
412
421
  TingYun::Agent.instance.stats_engine.merge_transaction_metrics!(@metrics, best_name)
413
422
  end
414
423
 
415
-
416
- def summary_metrics
417
- if @frozen_name.start_with?(CONTROLLER_PREFIX)
418
- [WEB_SUMMARY_METRIC]
419
- else
420
- background_summary_metrics
421
- end
422
- end
423
-
424
- def background_summary_metrics
425
- segments = @frozen_name.split('/')
426
- if segments.size > 2
427
- ["OtherTransaction/#{segments[1]}/all", OTHER_SUMMARY_METRIC]
428
- else
429
- []
430
- end
431
- end
432
-
433
-
434
- # The summary metrics recorded by this method all end up with a duration
435
- # equal to the transaction itself, and an exclusive time of zero.
436
- def record_summary_metrics(outermost_node_name, end_time)
437
- metrics = summary_metrics
438
- metrics << @frozen_name unless @frozen_name == outermost_node_name
439
- @metrics.record_unscoped(metrics, (end_time.to_f - start_time.to_f)*1000)
440
- end
441
-
442
424
  def name_last_frame(name)
443
425
  frame_stack.last.name = name
444
426
  end
@@ -5,9 +5,10 @@ module TingYun
5
5
  class Transaction
6
6
  class Attributes
7
7
 
8
- attr_accessor :agent_attributes
8
+ attr_accessor :agent_attributes, :request_params
9
9
  def initialize
10
10
  @agent_attributes = {}
11
+ @request_params = {}
11
12
  end
12
13
 
13
14
  def add_agent_attribute(key, value)
@@ -15,6 +16,9 @@ module TingYun
15
16
  @agent_attributes[key] = value
16
17
  end
17
18
 
19
+ def merge_request_parameters(hash)
20
+ @request_params.merge!(hash) if hash
21
+ end
18
22
  end
19
23
  end
20
24
  end
@@ -8,7 +8,7 @@ module TingYun
8
8
  class RequestAttributes
9
9
 
10
10
  attr_reader :request_path, :referer, :accept, :content_length, :host,
11
- :port, :user_agent, :request_method, :query_string, :header, :cookie
11
+ :port, :user_agent, :request_method, :header, :cookie
12
12
 
13
13
  HTTP_ACCEPT_HEADER_KEY = 'HTTP_ACCEPT'.freeze
14
14
 
@@ -22,7 +22,6 @@ module TingYun
22
22
  @port = port_from_request request
23
23
  @user_agent = attribute_from_request request, :user_agent
24
24
  @request_method = attribute_from_request request, :request_method
25
- @query_string = attribute_from_request request, :query_string
26
25
  @cookie = set_cookie(request)
27
26
  end
28
27
 
@@ -60,9 +59,7 @@ module TingYun
60
59
  txn.add_agent_attribute :method, request_method
61
60
  end
62
61
 
63
- if query_string
64
- txn.add_agent_attribute :request_params, request_params
65
- end
62
+
66
63
  end
67
64
 
68
65
 
@@ -71,16 +68,7 @@ module TingYun
71
68
  # Make a safe attempt to get the referer from a request object, generally successful when
72
69
  # it's a Rack request.
73
70
 
74
- def request_params
75
- hash = {}
76
- return hash if @query_string.empty?
77
- query_string.split('&').each do |param|
78
- _k,_v = param.strip.split('=')
79
- hash[_k] = _v unless _v.nil?
80
- end
81
71
 
82
- hash
83
- end
84
72
 
85
73
  def referer_from_request request
86
74
  if referer = attribute_from_request(request, :referer)
@@ -125,10 +113,12 @@ module TingYun
125
113
  def set_cookie(request)
126
114
  cookie = {}
127
115
  _c = attribute_from_env(request, 'HTTP_COOKIE')
128
- _c.split(';').each do |i|
116
+ _c.split(/\s*;\s*/).each do |i|
129
117
  _k, _v = i.split('=')
130
- cookie[_k.strip] = _v.strip
131
- end unless _c.nil?
118
+ if _k && _v
119
+ cookie[_k] = _v
120
+ end
121
+ end if _c
132
122
  cookie
133
123
  end
134
124
 
@@ -32,7 +32,7 @@ module TingYun
32
32
 
33
33
  def trace_tree
34
34
  [
35
- duration,
35
+ @start_time.round,
36
36
  request_params,
37
37
  custom_params,
38
38
  root_node.to_array
@@ -43,8 +43,8 @@ module TingYun
43
43
  [
44
44
  @start_time.round,
45
45
  duration,
46
- TingYun::Helper.correctly_encoded(metric_name),
47
- TingYun::Helper.correctly_encoded(uri),
46
+ TingYun::Helper.correctly_encoded(metric_name)|| '',
47
+ TingYun::Helper.correctly_encoded(uri||metric_name||''),
48
48
  encoder.encode(trace_tree),
49
49
  tx_id || '',
50
50
  guid
@@ -93,15 +93,18 @@ module TingYun
93
93
  end
94
94
 
95
95
  def custom_params
96
- {
96
+ custom_param = {
97
97
  :threadName => string(attributes.agent_attributes[:threadName]),
98
- :httpStatus => int(attributes.agent_attributes[:httpStatus]),
99
98
  :referer => string(attributes.agent_attributes[:referer]) || ''
100
99
  }
100
+ custom_param[:httpStatus] = int(attributes.agent_attributes[:httpStatus]) if attributes.agent_attributes[:httpStatus]
101
+
102
+ custom_param
101
103
  end
102
104
 
103
105
  def request_params
104
- attributes.agent_attributes[:request_params]
106
+ return {} unless TingYun::Agent.config['nbs.capture_params']
107
+ attributes.request_params
105
108
  end
106
109
 
107
110
  HEX_DIGITS = (0..15).map{|i| i.to_s(16)}
@@ -6,6 +6,26 @@ require 'ting_yun/agent/transaction/trace'
6
6
  module TingYun
7
7
  module Agent
8
8
  class TransactionSampleBuilder
9
+
10
+ class PlaceholderNode
11
+ attr_reader :parent_node
12
+ attr_accessor :depth
13
+
14
+ def initialize(parent_node)
15
+ @parent_node = parent_node
16
+ @depth = 1
17
+ end
18
+
19
+ # No-op - some clients expect to be able to use these to read/write
20
+ # params on TT nodes.
21
+ def [](key); end
22
+ def []=(key, value); end
23
+
24
+ # Stubbed out in case clients try to touch params directly.
25
+ def params; {}; end
26
+ def params=; end
27
+ end
28
+
9
29
  attr_reader :current_node, :trace
10
30
 
11
31
  def initialize(time=Time.now)
@@ -28,14 +48,27 @@ module TingYun
28
48
  if @trace.node_count == node_limit
29
49
  ::TingYun::Agent.logger.debug("Node limit of #{node_limit} reached, ceasing collection.")
30
50
  end
51
+ else
52
+ if @current_node.is_a?(PlaceholderNode)
53
+ @current_node.depth += 1
54
+ else
55
+ @current_node = PlaceholderNode.new(@current_node)
56
+ end
31
57
  end
32
58
  @current_node
33
59
  end
34
60
 
35
61
  def trace_exit(metric_name, time)
36
- @current_node.metric_name = metric_name
37
- @current_node.end_trace(time.to_f - @trace_start)
38
- @current_node = @current_node.parent_node
62
+ if @current_node.is_a?(PlaceholderNode)
63
+ @current_node.depth -= 1
64
+ if @current_node.depth == 0
65
+ @current_node = @current_node.parent_node
66
+ end
67
+ else
68
+ @current_node.metric_name = metric_name
69
+ @current_node.end_trace(time.to_f - @trace_start)
70
+ @current_node = @current_node.parent_node
71
+ end
39
72
  end
40
73
 
41
74
  def finish_trace(time=Time.now.to_f)
@@ -359,6 +359,13 @@ module TingYun
359
359
  :allowed_from_server => false,
360
360
  :description => 'Autodetected application component that reports metrics to Ting YUN.'
361
361
  },
362
+ :sync_startup => {
363
+ :default => false,
364
+ :public => true,
365
+ :type => Boolean,
366
+ :allowed_from_server => false,
367
+ :description => 'When set to true, forces a synchronous connection to the collector during application startup. For very short-lived processes, this helps ensure the has time to report.'
368
+ },
362
369
  :framework => {
363
370
  :default => DefaultSource.framework,
364
371
  :public => false,
@@ -565,11 +572,11 @@ module TingYun
565
572
  :allowed_from_server => true,
566
573
  :description => 'Enable or disable to use default name '
567
574
  },
568
- :capture_params => {
575
+ :'nbs.capture_params' => {
569
576
  :default => false,
570
577
  :public => true,
571
578
  :type => Boolean,
572
- :allowed_from_server => false,
579
+ :allowed_from_server => true,
573
580
  :description => 'Enable or disable the capture of HTTP request parameters to be attached to transaction traces and traced errors.'
574
581
  },
575
582
  :config_path => {
@@ -621,6 +628,20 @@ module TingYun
621
628
  :type => String,
622
629
  :allowed_from_server => true,
623
630
  :description => 'defined nme rule '
631
+ },
632
+ :disable_rake => {
633
+ :default => true,
634
+ :public => true,
635
+ :type => Boolean,
636
+ :allowed_from_server => false,
637
+ :description => 'If true, disables Rake instrumentation.'
638
+ },
639
+ :'rake.tasks' => {
640
+ :default => [],
641
+ :public => true,
642
+ :type => Array,
643
+ :allowed_from_server => false,
644
+ :description => 'Specify an array of Rake tasks to automatically instrument.'
624
645
  }
625
646
  }.freeze
626
647
  end
@@ -64,7 +64,7 @@ module TingYun
64
64
  start_agent
65
65
  install_instrumentation
66
66
  else
67
- LibraryDetection.detect!
67
+ TingYun::Support::LibraryDetection.detect!
68
68
  end
69
69
 
70
70
  end
@@ -30,7 +30,7 @@ module TingYun
30
30
 
31
31
 
32
32
  def process_action(*args)
33
- params = TingYun::Instrumentation::Support::ParameterFiltering.filter_rails_request_parameters(request.filtered_parameters)
33
+ params = TingYun::Instrumentation::Support::ParameterFiltering.flattened_filter_request_parameters(request.filtered_parameters)
34
34
  perform_action_with_tingyun_trace(:category => :controller,
35
35
  :name => self.action_name,
36
36
  :path => tingyun_metric_path,
@@ -5,6 +5,7 @@ require 'ting_yun/agent/transaction/transaction_state'
5
5
  require 'ting_yun/instrumentation/support/evented_subscriber'
6
6
  require 'ting_yun/agent/transaction'
7
7
  require 'ting_yun/instrumentation/support/split_controller'
8
+ require 'ting_yun/instrumentation/support/parameter_filtering'
8
9
 
9
10
  module TingYun
10
11
  module Instrumentation
@@ -36,9 +37,10 @@ module TingYun
36
37
  end
37
38
 
38
39
  def start_transaction(state, event)
40
+ params = TingYun::Instrumentation::Support::ParameterFiltering.flattened_filter_request_parameters(event.payload[:params])
39
41
  TingYun::Agent::Transaction.start(state, :controller,
40
42
  :request => event.request,
41
- :filtered_params => event.payload[:params],
43
+ :filtered_params => params,
42
44
  :apdex_start_time => event.queue_start,
43
45
  :transaction_name => event.metric_name)
44
46
  end
@@ -78,7 +80,7 @@ module TingYun
78
80
  if TingYun::Agent.config[:'nbs.auto_action_naming']
79
81
  @metric_name ||= "WebAction/Rails/#{metric_path}%2F#{metric_action}"
80
82
  else
81
- path
83
+ "WebAction/#{uri}"
82
84
  end
83
85
  end
84
86
  end
@@ -0,0 +1,111 @@
1
+ # encoding: utf-8
2
+
3
+ TingYun::Support::LibraryDetection.defer do
4
+
5
+ named :rake
6
+
7
+ depends_on do
8
+ defined?(::Rake)&&
9
+ !::TingYun::Agent.config[:'disable_rake'] &&
10
+ ::TingYun::Agent.config[:'rake.tasks'].any? &&
11
+ ::TingYun::Agent::Instrumentation::RakeInstrumentation.supported_version?
12
+ end
13
+
14
+ executes do
15
+ ::TingYun::Agent.logger.info 'Installing deferred Rake instrumentation'
16
+ end
17
+
18
+ executes do
19
+ module Rake
20
+ class Task
21
+ alias_method :invoke_without_tingyun, :invoke
22
+ def invoke(*args)
23
+ unless TingYun::Agent::Instrumentation::RakeInstrumentation.should_trace? name
24
+ return invoke_without_tingyun(*args)
25
+ end
26
+
27
+ TingYun::Agent::Instrumentation::RakeInstrumentation.before_invoke_transaction(self)
28
+
29
+ state = TingYun::Agent::TransactionState.tl_get
30
+ TingYun::Agent::Transaction.wrap(state, "BackgroundAction/Rake/invoke/#{name}", :rake) do
31
+ invoke_without_tingyun(*args)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ module TingYun
40
+ module Agent
41
+ module Instrumentation
42
+ module RakeInstrumentation
43
+
44
+ def self.supported_version?
45
+ ::TingYun::Support::VersionNumber.new(::Rake::VERSION) >= ::TingYun::Support::VersionNumber.new("10.0.0")
46
+ end
47
+
48
+ def self.before_invoke_transaction(task)
49
+ ensure_at_exit
50
+
51
+ if task.application.options.always_multitask
52
+ instrument_invoke_prerequisites_concurrently(task)
53
+ else
54
+ instrument_execute_on_prereqs(task)
55
+ end
56
+ rescue => e
57
+ TingYun::Agent.logger.error("Error during Rake task invoke", e)
58
+ end
59
+
60
+ def self.should_trace? name
61
+ TingYun::Agent.config[:'rake.tasks'].any? do |task|
62
+ task == name
63
+ end
64
+ end
65
+
66
+ def self.ensure_at_exit
67
+ return if @installed_at_exit
68
+
69
+ at_exit do
70
+ # The agent's default at_exit might not default to installing, but
71
+ # if we are running an instrumented rake task, we always want it.
72
+ TingYun::Agent.shutdown
73
+ end
74
+
75
+ @installed_at_exit = true
76
+ end
77
+
78
+ def self.instrument_execute_on_prereqs(task)
79
+ task.prerequisite_tasks.each do |child_task|
80
+ instrument_execute(child_task)
81
+ end
82
+ end
83
+
84
+ def self.instrument_execute(task)
85
+ return if task.instance_variable_get(:@__tingyun_instrumented_execute)
86
+
87
+ task.instance_variable_set(:@__tingyun_instrumented_execute, true)
88
+ task.instance_eval do
89
+ def execute(*args, &block)
90
+ TingYun::Agent::MethodTracer.trace_execution_scoped("Rake/execute/#{self.name}") do
91
+ super
92
+ end
93
+ end
94
+ end
95
+
96
+ instrument_execute_on_prereqs(task)
97
+ end
98
+
99
+ def self.instrument_invoke_prerequisites_concurrently(task)
100
+ task.instance_eval do
101
+ def invoke_prerequisites_concurrently(*_)
102
+ TingYun::Agent::MethodTracer.trace_execution_scoped("Rake/execute/multitask") do
103
+ super
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -13,8 +13,29 @@ module TingYun
13
13
  result = params.dup
14
14
  result.delete("controller")
15
15
  result.delete("action")
16
+ result.delete("commit")
17
+ result.delete("authenticity_token")
18
+ result.delete_if{|_,v| !v.is_a? String}
19
+ TingYun::Agent.config["nbs.ignored_params"].split(',').each{|key| result.delete(key)}
16
20
  result
17
21
  end
22
+
23
+ # turns {'a' => {'b' => 'c'}} into {'b' => 'c'}
24
+ def dot_flattened(nested_hash, result={})
25
+ nested_hash.each do |key, val|
26
+ next if val == nil
27
+ if val.respond_to?(:has_key?)
28
+ dot_flattened(val, result)
29
+ else
30
+ result[key] = val
31
+ end
32
+ end
33
+ result
34
+ end
35
+
36
+ def flattened_filter_request_parameters(params)
37
+ filter_rails_request_parameters(dot_flattened(params))
38
+ end
18
39
  end
19
40
  end
20
41
  end
@@ -1,3 +1,5 @@
1
+ require 'ting_yun/instrumentation/support/parameter_filtering'
2
+
1
3
  module TingYun
2
4
  module Instrumentation
3
5
  module Support
@@ -125,15 +127,7 @@ module TingYun
125
127
 
126
128
  # turns {'a' => {'b' => 'c'}} into {'b' => 'c'}
127
129
  def dot_flattened(nested_hash, result={})
128
- nested_hash.each do |key, val|
129
- next if val == nil
130
- if val.respond_to?(:has_key?)
131
- dot_flattened(val, result)
132
- else
133
- result[key] = val
134
- end
135
- end
136
- result
130
+ TingYun::Instrumentation::Support::ParameterFiltering.dot_flattened(nested_hash, result={})
137
131
  end
138
132
  end
139
133
  end
@@ -229,6 +229,8 @@ TingYun::Support::LibraryDetection.defer do
229
229
  state.client_tingyun_id_secret = tingyun_id_secret
230
230
  state.client_transaction_id = client_transaction_id
231
231
  state.client_req_id = client_req_id
232
+ state.transaction_sample_builder.trace.tx_id = client_transaction_id
233
+
232
234
  end
233
235
 
234
236
  alias :skip_without_tingyun :skip
@@ -241,18 +243,24 @@ TingYun::Support::LibraryDetection.defer do
241
243
 
242
244
 
243
245
  def send_message_args_with_tingyun(args_class, args = {})
244
- state = TingYun::Agent::TransactionState.tl_get
245
- return unless state.execution_traced?
246
- cross_app_id = TingYun::Agent.config[:tingyunIdSecret] or
247
- raise TingYun::Agent::CrossAppTracing::Error, "no tingyunIdSecret configured"
248
- txn_guid = state.request_guid
249
- tingyun_id = "#{cross_app_id};c=1;x=#{txn_guid}"
250
-
251
- data = TingYun::Support::Serialize::JSONWrapper.dump("TingyunID" => tingyun_id)
252
- @oprot.write_field_begin("TingyunField", 11, 6)
253
- @oprot.write_string(data)
254
- @oprot.write_field_end
255
- send_message_args_without_tingyun(args_class, args)
246
+ begin
247
+ state = TingYun::Agent::TransactionState.tl_get
248
+ return unless state.execution_traced?
249
+ cross_app_id = TingYun::Agent.config[:tingyunIdSecret] or
250
+ raise TingYun::Agent::CrossAppTracing::Error, "no tingyunIdSecret configured"
251
+ txn_guid = state.request_guid
252
+ tingyun_id = "#{cross_app_id};c=1;x=#{txn_guid}"
253
+ state.transaction_sample_builder.trace.tx_id = txn_guid
254
+
255
+ data = TingYun::Support::Serialize::JSONWrapper.dump("TingyunID" => tingyun_id)
256
+ @oprot.write_field_begin("TingyunField", 11, 6)
257
+ @oprot.write_string(data)
258
+ @oprot.write_field_end
259
+ rescue => e
260
+ TingYun::Agent.logger.error("Failed to thrift send_message_args_with_tingyun : ", e)
261
+ ensure
262
+ send_message_args_without_tingyun(args_class, args)
263
+ end
256
264
  end
257
265
 
258
266
  alias :send_message_args_without_tingyun :send_message_args
@@ -261,63 +269,80 @@ TingYun::Support::LibraryDetection.defer do
261
269
 
262
270
  def send_message_with_tingyun(name, args_class, args = {})
263
271
 
264
- tag = "#{args_class.to_s.split('::').first}.#{name}".downcase
265
- t0 = Time.now.to_f
266
- operations[tag] = {:started_time => t0}
267
- state = TingYun::Agent::TransactionState.tl_get
268
- return unless state.execution_traced?
269
- stack = state.traced_method_stack
270
- node = stack.push_frame(state,:thrift,t0)
271
- operations[tag][:node] = node
272
+ begin
273
+ tag = "#{args_class.to_s.split('::').first}.#{name}".downcase
274
+ t0 = Time.now.to_f
275
+ operations[tag] = {:started_time => t0}
276
+ state = TingYun::Agent::TransactionState.tl_get
277
+ return unless state.execution_traced?
278
+ stack = state.traced_method_stack
279
+ node = stack.push_frame(state,:thrift,t0)
280
+ operations[tag][:node] = node
281
+ rescue => e
282
+ TingYun::Agent.logger.error("Failed to thrift send_message_with_tingyun : ", e)
283
+ ensure
284
+ send_message_without_tingyun(name, args_class, args)
285
+ end
272
286
 
273
- send_message_without_tingyun(name, args_class, args)
274
287
  end
275
288
 
276
289
  alias :send_message_without_tingyun :send_message
277
290
  alias :send_message :send_message_with_tingyun
278
291
 
279
292
  def send_oneway_message_with_tingyun(name, args_class, args = {})
280
- tag = "#{args_class.to_s.split('::').first}.#{name}".downcase
281
- op_started = Time.now.to_f
282
- base, *other_metrics = metrics(tag)
283
- result = send_oneway_message_without_tingyun(name, args_class, args)
284
- duration = (Time.now.to_f - op_started)*1000
285
- TingYun::Agent.instance.stats_engine.tl_record_scoped_and_unscoped_metrics(base, other_metrics, duration)
286
- result
293
+
294
+ begin
295
+ tag = "#{args_class.to_s.split('::').first}.#{name}".downcase
296
+ op_started = Time.now.to_f
297
+ base, *other_metrics = metrics(tag)
298
+ result = send_oneway_message_without_tingyun(name, args_class, args)
299
+ duration = (Time.now.to_f - op_started)*1000
300
+ TingYun::Agent.instance.stats_engine.tl_record_scoped_and_unscoped_metrics(base, other_metrics, duration)
301
+ result
302
+ rescue => e
303
+ TingYun::Agent.logger.error("Failed to thrift send_oneway_message_with_tingyun : ", e)
304
+ return send_oneway_message_without_tingyun(name, args_class, args)
305
+ end
306
+
287
307
  end
288
308
  alias :send_oneway_message_without_tingyun :send_oneway_message
289
309
  alias :send_oneway_message :send_oneway_message_with_tingyun
290
310
 
291
311
  def receive_message_with_tingyun(result_klass)
292
- state = TingYun::Agent::TransactionState.tl_get
312
+ begin
313
+ state = TingYun::Agent::TransactionState.tl_get
293
314
 
294
- operate = operator(result_klass)
315
+ operate = operator(result_klass)
295
316
 
296
- t0, node = started_time_and_node(operate)
317
+ t0, node = started_time_and_node(operate)
297
318
 
298
319
 
299
- result = receive_message_without_tingyun(result_klass)
300
- if result.nil? || result.success.nil?
301
- e = ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, "#{operate} failed: unknown result")
302
- ::TingYun::Instrumentation::Support::ExternalError.handle_error(e,metrics(operate)[0])
303
- end
320
+ result = receive_message_without_tingyun(result_klass)
321
+ if result.nil? || result.success.nil?
322
+ e = ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, "#{operate} failed: unknown result")
323
+ ::TingYun::Instrumentation::Support::ExternalError.handle_error(e,metrics(operate)[0])
324
+ end
304
325
 
305
- t1 = Time.now.to_f
306
- node_name, *other_metrics = metrics(operate)
307
- duration = TingYun::Helper.time_to_millis(t1 - t0)
308
-
309
- TingYun::Agent.instance.stats_engine.tl_record_scoped_and_unscoped_metrics(
310
- other_metrics.pop, other_metrics, duration
311
- )
312
- if node
313
- node.name = node_name
314
- transaction_sampler = ::TingYun::Agent.instance.transaction_sampler
315
- transaction_sampler.add_node_info(:uri => "thrift:#{tingyun_host}:#{tingyun_port}/#{operate}")
316
- stack = state.traced_method_stack
317
- stack.pop_frame(state, node, node_name, t1)
318
- end
326
+ t1 = Time.now.to_f
327
+ node_name, *other_metrics = metrics(operate)
328
+ duration = TingYun::Helper.time_to_millis(t1 - t0)
319
329
 
320
- result
330
+ TingYun::Agent.instance.stats_engine.tl_record_scoped_and_unscoped_metrics(
331
+ other_metrics.pop, other_metrics, duration
332
+ )
333
+ if node
334
+ node.name = node_name
335
+ transaction_sampler = ::TingYun::Agent.instance.transaction_sampler
336
+ transaction_sampler.add_node_info(:uri => "thrift:#{tingyun_host}:#{tingyun_port}/#{operate}")
337
+ stack = state.traced_method_stack
338
+ stack.pop_frame(state, node, node_name, t1)
339
+ end
340
+
341
+ result
342
+ rescue => e
343
+ TingYun::Agent.logger.error("Failed to thrift receive_message_with_tingyun : ", e)
344
+ return receive_message_without_tingyun(result_klass)
345
+ end
321
346
  end
322
347
 
323
348
  alias :receive_message_without_tingyun :receive_message
@@ -109,7 +109,7 @@ module TingYun
109
109
  set_log_format!
110
110
  rescue => e
111
111
  @log = ::Logger.new(STDOUT)
112
- warn("check_log_file: Failed creating logger for file #{file_path}, using standard out for logging.", e)
112
+ warn("check_log_file: Failed creating logger for file #{@file_path}, using standard out for logging.", e)
113
113
  end
114
114
  end
115
115
  end
@@ -139,7 +139,7 @@ module TingYun
139
139
  @log = ::Logger.new(@file_path)
140
140
  rescue => e
141
141
  @log = ::Logger.new(STDOUT)
142
- warn("Failed creating logger for file #{file_path}, using standard out for logging.", e)
142
+ warn("Failed creating logger for file #{@file_path}, using standard out for logging.", e)
143
143
  end
144
144
  end
145
145
  end
@@ -69,14 +69,14 @@ module TingYun
69
69
 
70
70
  metrics << int(stats.call_count, stat_key)
71
71
  if stats.max_call_time != 0.0 #apedx
72
- metrics << float(stats.total_call_time.round, stat_key)
73
- metrics << float(stats.total_exclusive_time.round, stat_key)
74
- metrics << float(stats.max_call_time.round, stat_key)
72
+ metrics << float(stats.total_call_time, stat_key)
73
+ metrics << float(stats.total_exclusive_time, stat_key)
74
+ metrics << float(stats.max_call_time, stat_key)
75
75
  end
76
76
 
77
77
  if stats.min_call_time !=0.0 #
78
- metrics << float(stats.min_call_time.round, stat_key)
79
- metrics << float(stats.sum_of_squares.round, stat_key)
78
+ metrics << float(stats.min_call_time, stat_key)
79
+ metrics << float(stats.sum_of_squares, stat_key)
80
80
  end
81
81
 
82
82
  metrics
@@ -7,6 +7,7 @@ module TingYun
7
7
  class TingYunService
8
8
  module UploadService
9
9
 
10
+ EMPTY_PARENT = ''.freeze
10
11
  def compressed_json
11
12
  TingYun::Support::Serialize::Encoders::CompressedJSON
12
13
  end
@@ -55,15 +56,15 @@ module TingYun
55
56
 
56
57
  # Omit empty stats as an optimization
57
58
  unless stats.is_reset?
58
- metric_id = metric_id_cache[metric_spec.name]
59
+ metric_id = metric_id_cache[metric_spec.hash]
59
60
 
60
- if metric_spec.name.start_with?('WebAction')
61
+ if metric_spec.name.start_with?('WebAction','BackgroundAction')
61
62
  action_array << TingYun::Metrics::MetricData.new(metric_spec, stats, metric_id)
62
63
  elsif metric_spec.name.start_with?('Apdex')
63
64
  adpex_array << TingYun::Metrics::MetricData.new(metric_spec, stats, metric_id)
64
65
  elsif metric_spec.name.start_with?('Errors') && metric_spec.scope.empty?
65
66
  errors_array << TingYun::Metrics::MetricData.new(metric_spec, stats, metric_id)
66
- elsif metric_spec.name.start_with?('Database','View','MongoDB','Redis','Memcached','External','Nested', 'CPU', 'Memory', 'WebFrontend')
67
+ elsif metric_spec.name.start_with?('Database','View','MongoDB','Redis','Memcached','External','Nested', 'CPU', 'Memory', 'WebFrontend','Rake')
67
68
  if metric_spec.scope.empty?
68
69
  general_array << TingYun::Metrics::MetricData.new(metric_spec, stats, metric_id)
69
70
  else
@@ -97,7 +98,7 @@ module TingYun
97
98
  if value.is_a? Array
98
99
  value.each do |array|
99
100
  if array.is_a? Array
100
- metric_id_cache[array[0]["name"]] = array[1]
101
+ metric_id_cache[array[0]['name'].hash ^ (array[0]['parent']||EMPTY_PARENT).hash] = array[1]
101
102
  end
102
103
  end
103
104
  end
@@ -7,7 +7,9 @@ module TingYun
7
7
  MAJOR = 1
8
8
 
9
9
  MINOR = 1
10
- TINY = 2
10
+
11
+ TINY = 3
12
+
11
13
 
12
14
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
13
15
 
@@ -23,15 +23,17 @@
23
23
 
24
24
  require 'ting_yun/frameworks'
25
25
 
26
-
26
+ require 'pry'
27
+ #if the agent had started in manual , then shouldn't start in auto again
28
+ unless defined?(::TingYun::Agent) && TingYun::Agent.agent
27
29
  if defined?(Rails::VERSION)
28
30
  if Rails::VERSION::MAJOR.to_i >= 3
29
31
  module TingYun
30
32
  class Railtie < Rails::Railtie
31
33
 
32
34
  initializer "tingyun_rpm.start_plugin" do |app|
33
- TingYun::Agent.logger.info('initialize tingyun_rpm start_plugin')
34
- TingYun::Frameworks.init_start(:config => app.config)
35
+ TingYun::Agent.logger.info('initialize tingyun_rpm start_plugin')
36
+ TingYun::Frameworks.init_start(:config => app.config)
35
37
  end
36
38
  end
37
39
  end
@@ -45,3 +47,4 @@ if defined?(Rails::VERSION)
45
47
  else
46
48
  TingYun::Frameworks.init_start
47
49
  end
50
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tingyun_rpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - tingyun
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-21 00:00:00.000000000 Z
11
+ date: 2016-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -332,6 +332,7 @@ files:
332
332
  - lib/ting_yun/instrumentation/rails4/action_view_subscriber.rb
333
333
  - lib/ting_yun/instrumentation/rails4/active_record_subscriber.rb
334
334
  - lib/ting_yun/instrumentation/rails_middleware.rb
335
+ - lib/ting_yun/instrumentation/rake.rb
335
336
  - lib/ting_yun/instrumentation/redis.rb
336
337
  - lib/ting_yun/instrumentation/support/active_record_helper.rb
337
338
  - lib/ting_yun/instrumentation/support/controller_instrumentation.rb
@@ -412,4 +413,3 @@ signing_key:
412
413
  specification_version: 4
413
414
  summary: TingYun Ruby Agent
414
415
  test_files: []
415
- has_rdoc: