tingyun_rpm 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +4 -0
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +3 -0
  7. data/Guardfile +25 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +41 -0
  10. data/cert/cacert.pem +0 -0
  11. data/lib/ting_yun/agent/agent.rb +128 -0
  12. data/lib/ting_yun/agent/class_methods.rb +21 -0
  13. data/lib/ting_yun/agent/collector/base_sampler.rb +2 -0
  14. data/lib/ting_yun/agent/collector/error_collector/error_trace_array.rb +88 -0
  15. data/lib/ting_yun/agent/collector/error_collector/noticed_error.rb +129 -0
  16. data/lib/ting_yun/agent/collector/error_collector.rb +165 -0
  17. data/lib/ting_yun/agent/collector/middle_ware_collector/cpu_sampler.rb +68 -0
  18. data/lib/ting_yun/agent/collector/middle_ware_collector/memory_sampler.rb +139 -0
  19. data/lib/ting_yun/agent/collector/middle_ware_collector/middle_ware.rb +13 -0
  20. data/lib/ting_yun/agent/collector/middle_ware_collector/sampler.rb +59 -0
  21. data/lib/ting_yun/agent/collector/middle_ware_collector.rb +80 -0
  22. data/lib/ting_yun/agent/collector/sql_sampler.rb +299 -0
  23. data/lib/ting_yun/agent/collector/stats_engine/metric_stats.rb +170 -0
  24. data/lib/ting_yun/agent/collector/stats_engine/stats_hash.rb +172 -0
  25. data/lib/ting_yun/agent/collector/stats_engine.rb +28 -0
  26. data/lib/ting_yun/agent/collector/transaction_sampler/slowest_sample_buffer.rb +25 -0
  27. data/lib/ting_yun/agent/collector/transaction_sampler/transaction_sample_buffer_base.rb +96 -0
  28. data/lib/ting_yun/agent/collector/transaction_sampler.rb +226 -0
  29. data/lib/ting_yun/agent/container_data_manager.rb +94 -0
  30. data/lib/ting_yun/agent/cross_app/cross_app_monitor.rb +131 -0
  31. data/lib/ting_yun/agent/cross_app/cross_app_tracing.rb +202 -0
  32. data/lib/ting_yun/agent/cross_app/inbound_request_monitor.rb +22 -0
  33. data/lib/ting_yun/agent/database.rb +410 -0
  34. data/lib/ting_yun/agent/datastore/metric_helper.rb +82 -0
  35. data/lib/ting_yun/agent/datastore/mongo.rb +44 -0
  36. data/lib/ting_yun/agent/datastore.rb +33 -0
  37. data/lib/ting_yun/agent/dispatcher.rb +39 -0
  38. data/lib/ting_yun/agent/event/event_listener.rb +47 -0
  39. data/lib/ting_yun/agent/event/event_loop.rb +194 -0
  40. data/lib/ting_yun/agent/instance_methods/connect.rb +164 -0
  41. data/lib/ting_yun/agent/instance_methods/container_data_manager.rb +137 -0
  42. data/lib/ting_yun/agent/instance_methods/handle_errors.rb +71 -0
  43. data/lib/ting_yun/agent/instance_methods/start.rb +219 -0
  44. data/lib/ting_yun/agent/instance_methods/start_worker_thread.rb +51 -0
  45. data/lib/ting_yun/agent/instance_methods.rb +39 -0
  46. data/lib/ting_yun/agent/method_tracer.rb +256 -0
  47. data/lib/ting_yun/agent/method_tracer_helpers.rb +85 -0
  48. data/lib/ting_yun/agent/threading/agent_thread.rb +49 -0
  49. data/lib/ting_yun/agent/transaction/attributes.rb +22 -0
  50. data/lib/ting_yun/agent/transaction/request_attributes.rb +126 -0
  51. data/lib/ting_yun/agent/transaction/trace.rb +125 -0
  52. data/lib/ting_yun/agent/transaction/trace_node.rb +110 -0
  53. data/lib/ting_yun/agent/transaction/traced_method_stack.rb +80 -0
  54. data/lib/ting_yun/agent/transaction/transaction_metrics.rb +51 -0
  55. data/lib/ting_yun/agent/transaction/transaction_sample_builder.rb +63 -0
  56. data/lib/ting_yun/agent/transaction/transaction_state.rb +112 -0
  57. data/lib/ting_yun/agent/transaction.rb +522 -0
  58. data/lib/ting_yun/agent.rb +207 -0
  59. data/lib/ting_yun/configuration/default_source.rb +638 -0
  60. data/lib/ting_yun/configuration/dotted_hash.rb +46 -0
  61. data/lib/ting_yun/configuration/environment_source.rb +116 -0
  62. data/lib/ting_yun/configuration/manager.rb +232 -0
  63. data/lib/ting_yun/configuration/manual_source.rb +14 -0
  64. data/lib/ting_yun/configuration/server_source.rb +88 -0
  65. data/lib/ting_yun/configuration/yaml_source.rb +136 -0
  66. data/lib/ting_yun/configuration.rb +9 -0
  67. data/lib/ting_yun/environment_report.rb +123 -0
  68. data/lib/ting_yun/frameworks/class_methods.rb +47 -0
  69. data/lib/ting_yun/frameworks/external.rb +15 -0
  70. data/lib/ting_yun/frameworks/instance_methods.rb +120 -0
  71. data/lib/ting_yun/frameworks/instrumentation.rb +67 -0
  72. data/lib/ting_yun/frameworks/rails.rb +63 -0
  73. data/lib/ting_yun/frameworks/rails3.rb +26 -0
  74. data/lib/ting_yun/frameworks/rails4.rb +14 -0
  75. data/lib/ting_yun/frameworks/ruby.rb +17 -0
  76. data/lib/ting_yun/frameworks/sinatra.rb +10 -0
  77. data/lib/ting_yun/frameworks.rb +34 -0
  78. data/lib/ting_yun/http/generic_request.rb +8 -0
  79. data/lib/ting_yun/http/net_http_request.rb +46 -0
  80. data/lib/ting_yun/instrumentation/active_record.rb +103 -0
  81. data/lib/ting_yun/instrumentation/middleware_proxy.rb +77 -0
  82. data/lib/ting_yun/instrumentation/middleware_tracing.rb +84 -0
  83. data/lib/ting_yun/instrumentation/mongo.rb +103 -0
  84. data/lib/ting_yun/instrumentation/mongo2.rb +37 -0
  85. data/lib/ting_yun/instrumentation/mongo_command_log_subscriber.rb +97 -0
  86. data/lib/ting_yun/instrumentation/moped.rb +95 -0
  87. data/lib/ting_yun/instrumentation/net.rb +59 -0
  88. data/lib/ting_yun/instrumentation/rack.rb +109 -0
  89. data/lib/ting_yun/instrumentation/rails3/action_controller.rb +63 -0
  90. data/lib/ting_yun/instrumentation/rails3/action_view.rb +115 -0
  91. data/lib/ting_yun/instrumentation/rails4/action_controller_subscriber.rb +124 -0
  92. data/lib/ting_yun/instrumentation/rails4/action_view_subscriber.rb +118 -0
  93. data/lib/ting_yun/instrumentation/rails4/active_record_subscriber.rb +124 -0
  94. data/lib/ting_yun/instrumentation/rails_middleware.rb +38 -0
  95. data/lib/ting_yun/instrumentation/redis.rb +70 -0
  96. data/lib/ting_yun/instrumentation/support/active_record_helper.rb +178 -0
  97. data/lib/ting_yun/instrumentation/support/controller_instrumentation.rb +54 -0
  98. data/lib/ting_yun/instrumentation/support/database.rb +38 -0
  99. data/lib/ting_yun/instrumentation/support/event_formatter.rb +19 -0
  100. data/lib/ting_yun/instrumentation/support/evented_subscriber.rb +97 -0
  101. data/lib/ting_yun/instrumentation/support/external_error.rb +52 -0
  102. data/lib/ting_yun/instrumentation/support/metric_translator.rb +84 -0
  103. data/lib/ting_yun/instrumentation/support/mongo_formatter.rb +49 -0
  104. data/lib/ting_yun/instrumentation/support/parameter_filtering.rb +21 -0
  105. data/lib/ting_yun/instrumentation/support/queue_time.rb +76 -0
  106. data/lib/ting_yun/instrumentation/support/transaction_namer.rb +68 -0
  107. data/lib/ting_yun/instrumentation/thrift.rb +329 -0
  108. data/lib/ting_yun/logger/agent_logger.rb +196 -0
  109. data/lib/ting_yun/logger/log_once.rb +38 -0
  110. data/lib/ting_yun/logger/memory_logger.rb +56 -0
  111. data/lib/ting_yun/logger/null_logger.rb +31 -0
  112. data/lib/ting_yun/logger/startup_logger.rb +13 -0
  113. data/lib/ting_yun/logger.rb +8 -0
  114. data/lib/ting_yun/metrics/metric_data.rb +86 -0
  115. data/lib/ting_yun/metrics/metric_spec.rb +89 -0
  116. data/lib/ting_yun/metrics/stats.rb +158 -0
  117. data/lib/ting_yun/metrics.rb +12 -0
  118. data/lib/ting_yun/support/coerce.rb +86 -0
  119. data/lib/ting_yun/support/collector.rb +29 -0
  120. data/lib/ting_yun/support/exception.rb +79 -0
  121. data/lib/ting_yun/support/hash_extensions.rb +25 -0
  122. data/lib/ting_yun/support/helper.rb +54 -0
  123. data/lib/ting_yun/support/hostname.rb +13 -0
  124. data/lib/ting_yun/support/http_clients/uri_util.rb +49 -0
  125. data/lib/ting_yun/support/language_support.rb +155 -0
  126. data/lib/ting_yun/support/library_detection.rb +129 -0
  127. data/lib/ting_yun/support/local_environment.rb +185 -0
  128. data/lib/ting_yun/support/path.rb +13 -0
  129. data/lib/ting_yun/support/serialize/encodes.rb +61 -0
  130. data/lib/ting_yun/support/serialize/encoding_normalizer.rb +84 -0
  131. data/lib/ting_yun/support/serialize/json_marshaller.rb +73 -0
  132. data/lib/ting_yun/support/serialize/json_wrapper.rb +78 -0
  133. data/lib/ting_yun/support/serialize/marshaller.rb +69 -0
  134. data/lib/ting_yun/support/serialize/ok_json.rb +651 -0
  135. data/lib/ting_yun/support/system_info.rb +206 -0
  136. data/lib/ting_yun/support/timer_lib.rb +29 -0
  137. data/lib/ting_yun/support/version_number.rb +70 -0
  138. data/lib/ting_yun/ting_yun_service/connection.rb +118 -0
  139. data/lib/ting_yun/ting_yun_service/http.rb +41 -0
  140. data/lib/ting_yun/ting_yun_service/request.rb +90 -0
  141. data/lib/ting_yun/ting_yun_service/ssl.rb +45 -0
  142. data/lib/ting_yun/ting_yun_service/upload_service.rb +149 -0
  143. data/lib/ting_yun/ting_yun_service.rb +124 -0
  144. data/lib/ting_yun/version.rb +17 -0
  145. data/lib/tingyun_rpm.rb +47 -0
  146. data/tingyun_rpm.gemspec +60 -0
  147. metadata +415 -0
@@ -0,0 +1,112 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent/transaction/traced_method_stack'
4
+ module TingYun
5
+ module Agent
6
+
7
+ # This is THE location to store thread local information during a transaction
8
+ # Need a new piece of data? Add a method here, NOT a new thread local variable.
9
+ class TransactionState
10
+
11
+ # Request data
12
+ attr_accessor :request, :transaction_sample_builder
13
+ attr_reader :current_transaction, :traced_method_stack
14
+ # Sql Sampler Transaction Data
15
+ attr_accessor :sql_sampler_transaction_data,
16
+ :client_transaction_id,
17
+ :client_tingyun_id_secret,
18
+ :client_req_id,
19
+ :sql_duration,
20
+ :external_duration,
21
+ :web_duration,
22
+ :queue_duration,
23
+ :rds_duration,
24
+ :mc_duration,
25
+ :mon_duration,
26
+ :thrift_return_data
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+ def self.tl_get
35
+ tl_state_for(Thread.current)
36
+ end
37
+
38
+ # This method should only be used by TransactionState for access to the
39
+ # current thread's state or to provide read-only accessors for other threads
40
+ #
41
+ # If ever exposed, this requires additional synchronization
42
+ def self.tl_state_for(thread)
43
+ state = thread[:tingyun_transaction_state]
44
+
45
+ if state.nil?
46
+ state = TransactionState.new
47
+ thread[:tingyun_transaction_state] = state
48
+ end
49
+
50
+ state
51
+ end
52
+
53
+ def initialize
54
+ @untraced = []
55
+ @current_transaction = nil
56
+ @traced_method_stack = TingYun::Agent::TracedMethodStack.new
57
+ @sql_duration = 0
58
+ @external_duration = 0
59
+ @web_duration = 0
60
+ @queue_duration = 0
61
+ @rds_duration = 0
62
+ @mc_duration = 0
63
+ @mon_duration = 0
64
+ end
65
+
66
+ # This starts the timer for the transaction.
67
+ def reset(transaction=nil)
68
+ # We purposefully don't reset @untraced, @record_tt and @record_sql
69
+ # since those are managed by TingYun::Agent.disable_* calls explicitly
70
+ # and (more importantly) outside the scope of a transaction
71
+ @request = nil
72
+ @current_transaction = transaction
73
+ @traced_method_stack.clear
74
+ @transaction_sample_builder = nil
75
+ @sql_sampler_transaction_data = nil
76
+ @cross_tx_data = nil
77
+ @thrift_return_data = nil
78
+ end
79
+
80
+ # TT's and SQL
81
+ attr_accessor :record_tt, :record_sql
82
+ attr_accessor :untraced
83
+
84
+ def push_traced(should_trace)
85
+ @untraced << should_trace
86
+ end
87
+
88
+ def pop_traced
89
+ @untraced.pop if @untraced
90
+ end
91
+
92
+ def execution_traced?
93
+ @untraced.nil? || @untraced.last != false
94
+ end
95
+
96
+ def sql_recorded?
97
+ @record_sql != false
98
+ end
99
+
100
+ def transaction_traced?
101
+ @record_tt != false
102
+ end
103
+
104
+ def request_guid
105
+ return nil unless current_transaction
106
+ current_transaction.guid
107
+ end
108
+
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,522 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent'
4
+ require 'ting_yun/support/helper'
5
+ require 'ting_yun/agent/method_tracer_helpers'
6
+ require 'ting_yun/agent/transaction/transaction_metrics'
7
+ require 'ting_yun/agent/transaction/request_attributes'
8
+ require 'ting_yun/agent/transaction/attributes'
9
+
10
+
11
+ module TingYun
12
+ module Agent
13
+ class Transaction
14
+
15
+
16
+ APDEX_TXN_METRIC_PREFIX = 'Apdex/'.freeze
17
+ SUBTRANSACTION_PREFIX = 'Nested/'.freeze
18
+ CONTROLLER_PREFIX = 'WebAction/'.freeze
19
+ TASK_PREFIX = 'OtherTransaction/Background/'.freeze
20
+ RACK_PREFIX = 'Rack/'.freeze
21
+ SINATRA_PREFIX = 'Sinatra/'.freeze
22
+ MIDDLEWARE_PREFIX = 'Middleware/Rack/'.freeze
23
+ GRAPE_PREFIX = 'Grape/'.freeze
24
+ RAKE_PREFIX = 'OtherTransaction/Rake/'.freeze
25
+ WEB_TRANSACTION_CATEGORIES = [:controller, :uri, :rack, :sinatra, :grape, :middleware, :thrift].freeze
26
+ EMPTY_SUMMARY_METRICS = [].freeze
27
+ MIDDLEWARE_SUMMARY_METRICS = ['Middleware/all'.freeze].freeze
28
+
29
+ TRACE_OPTIONS_SCOPED = {:metric => true, :scoped_metric => true}.freeze
30
+ TRACE_OPTIONS_UNSCOPED = {:metric => true, :scoped_metric => false}.freeze
31
+ NESTED_TRACE_STOP_OPTIONS = {:metric => true}.freeze
32
+
33
+
34
+ WEB_SUMMARY_METRIC = 'HttpDispatcher'.freeze
35
+ OTHER_SUMMARY_METRIC = 'OtherTransaction/all'.freeze
36
+
37
+ # A Time instance for the start time, never nil
38
+ attr_accessor :start_time
39
+
40
+
41
+ # A Time instance used for calculating the apdex score, which
42
+ # might end up being @start, or it might be further upstream if
43
+ # we can find a request header for the queue entry time
44
+ attr_accessor :apdex_start,
45
+ :category,
46
+ :frame_stack,
47
+ :exceptions,
48
+ :filtered_params,
49
+ :default_name,
50
+ :metrics,
51
+ :http_response_code,
52
+ :response_content_type,
53
+ :error_recorded,
54
+ :guid,
55
+ :attributes
56
+
57
+
58
+ def initialize(category, options)
59
+ @guid = options[:client_transaction_id] || generate_guid
60
+ @has_children = false
61
+ @category = category
62
+ @exceptions = {}
63
+ @start_time = Time.now
64
+ @apdex_start = options[:apdex_start_time] || @start_time
65
+ @frame_stack = []
66
+ @filtered_params = options[:filtered_params] || {}
67
+ @frozen_name = nil
68
+ @default_name = TingYun::Helper.correctly_encoded(options[:transaction_name])
69
+ @metrics = TingYun::Agent::TransactionMetrics.new
70
+
71
+ @error_recorded = false
72
+
73
+ @attributes = TingYun::Agent::Transaction::Attributes.new
74
+
75
+ if request = options[:request]
76
+ @request_attributes = TingYun::Agent::Transaction::RequestAttributes.new request
77
+ else
78
+ @request_attributes = nil
79
+ end
80
+ end
81
+
82
+
83
+ def request_path
84
+ @request_attributes && @request_attributes.request_path
85
+ end
86
+
87
+ def request_port
88
+ @request_attributes && @request_attributes.port
89
+ end
90
+
91
+ def self.wrap(state, name, category, options = {})
92
+ Transaction.start(state, category, options.merge(:transaction_name => name))
93
+
94
+ begin
95
+ # We shouldn't raise from Transaction.start, but only wrap the yield
96
+ # to be absolutely sure we don't report agent problems as app errors
97
+ yield
98
+ rescue => e
99
+ Transaction.notice_error(e)
100
+ raise e
101
+ ensure
102
+ Transaction.stop(state)
103
+ end
104
+ end
105
+
106
+
107
+ def self.start(state, category, options)
108
+ category ||= :controller
109
+ txn = state.current_transaction
110
+ options[:client_transaction_id] = state.client_transaction_id
111
+ if txn && options[:transaction_name]
112
+ txn.create_nested_frame(state, category, options)
113
+ else
114
+ txn = start_new_transaction(state, category, options)
115
+ end
116
+
117
+ txn
118
+ rescue => e
119
+ TingYun::Agent.logger.error("Exception during Transaction.start", e)
120
+ end
121
+
122
+ def self.start_new_transaction(state, category, options)
123
+ txn = Transaction.new(category, options)
124
+ state.reset(txn)
125
+ txn.start(state)
126
+ txn
127
+ end
128
+
129
+ def start(state)
130
+ return if !state.execution_traced?
131
+
132
+ transaction_sampler.on_start_transaction(state, start_time)
133
+ sql_sampler.on_start_transaction(state, request_path)
134
+ TingYun::Agent.instance.events.notify(:start_transaction)
135
+ frame_stack.push TingYun::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, Time.now.to_f)
136
+ name_last_frame @default_name
137
+ end
138
+
139
+ def create_nested_frame(state, category, options)
140
+ @has_children = true
141
+ frame_stack.push TingYun::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, Time.now.to_f)
142
+ name_last_frame(options[:transaction_name])
143
+
144
+ set_default_transaction_name(options[:transaction_name], category)
145
+ end
146
+
147
+
148
+ def set_default_transaction_name(name, category)
149
+ return log_frozen_name(name) if name_frozen?
150
+ if influences_transaction_name?(category)
151
+ self.default_name = name
152
+ @category = category if category
153
+ end
154
+ end
155
+
156
+ def self.stop(state, end_time = Time.now)
157
+
158
+ txn = state.current_transaction
159
+
160
+ if txn.nil?
161
+ TingYun::Agent.logger.error("Failed during Transaction.stop because there is no current transaction")
162
+ return
163
+ end
164
+
165
+ nested_frame = txn.frame_stack.pop
166
+
167
+ if txn.frame_stack.empty?
168
+ txn.stop(state, end_time, nested_frame)
169
+ state.reset
170
+ else
171
+ nested_name = nested_transaction_name(nested_frame.name)
172
+
173
+ if nested_name.start_with?(MIDDLEWARE_PREFIX)
174
+ summary_metrics = MIDDLEWARE_SUMMARY_METRICS
175
+ else
176
+ summary_metrics = EMPTY_SUMMARY_METRICS
177
+ end
178
+
179
+ TingYun::Agent::MethodTracerHelpers.trace_execution_scoped_footer(
180
+ state,
181
+ nested_frame.start_time.to_f,
182
+ nested_name,
183
+ summary_metrics,
184
+ nested_frame,
185
+ NESTED_TRACE_STOP_OPTIONS,
186
+ end_time.to_f)
187
+
188
+ end
189
+
190
+ :transaction_stopped
191
+ rescue => e
192
+ state.reset
193
+ TingYun::Agent.logger.error("Exception during Transaction.stop", e)
194
+ nil
195
+ end
196
+
197
+
198
+ def stop(state, end_time, outermost_frame)
199
+
200
+ freeze_name_and_execute
201
+
202
+ if @has_children
203
+ name = Transaction.nested_transaction_name(outermost_frame.name)
204
+ trace_options = TRACE_OPTIONS_SCOPED
205
+ else
206
+ name = @frozen_name
207
+ trace_options = TRACE_OPTIONS_UNSCOPED
208
+ end
209
+
210
+ if needs_middleware_summary_metrics?(name)
211
+ summary_metrics_with_exclusive_time = MIDDLEWARE_SUMMARY_METRICS
212
+ else
213
+ summary_metrics_with_exclusive_time = EMPTY_SUMMARY_METRICS
214
+ end
215
+
216
+
217
+ TingYun::Agent::MethodTracerHelpers.trace_execution_scoped_footer(
218
+ state,
219
+ start_time.to_f,
220
+ name,
221
+ summary_metrics_with_exclusive_time,
222
+ outermost_frame,
223
+ trace_options,
224
+ end_time.to_f)
225
+
226
+ commit!(state, end_time, name) unless ignore(best_name)
227
+ end
228
+
229
+ def self.nested_transaction_name(name)
230
+ if name.start_with?(CONTROLLER_PREFIX)
231
+ "#{SUBTRANSACTION_PREFIX}#{name}"
232
+ else
233
+ name
234
+ end
235
+ end
236
+
237
+ def commit!(state, end_time, outermost_node_name)
238
+ assign_agent_attributes
239
+
240
+
241
+ transaction_sampler.on_finishing_transaction(state, self, end_time)
242
+
243
+ sql_sampler.on_finishing_transaction(state, @frozen_name)
244
+
245
+
246
+ record_summary_metrics(outermost_node_name, end_time)
247
+ record_apdex(state, end_time)
248
+ record_exceptions
249
+ merge_metrics
250
+ end
251
+
252
+ def assign_agent_attributes
253
+
254
+ add_agent_attribute(:threadName, "pid-#{$$}");
255
+
256
+ if http_response_code
257
+ add_agent_attribute(:httpStatus, http_response_code.to_s)
258
+ end
259
+
260
+ if response_content_type
261
+ add_agent_attribute(:contentType, response_content_type)
262
+ end
263
+
264
+
265
+ if @request_attributes
266
+ @request_attributes.assign_agent_attributes self
267
+ end
268
+
269
+ end
270
+
271
+ def add_agent_attribute(key, value)
272
+ @attributes.add_agent_attribute(key, value)
273
+ end
274
+
275
+ #collector error
276
+ def had_error?
277
+ if @exceptions.empty?
278
+ return false
279
+ else
280
+ return true
281
+ end
282
+ end
283
+
284
+ def record_exceptions
285
+ unless @exceptions.empty?
286
+ @exceptions.each do |exception, options|
287
+
288
+ options[:uri] ||= request_path if request_path
289
+ options[:port] = request_port if request_port
290
+ options[:metric_name] = best_name
291
+ options[:attributes] = @attributes
292
+
293
+ @error_recorded = !!::TingYun::Agent.instance.error_collector.notice_error(exception, options) || @error_recorded
294
+ end
295
+ end
296
+ end
297
+
298
+ def record_apdex(state, end_time=Time.now)
299
+ total_duration = (end_time - apdex_start)*1000
300
+ if recording_web_transaction?
301
+ record_apdex_metrics(APDEX_TXN_METRIC_PREFIX, total_duration, apdex_t)
302
+ end
303
+ end
304
+
305
+
306
+ def record_apdex_metrics(transaction_prefix, total_duration, current_apdex_t)
307
+ return unless current_apdex_t
308
+ return unless @frozen_name.start_with?(CONTROLLER_PREFIX)
309
+
310
+ apdex_bucket_global = apdex_bucket(total_duration, current_apdex_t)
311
+ txn_apdex_metric = @frozen_name.sub(/^[^\/]+\//, transaction_prefix)
312
+ @metrics.record_unscoped(txn_apdex_metric, apdex_bucket_global, current_apdex_t)
313
+ end
314
+
315
+
316
+ def apdex_bucket(duration, current_apdex_t)
317
+ self.class.apdex_bucket(duration, had_error?, current_apdex_t)
318
+ end
319
+
320
+ def self.apdex_bucket(duration, failed, apdex_t)
321
+ case
322
+ when failed
323
+ :apdex_f
324
+ when duration <= apdex_t
325
+ :apdex_s
326
+ when duration <= 4 * apdex_t
327
+ :apdex_t
328
+ else
329
+ :apdex_f
330
+ end
331
+ end
332
+
333
+ def apdex_t
334
+ TingYun::Agent.config[:apdex_t]
335
+ end
336
+
337
+
338
+ # See TingYun::Agent.notice_error for options and commentary
339
+ def self.notice_error(e, options={})
340
+ state = TingYun::Agent::TransactionState.tl_get
341
+ txn = state.current_transaction
342
+ if txn
343
+ txn.notice_error(e, options)
344
+ elsif TingYun::Agent.instance
345
+ TingYun::Agent.instance.error_collector.notice_error(e, options)
346
+ end
347
+ end
348
+
349
+ # Do not call this. Invoke the class method instead.
350
+ def notice_error(error, options={}) # :nodoc:
351
+ if @exceptions[error]
352
+ @exceptions[error].merge! options
353
+ else
354
+ @exceptions[error] = options
355
+ end
356
+ end
357
+
358
+
359
+ # This transaction-local hash may be used as temprory storage by
360
+ # instrumentation that needs to pass data from one instrumentation point
361
+ # to another.
362
+ #
363
+ # For example, if both A and B are instrumented, and A calls B
364
+ # but some piece of state needed by the instrumentation at B is only
365
+ # available at A, the instrumentation at A may write into the hash, call
366
+ # through, and then remove the key afterwards, allowing the
367
+ # instrumentation at B to read the value in between.
368
+ #
369
+ # Keys should be symbols, and care should be taken to not generate key
370
+ # names dynamically, and to ensure that keys are removed upon return from
371
+ # the method that creates them.
372
+ #
373
+ def instrumentation_state
374
+ @instrumentation_state ||= {}
375
+ end
376
+
377
+ def with_database_metric_name(model, method, product=nil)
378
+ previous = self.instrumentation_state[:datastore_override]
379
+ model_name = case model
380
+ when Class
381
+ model.name
382
+ when String
383
+ model
384
+ else
385
+ model.to_s
386
+ end
387
+ self.instrumentation_state[:datastore_override] = [method, model_name, product]
388
+ yield
389
+ ensure
390
+ self.instrumentation_state[:datastore_override] = previous
391
+ end
392
+
393
+ def freeze_name_and_execute
394
+ if !name_frozen?
395
+ @name_frozen = true
396
+ @frozen_name = best_name
397
+ end
398
+
399
+ yield if block_given?
400
+ end
401
+
402
+ def promoted_transaction_name(name)
403
+ if name.start_with?(MIDDLEWARE_PREFIX)
404
+ "#{CONTROLLER_PREFIX}#{name}"
405
+ else
406
+ name
407
+ end
408
+ end
409
+
410
+ def merge_metrics
411
+ TingYun::Agent.instance.stats_engine.merge_transaction_metrics!(@metrics, best_name)
412
+ end
413
+
414
+
415
+ def summary_metrics
416
+ if @frozen_name.start_with?(CONTROLLER_PREFIX)
417
+ [WEB_SUMMARY_METRIC]
418
+ else
419
+ background_summary_metrics
420
+ end
421
+ end
422
+
423
+ def background_summary_metrics
424
+ segments = @frozen_name.split('/')
425
+ if segments.size > 2
426
+ ["OtherTransaction/#{segments[1]}/all", OTHER_SUMMARY_METRIC]
427
+ else
428
+ []
429
+ end
430
+ end
431
+
432
+
433
+ # The summary metrics recorded by this method all end up with a duration
434
+ # equal to the transaction itself, and an exclusive time of zero.
435
+ def record_summary_metrics(outermost_node_name, end_time)
436
+ metrics = summary_metrics
437
+ metrics << @frozen_name unless @frozen_name == outermost_node_name
438
+ @metrics.record_unscoped(metrics, (end_time.to_f - start_time.to_f)*1000)
439
+ end
440
+
441
+ def name_last_frame(name)
442
+ frame_stack.last.name = name
443
+ end
444
+
445
+ def name_frozen?
446
+ @frozen_name ? true : false
447
+ end
448
+
449
+ def log_frozen_name(name)
450
+ TingYun::Agent.logger.warn("Attempted to rename transaction to '#{name}' after transaction name was already frozen as '#{@frozen_name}'.")
451
+ nil
452
+ end
453
+
454
+ def influences_transaction_name?(category)
455
+ !category || frame_stack.size == 1 || similar_category?(category)
456
+ end
457
+
458
+ def web_category?(category)
459
+ WEB_TRANSACTION_CATEGORIES.include?(category)
460
+ end
461
+
462
+ def similar_category?(category)
463
+ web_category?(@category) == web_category?(category)
464
+ end
465
+
466
+ def recording_web_transaction?
467
+ web_category?(@category)
468
+ end
469
+
470
+ def self.recording_web_transaction? #THREAD_LOCAL_ACCESS
471
+ txn = tl_current
472
+ txn && txn.recording_web_transaction?
473
+ end
474
+
475
+ def self.tl_current
476
+ TingYun::Agent::TransactionState.tl_get.current_transaction
477
+ end
478
+
479
+ def needs_middleware_summary_metrics?(name)
480
+ name.start_with?(MIDDLEWARE_PREFIX)
481
+ end
482
+
483
+ alias_method :ignore, :needs_middleware_summary_metrics?
484
+
485
+ def best_name
486
+ @frozen_name || @default_name || ::TingYun::Agent::UNKNOWN_METRIC
487
+ end
488
+
489
+ def queue_time
490
+ @apdex_start ? @start_time - @apdex_start : 0
491
+ end
492
+
493
+
494
+ def agent
495
+ TingYun::Agent.instance
496
+ end
497
+
498
+ def sql_sampler
499
+ agent.sql_sampler
500
+ end
501
+
502
+
503
+ def transaction_sampler
504
+ TingYun::Agent.instance.transaction_sampler
505
+ end
506
+
507
+ HEX_DIGITS = (0..15).map{|i| i.to_s(16)}
508
+ GUID_LENGTH = 16
509
+
510
+ # generate a random 64 bit uuid
511
+ private
512
+ def generate_guid
513
+ guid = ''
514
+ GUID_LENGTH.times do
515
+ guid << HEX_DIGITS[rand(16)]
516
+ end
517
+ guid
518
+ end
519
+
520
+ end
521
+ end
522
+ end