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,226 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent/transaction/transaction_sample_builder'
4
+ require 'ting_yun/agent/collector/transaction_sampler/slowest_sample_buffer'
5
+
6
+ require 'ting_yun/agent/transaction/transaction_state'
7
+
8
+
9
+ module TingYun
10
+ module Agent
11
+ module Collector
12
+ class TransactionSampler
13
+
14
+
15
+ attr_accessor :last_sample
16
+
17
+
18
+ def initialize
19
+ @lock = Mutex.new
20
+ @sample_buffers = []
21
+ @sample_buffers << TingYun::Agent::Collector::TransactionSampler::SlowestSampleBuffer.new
22
+ end
23
+
24
+
25
+ def notice_push_frame(state, time=Time.now)
26
+ builder = state.transaction_sample_builder
27
+ return unless builder
28
+ builder.trace_entry(time.to_f)
29
+ end
30
+
31
+ # Informs the transaction sample builder about the end of a traced frame
32
+ def notice_pop_frame(state, frame, time = Time.now)
33
+ builder = state.transaction_sample_builder
34
+ return unless builder
35
+ builder.trace_exit(frame, time.to_f)
36
+ end
37
+
38
+ def enabled?
39
+ TingYun::Agent.config[:'nbs.action_tracer.enabled']
40
+ end
41
+
42
+ def on_start_transaction(state, time)
43
+ start_builder(state, time)
44
+ end
45
+
46
+ def start_builder(state, time=nil)
47
+ if enabled?
48
+ state.transaction_sample_builder ||= TingYun::Agent::TransactionSampleBuilder.new(time)
49
+ else
50
+ state.transaction_sample_builder = nil
51
+ end
52
+ end
53
+
54
+
55
+ def on_finishing_transaction(state, txn, time=Time.now.to_f)
56
+ last_builder = state.transaction_sample_builder
57
+ return unless last_builder && enabled?
58
+
59
+ last_builder.finish_trace(time)
60
+
61
+ state.transaction_sample_builder = nil
62
+
63
+
64
+ final_trace = last_builder.trace
65
+ final_trace.metric_name = txn.best_name
66
+ final_trace.uri = txn.request_path
67
+ final_trace.attributes = txn.attributes
68
+
69
+
70
+ @lock.synchronize do
71
+ @last_sample = final_trace
72
+
73
+ store_sample(@last_sample)
74
+ @last_sample
75
+ end
76
+ end
77
+
78
+ def store_sample(sample)
79
+ @sample_buffers.each do |sample_buffer|
80
+ sample_buffer.store(sample)
81
+ end
82
+ end
83
+
84
+
85
+ # Attaches an SQL query on the current transaction trace node.
86
+ # @param sql [String] the SQL query being recorded
87
+ # @param config [Object] the driver configuration for the connection
88
+ # @param duration [Float] number of seconds the query took to execute
89
+ # @param explainer [Proc] for internal use only - 3rd-party clients must
90
+ # not pass this parameter.
91
+ # duration{:type => sec}
92
+ def notice_sql(sql, config, duration, state=nil, explainer=nil)
93
+ # some statements (particularly INSERTS with large BLOBS
94
+ # may be very large; we should trim them to a maximum usable length
95
+ state ||= TingYun::Agent::TransactionState.tl_get
96
+ builder = state.transaction_sample_builder
97
+ if state.sql_recorded?
98
+ statement = TingYun::Agent::Database::Statement.new(sql, config, explainer)
99
+ action_tracer_segment(builder, statement, duration, :sql)
100
+ end
101
+ end
102
+
103
+ # duration{:type => sec}
104
+ def notice_nosql(key, duration) #THREAD_LOCAL_ACCESS
105
+ builder = tl_builder
106
+ return unless builder
107
+ action_tracer_segment(builder, key, duration, :key)
108
+ end
109
+
110
+ # duration{:type => sec}
111
+ def notice_nosql_statement(statement, duration) #THREAD_LOCAL_ACCESS
112
+ builder = tl_builder
113
+ return unless builder
114
+ action_tracer_segment(builder, statement, duration, :statement)
115
+ end
116
+
117
+
118
+
119
+ MAX_DATA_LENGTH = 16384
120
+ # This method is used to record metadata into the currently
121
+ # active node like a sql query, memcache key, or Net::HTTP uri
122
+ #
123
+ # duration is milliseconds, float value.
124
+ # duration{:type => sec}
125
+ def action_tracer_segment(builder, message, duration, key)
126
+ return unless builder
127
+ node = builder.current_node
128
+ if node
129
+ if key == :sql
130
+ statement = node[:sql]
131
+ if statement && !statement.sql.empty?
132
+ statement.sql = self.class.truncate_message(statement.sql + "\n#{message.sql}") if statement.sql.length <= MAX_DATA_LENGTH
133
+ else
134
+ # message is expected to have been pre-truncated by notice_sql
135
+ node[:sql] = message
136
+ end
137
+ else
138
+ node[key] = self.class.truncate_message(message)
139
+ end
140
+ append_backtrace(node, duration)
141
+ end
142
+ end
143
+
144
+ # Truncates the message to `MAX_DATA_LENGTH` if needed, and
145
+ # appends an ellipsis because it makes the trucation clearer in
146
+ # the UI
147
+ def self.truncate_message(message)
148
+ if message.length > (MAX_DATA_LENGTH - 4)
149
+ message.slice!(MAX_DATA_LENGTH - 4..message.length)
150
+ message << '...'
151
+ else
152
+ message
153
+ end
154
+ end
155
+
156
+ # Appends a backtrace to a node if that node took longer
157
+ # than the specified duration
158
+ def append_backtrace(node, duration)
159
+ if duration*1000 >= Agent.config[:'nbs.action_tracer.stack_trace_threshold']
160
+ node[:stacktrace] = caller.reject! { |t| t.include?('tingyun_rpm') }
161
+ end
162
+
163
+ end
164
+
165
+ def harvest!
166
+ return [] unless enabled?
167
+
168
+ samples = @lock.synchronize do
169
+ @last_sample = nil
170
+ harvest_from_sample_buffers
171
+ end
172
+
173
+ prepare_samples(samples)
174
+ end
175
+
176
+ def harvest_from_sample_buffers
177
+ result = []
178
+ @sample_buffers.each { |buffer| result.concat(buffer.harvest_samples) }
179
+ result.uniq
180
+ end
181
+
182
+ def prepare_samples(samples)
183
+ samples.select do |sample|
184
+ begin
185
+ sample.prepare_to_send!
186
+ rescue => e
187
+
188
+ TingYun::Agent.logger.error('Failed to prepare transaction trace. Error: ', e)
189
+
190
+ false
191
+ else
192
+ true
193
+ end
194
+ end
195
+ end
196
+
197
+ def merge!
198
+
199
+ @lock.synchronize do
200
+ @sample_buffers.each do |buffer|
201
+ buffer.store_previous(previous)
202
+ end
203
+ end
204
+ end
205
+
206
+ def reset!
207
+ @lock.synchronize do
208
+ @sample_buffers.each { |sample| sample.reset! }
209
+ end
210
+ end
211
+
212
+
213
+ def add_node_info(params)
214
+ builder = tl_builder
215
+ return unless builder
216
+ params.each { |k,v| builder.current_node.instance_variable_set(('@'<<k.to_s).to_sym, v) }
217
+ end
218
+
219
+ def tl_builder
220
+ TingYun::Agent::TransactionState.tl_get.transaction_sample_builder
221
+ end
222
+
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+ require 'ting_yun/support/exception'
3
+ require 'ting_yun/agent/collector/stats_engine'
4
+ require 'ting_yun/agent/collector/error_collector'
5
+
6
+ module TingYun
7
+ module Agent
8
+ module ContainerDataManager
9
+
10
+ attr_reader :stats_engine, :error_collector, :transaction_sampler
11
+
12
+ def drop_buffered_data
13
+ @stats_engine.reset!
14
+ end
15
+
16
+ # private
17
+
18
+ def init_containers
19
+ @stats_engine = TingYun::Agent::Collector::StatsEngine.new
20
+ @error_collector = TingYun::Agent::Collector::ErrorCollector.new
21
+ end
22
+
23
+ def container_for_endpoint(endpoint)
24
+ case endpoint
25
+ when :metric_data then @stats_engine
26
+ # type code here
27
+ end
28
+ end
29
+
30
+ def transmit_data
31
+ ::TingYun::Agent.logger.debug('Sending data to Ting Yun Service')
32
+
33
+ @service.session do # use http keep-alive
34
+ harvest_and_send_errors
35
+ harvest_and_send_timeslice_data
36
+ end
37
+ end
38
+
39
+ def harvest_and_send_timeslice_data
40
+ harvest_and_send_from_container(@stats_engine, :metric_data)
41
+ end
42
+
43
+ def harvest_and_send_errors
44
+ harvest_and_send_from_container(@error_collector.error_trace_array, :error_data)
45
+ end
46
+
47
+ # Harvests data from the given container, sends it to the named endpoint
48
+ # on the service, and automatically merges back in upon a recoverable
49
+ # failure.
50
+ #
51
+ # The given container should respond to:
52
+ #
53
+ # #harvest!
54
+ # returns an enumerable collection of data items to be sent to the
55
+ # collector.
56
+ #
57
+ # #reset!
58
+ # drop any stored data and reset to a clean state.
59
+ #
60
+ # #merge!(items)
61
+ # merge the given items back into the internal buffer of the
62
+ # container, so that they may be harvested again later.
63
+ #
64
+ def harvest_and_send_from_container(container,endpoint)
65
+ items = harvest_from_container(container, endpoint)
66
+ if !items.empty? && TingYun::Agent.config[:'nbs.agent_enabled']
67
+ send_data_to_endpoint(endpoint, items, container)
68
+ end
69
+ end
70
+
71
+ def harvest_from_container(container, endpoint)
72
+ items =[]
73
+ begin
74
+ items = container.harvest!
75
+ rescue => e
76
+ TingYun::Agent.logger.error("Failed to harvest #{endpoint} data, resetting. Error: ", e)
77
+ container.reset!
78
+ end
79
+ items
80
+ end
81
+
82
+ def send_data_to_endpoint(endpoint, items, container)
83
+ TingYun::Agent.logger.debug("Sending #{items.size} items to #{endpoint}")
84
+ begin
85
+ @service.send(endpoint, items)
86
+ rescue => e
87
+ TingYun::Agent.logger.info("Unable to send #{endpoint} data, will try again later. Error: ", e)
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,131 @@
1
+ # encoding: utf-8
2
+ require 'ting_yun/agent/cross_app/inbound_request_monitor'
3
+ require 'ting_yun/agent/cross_app/cross_app_tracing'
4
+ require 'ting_yun/agent/transaction/transaction_state'
5
+ require 'ting_yun/agent'
6
+ require 'ting_yun/support/serialize/json_wrapper'
7
+
8
+
9
+ module TingYun
10
+ module Agent
11
+ class CrossAppMonitor < TingYun::Agent::InboundRequestMonitor
12
+
13
+
14
+ TY_ID_HEADER = 'HTTP_X_TINGYUN_ID'.freeze
15
+ TY_DATA_HEADER = 'X-Tingyun-Tx-Data'.freeze
16
+
17
+
18
+ def on_finished_configuring(events)
19
+ register_event_listeners(events)
20
+ end
21
+
22
+ # Expected sequence of events:
23
+ # :before_call will save our cross application request id to the thread
24
+ # :after_call will write our response headers/metrics and clean up the thread
25
+ def register_event_listeners(events)
26
+ TingYun::Agent.logger.debug("Wiring up Cross Application Tracing to events after finished configuring")
27
+
28
+ events.subscribe(:before_call) do |env| #THREAD_LOCAL_ACCESS
29
+ if cross_app_enabled?
30
+ state = TingYun::Agent::TransactionState.tl_get
31
+ save_referring_transaction_info(state, env) unless env[TY_ID_HEADER].nil?
32
+ end
33
+ end
34
+
35
+ events.subscribe(:after_call) do |_status_code, headers, _body| #THREAD_LOCAL_ACCESS
36
+ state = TingYun::Agent::TransactionState.tl_get
37
+ state.queue_duration = state.current_transaction.queue_time * 1000
38
+ state.web_duration = (Time.now - state.current_transaction.start_time) * 1000
39
+ insert_response_header(state, headers)
40
+ end
41
+
42
+ end
43
+
44
+
45
+ def cross_app_enabled?
46
+ TingYun::Agent::CrossAppTracing.cross_app_enabled?
47
+ end
48
+
49
+
50
+ def save_referring_transaction_info(state,request)
51
+
52
+ info = request[TY_ID_HEADER].split(';')
53
+ tingyun_id_secret = info[0]
54
+ client_transaction_id = info.find do |e|
55
+ e.match(/x=/)
56
+ end.split('=')[1] rescue nil
57
+ client_req_id = info.find do |e|
58
+ e.match(/r=/)
59
+ end.split('=')[1] rescue nil
60
+
61
+ state.client_tingyun_id_secret = tingyun_id_secret
62
+ state.client_transaction_id = client_transaction_id
63
+ state.transaction_sample_builder.trace.tx_id = client_transaction_id
64
+ state.client_req_id = client_req_id
65
+ end
66
+
67
+
68
+ def insert_response_header(state, response_headers)
69
+ if same_account?(state)
70
+ txn = state.current_transaction
71
+ unless txn.nil?
72
+ set_response_headers(state, response_headers)
73
+ end
74
+ clear_client_tingyun_id_secret(state)
75
+ end
76
+ end
77
+
78
+ def clear_client_tingyun_id_secret(state)
79
+ state.client_tingyun_id_secret = nil
80
+ end
81
+
82
+ def same_account?(state)
83
+ server_info = TingYun::Agent.config[:tingyunIdSecret].split('|')
84
+ client_info = (state.client_tingyun_id_secret || '').split('|')
85
+ if !server_info[0].nil? && server_info[0] == client_info[0] && !server_info[0].empty?
86
+ return true
87
+ else
88
+ return false
89
+ end
90
+ end
91
+
92
+ def set_response_headers(state, response_headers)
93
+ response_headers[TY_DATA_HEADER] = TingYun::Support::Serialize::JSONWrapper.dump build_payload(state)
94
+ TingYun::Agent.logger.debug("now,cross app will send response_headers #{response_headers[TY_DATA_HEADER]}")
95
+ end
96
+
97
+ def build_payload(state)
98
+ payload = {
99
+ :id => TingYun::Agent.config[:tingyunIdSecret].split('|')[1],
100
+ :action => state.current_transaction.best_name,
101
+ :trId => state.transaction_sample_builder.trace.guid,
102
+ :time => {
103
+ :duration => state.web_duration,
104
+ :qu => state.queue_duration,
105
+ :db => state.sql_duration,
106
+ :ex => state.external_duration,
107
+ :rds => state.rds_duration,
108
+ :mc => state.mc_duration,
109
+ :mon => state.mon_duration,
110
+ :code => execute_duration(state)
111
+ }
112
+ }
113
+ payload[:tr] = 1 if slow_action_tracer?(state)
114
+ payload[:r] = state.client_req_id unless state.client_req_id.nil?
115
+ payload
116
+ end
117
+
118
+ def slow_action_tracer?(state)
119
+ if state.web_duration > TingYun::Agent.config[:'nbs.action_tracer.action_threshold']
120
+ return true
121
+ else
122
+ return false
123
+ end
124
+ end
125
+
126
+ def execute_duration(state)
127
+ state.web_duration - state.queue_duration - state.sql_duration - state.external_duration - state.rds_duration - state.mc_duration - state.mon_duration
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,202 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ting_yun/agent/transaction/transaction_state'
4
+ require 'ting_yun/agent/transaction'
5
+ require 'ting_yun/support/http_clients/uri_util'
6
+ require 'ting_yun/support/serialize/json_wrapper'
7
+ require 'ting_yun/instrumentation/support/external_error'
8
+
9
+
10
+ module TingYun
11
+ module Agent
12
+ module CrossAppTracing
13
+
14
+ extend ::TingYun::Instrumentation::Support::ExternalError
15
+
16
+ # Exception raised if there is a problem with cross app transactions.
17
+ class Error < RuntimeError; end
18
+
19
+ # The cross app id header for "outgoing" calls
20
+
21
+ TY_ID_HEADER = 'X-Tingyun-Id'.freeze
22
+ TY_DATA_HEADER = 'X-Tingyun-Tx-Data'.freeze
23
+
24
+
25
+
26
+
27
+ module_function
28
+
29
+
30
+
31
+ def tl_trace_http_request(request)
32
+ t0 = Time.now.to_f
33
+ state = TingYun::Agent::TransactionState.tl_get
34
+ return yield unless state.execution_traced?
35
+
36
+ begin
37
+ node = start_trace(state, t0, request)
38
+ response = yield
39
+ capture_exception(response,request,'net%2Fhttp')
40
+ rescue => e
41
+ klass = "External/#{request.uri.to_s.gsub('/','%2F')}/net%2Fhttp"
42
+ handle_error(e,klass)
43
+ ensure
44
+ finish_trace(state, t0, node, request, response)
45
+ end
46
+ return response
47
+ end
48
+
49
+ def start_trace(state, t0, request)
50
+ # inject_request_headers(state, request) if cross_app_enabled?
51
+ stack = state.traced_method_stack
52
+ node = stack.push_frame(state,:http_request,t0)
53
+
54
+ return node
55
+ end
56
+
57
+ def finish_trace(state, t0, node, request, response)
58
+ t1 = Time.now.to_f
59
+ duration = (t1- t0) * 1000
60
+ state.external_duration = duration
61
+
62
+ begin
63
+ if request
64
+ metrics = metrics_for(state, request, response)
65
+ node_name = metrics.pop
66
+ scoped_metric = metrics.pop
67
+
68
+ stats_engine.record_scoped_and_unscoped_metrics(state, scoped_metric, metrics, duration)
69
+
70
+ if node
71
+ node.name = node_name
72
+ add_transaction_trace_info(request, response)
73
+ end
74
+ end
75
+ ensure
76
+ if node
77
+ stack = state.traced_method_stack
78
+ stack.pop_frame(state, node, node_name, t1)
79
+ end
80
+ end
81
+ rescue => err
82
+ TingYun::Agent.logger.error "Uncaught exception while finishing an HTTP request trace", err
83
+
84
+ end
85
+
86
+
87
+
88
+ def add_transaction_trace_info(request, response)
89
+ state = TingYun::Agent::TransactionState.tl_get
90
+ filtered_uri = TingYun::Agent::HTTPClients::URIUtil.filter_uri request.uri
91
+ transaction_sampler.add_node_info(:uri => filtered_uri)
92
+ if response && response_is_cross_app?( response )
93
+ transaction_sampler.tl_builder.current_node[:txId] = state.client_transaction_id || state.request_guid
94
+ my_data = TingYun::Support::Serialize::JSONWrapper.load response[TY_DATA_HEADER].gsub("'",'"')
95
+ transaction_sampler.tl_builder.current_node[:txData] = my_data
96
+ end
97
+ end
98
+
99
+ def metrics_for(state, request, response)
100
+ metrics = common_metrics(request)
101
+
102
+ if response && response_is_cross_app?( response )
103
+ begin
104
+ metrics.concat metrics_for_cross_app_response( request, response )
105
+ rescue => err
106
+ # Fall back to regular metrics if there's a problem with x-process metrics
107
+ TingYun::Agent.logger.debug "%p while fetching x-process metrics: %s" %
108
+ [ err.class, err.message ]
109
+ metrics.concat metrics_for_regular_request( request )
110
+ end
111
+ else
112
+ metrics.concat metrics_for_regular_request( request )
113
+ end
114
+
115
+ return metrics
116
+ end
117
+
118
+ def common_metrics(request)
119
+ metrics = [ "External/NULL/ALL" ]
120
+
121
+ if TingYun::Agent::Transaction.recording_web_transaction?
122
+ metrics << "External/NULL/AllWeb"
123
+ else
124
+ metrics << "External/NULL/AllBackground"
125
+ end
126
+
127
+ return metrics
128
+ end
129
+
130
+ def metrics_for_regular_request( request )
131
+ state = TingYun::Agent::TransactionState.tl_get
132
+ metrics = []
133
+ metrics << "External/#{request.uri.to_s.gsub('/','%2F')}/net%2Fhttp"
134
+ metrics << "External/#{request.uri.to_s.gsub('/','%2F')}/net%2Fhttp"
135
+
136
+ return metrics
137
+ end
138
+
139
+ def stats_engine
140
+ ::TingYun::Agent.instance.stats_engine
141
+ end
142
+
143
+ def transaction_sampler
144
+ ::TingYun::Agent.instance.transaction_sampler
145
+ end
146
+
147
+
148
+ def cross_app_enabled?
149
+ valid_tingyun_secret_id? && web_action_tracer_enabled? && cross_application_tracer_enabled?
150
+ end
151
+
152
+ def web_action_tracer_enabled?
153
+ TingYun::Agent.config[:'nbs.action_tracer.enabled']
154
+ end
155
+
156
+ def cross_application_tracer_enabled?
157
+ TingYun::Agent.config[:'nbs.transaction_tracer.enabled']
158
+ end
159
+
160
+ def valid_tingyun_secret_id?
161
+ TingYun::Agent.config[:tingyunIdSecret] && TingYun::Agent.config[:tingyunIdSecret].size > 0
162
+ end
163
+
164
+ # Inject the X-Process header into the outgoing +request+.
165
+ def inject_request_headers(state, request)
166
+ cross_app_id = TingYun::Agent.config[:tingyunIdSecret] or
167
+ raise TingYun::Agent::CrossAppTracing::Error, "no tingyunIdSecret configured"
168
+
169
+ txn_guid = state.client_transaction_id || state.request_guid
170
+ state.transaction_sample_builder.trace.tx_id = txn_guid
171
+ request[TY_ID_HEADER] = "#{cross_app_id};c=1;x=#{txn_guid}"
172
+
173
+ rescue TingYun::Agent::CrossAppTracing::Error => err
174
+ TingYun::Agent.logger.debug "Not injecting x-process header", err
175
+ end
176
+
177
+ # Returns +true+ if Cross Application Tracing is enabled, and the given +response+
178
+ # has the appropriate headers.
179
+ def response_is_cross_app?( response )
180
+ return false unless cross_app_enabled?
181
+ unless response[TY_DATA_HEADER]
182
+ return false
183
+ end
184
+ return true
185
+ end
186
+
187
+ # Return the set of metric objects appropriate for the given cross app
188
+ # +response+.
189
+ def metrics_for_cross_app_response(request, response )
190
+ state = TingYun::Agent::TransactionState.tl_get
191
+ my_data = TingYun::Support::Serialize::JSONWrapper.load response[TY_DATA_HEADER].gsub("'",'"')
192
+ uri = "#{request.uri.to_s.gsub('/','%2F')}/net%2Fhttp"
193
+ metrics = []
194
+ metrics << "cross_app;#{my_data["id"]};#{my_data["action"]};#{uri}"
195
+ metrics << "External/#{my_data["action"]}:#{uri}"
196
+
197
+ return metrics
198
+ end
199
+
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ # This class serves as the base for objects wanting to monitor and respond to
4
+ # incoming web requests. Examples include cross application tracing and
5
+ # synthetics.
6
+ #
7
+ # Subclasses are expected to define on_finished_configuring(events) which will
8
+ # be called when the agent is fully configured. That method is expected to
9
+ # subscribe to the necessary request events, such as before_call and after_call
10
+ # for the monitor to do its work.
11
+
12
+ module TingYun
13
+ module Agent
14
+ class InboundRequestMonitor
15
+ def initialize(events)
16
+ events.subscribe(:finished_configuring) do
17
+ on_finished_configuring(events)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end