oneapm_rpm 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +30 -0
  3. data/.rubocop.yml +725 -0
  4. data/Gemfile +3 -0
  5. data/Guardfile +7 -0
  6. data/LICENSE +1 -0
  7. data/README.md +3 -0
  8. data/config/cert/cacert.pem +1177 -0
  9. data/config/database.yml +5 -0
  10. data/lib/initializers/goliath.rb +11 -0
  11. data/lib/initializers/other.rb +1 -0
  12. data/lib/initializers/rails.rb +15 -0
  13. data/lib/one_apm/agent.rb +253 -0
  14. data/lib/one_apm/agent/agent.rb +283 -0
  15. data/lib/one_apm/agent/agent/connect.rb +175 -0
  16. data/lib/one_apm/agent/agent/container_data_manager.rb +218 -0
  17. data/lib/one_apm/agent/agent/forkable_dispatcher_functions.rb +96 -0
  18. data/lib/one_apm/agent/agent/helpers.rb +45 -0
  19. data/lib/one_apm/agent/agent/start.rb +226 -0
  20. data/lib/one_apm/agent/agent/start_worker_thread.rb +148 -0
  21. data/lib/one_apm/agent/busy_calculator.rb +115 -0
  22. data/lib/one_apm/agent/cross_app/cross_app_monitor.rb +181 -0
  23. data/lib/one_apm/agent/cross_app/cross_app_tracing.rb +336 -0
  24. data/lib/one_apm/agent/database.rb +308 -0
  25. data/lib/one_apm/agent/database/active_record_helper.rb +80 -0
  26. data/lib/one_apm/agent/database/obfuscation_helpers.rb +76 -0
  27. data/lib/one_apm/agent/database/obfuscator.rb +78 -0
  28. data/lib/one_apm/agent/database/postgres_explain_obfuscator.rb +45 -0
  29. data/lib/one_apm/agent/datastores.rb +175 -0
  30. data/lib/one_apm/agent/datastores/metric_helper.rb +83 -0
  31. data/lib/one_apm/agent/datastores/mongo.rb +27 -0
  32. data/lib/one_apm/agent/datastores/mongo/metric_translator.rb +189 -0
  33. data/lib/one_apm/agent/datastores/mongo/obfuscator.rb +37 -0
  34. data/lib/one_apm/agent/datastores/mongo/statement_formatter.rb +51 -0
  35. data/lib/one_apm/agent/event/event_listener.rb +40 -0
  36. data/lib/one_apm/agent/event/event_loop.rb +191 -0
  37. data/lib/one_apm/agent/event/worker_loop.rb +97 -0
  38. data/lib/one_apm/agent/harvester.rb +48 -0
  39. data/lib/one_apm/agent/inbound_request_monitor.rb +30 -0
  40. data/lib/one_apm/agent/javascript_instrumentor.rb +186 -0
  41. data/lib/one_apm/agent/pipe/pipe_channel_manager.rb +275 -0
  42. data/lib/one_apm/agent/pipe/pipe_service.rb +81 -0
  43. data/lib/one_apm/agent/sampler.rb +55 -0
  44. data/lib/one_apm/agent/sampler_collection.rb +65 -0
  45. data/lib/one_apm/agent/samplers/cpu_sampler.rb +49 -0
  46. data/lib/one_apm/agent/samplers/delayed_job_sampler.rb +109 -0
  47. data/lib/one_apm/agent/samplers/memory_sampler.rb +144 -0
  48. data/lib/one_apm/agent/samplers/object_sampler.rb +22 -0
  49. data/lib/one_apm/agent/samplers/vm_sampler.rb +124 -0
  50. data/lib/one_apm/agent/synthetics_monitor.rb +48 -0
  51. data/lib/one_apm/agent/threading/agent_thread.rb +74 -0
  52. data/lib/one_apm/agent/threading/backtrace_node.rb +133 -0
  53. data/lib/one_apm/agent/threading/backtrace_service.rb +259 -0
  54. data/lib/one_apm/agent/threading/thread_profile.rb +155 -0
  55. data/lib/one_apm/collector/collector/helper.rb +139 -0
  56. data/lib/one_apm/collector/collector/http_connection.rb +254 -0
  57. data/lib/one_apm/collector/collector/server_methods.rb +71 -0
  58. data/lib/one_apm/collector/collector_service.rb +123 -0
  59. data/lib/one_apm/collector/commands/agent_command.rb +17 -0
  60. data/lib/one_apm/collector/commands/thread_profiler_session.rb +108 -0
  61. data/lib/one_apm/collector/commands/xray_session.rb +53 -0
  62. data/lib/one_apm/collector/commands/xray_session_collection.rb +156 -0
  63. data/lib/one_apm/collector/containers/agent_command_router.rb +153 -0
  64. data/lib/one_apm/collector/containers/custom_event_aggregator.rb +94 -0
  65. data/lib/one_apm/collector/containers/error_collector.rb +349 -0
  66. data/lib/one_apm/collector/containers/sql_sampler.rb +331 -0
  67. data/lib/one_apm/collector/containers/stats_engine.rb +34 -0
  68. data/lib/one_apm/collector/containers/transaction_event_aggregator.rb +249 -0
  69. data/lib/one_apm/collector/containers/transaction_sampler.rb +352 -0
  70. data/lib/one_apm/collector/containers/utilization_data.rb +36 -0
  71. data/lib/one_apm/collector/stats_engine/gc_profiler.rb +106 -0
  72. data/lib/one_apm/collector/stats_engine/metric_stats.rb +243 -0
  73. data/lib/one_apm/collector/stats_engine/stats_hash.rb +105 -0
  74. data/lib/one_apm/configuration.rb +429 -0
  75. data/lib/one_apm/configuration/autostart.rb +41 -0
  76. data/lib/one_apm/configuration/default_source.rb +1026 -0
  77. data/lib/one_apm/configuration/environment_source.rb +113 -0
  78. data/lib/one_apm/configuration/high_security_source.rb +56 -0
  79. data/lib/one_apm/configuration/manual_source.rb +13 -0
  80. data/lib/one_apm/configuration/server_source.rb +60 -0
  81. data/lib/one_apm/configuration/yaml_source.rb +134 -0
  82. data/lib/one_apm/errors/agent_errors.rb +26 -0
  83. data/lib/one_apm/errors/internal_agent_error.rb +16 -0
  84. data/lib/one_apm/errors/noticed_error.rb +79 -0
  85. data/lib/one_apm/frameworks/external.rb +15 -0
  86. data/lib/one_apm/frameworks/rails.rb +103 -0
  87. data/lib/one_apm/frameworks/rails3.rb +37 -0
  88. data/lib/one_apm/frameworks/rails4.rb +21 -0
  89. data/lib/one_apm/frameworks/ruby.rb +21 -0
  90. data/lib/one_apm/frameworks/sinatra.rb +12 -0
  91. data/lib/one_apm/inst/3rd/active_merchant.rb +35 -0
  92. data/lib/one_apm/inst/3rd/acts_as_solr.rb +70 -0
  93. data/lib/one_apm/inst/3rd/authlogic.rb +23 -0
  94. data/lib/one_apm/inst/3rd/sunspot.rb +31 -0
  95. data/lib/one_apm/inst/background_job/active_job.rb +88 -0
  96. data/lib/one_apm/inst/background_job/delayed_job.rb +52 -0
  97. data/lib/one_apm/inst/background_job/delayed_job_injection.rb +8 -0
  98. data/lib/one_apm/inst/background_job/resque.rb +107 -0
  99. data/lib/one_apm/inst/background_job/sidekiq.rb +64 -0
  100. data/lib/one_apm/inst/dispatcher/passenger.rb +25 -0
  101. data/lib/one_apm/inst/dispatcher/rainbows.rb +23 -0
  102. data/lib/one_apm/inst/framework/grape.rb +94 -0
  103. data/lib/one_apm/inst/framework/padrino.rb +30 -0
  104. data/lib/one_apm/inst/framework/sinatra.rb +185 -0
  105. data/lib/one_apm/inst/framework/sinatra/ignorer.rb +50 -0
  106. data/lib/one_apm/inst/framework/sinatra/transaction_namer.rb +54 -0
  107. data/lib/one_apm/inst/http_clients/curb.rb +189 -0
  108. data/lib/one_apm/inst/http_clients/excon.rb +70 -0
  109. data/lib/one_apm/inst/http_clients/excon/connection.rb +31 -0
  110. data/lib/one_apm/inst/http_clients/excon/middleware.rb +55 -0
  111. data/lib/one_apm/inst/http_clients/httpclient.rb +44 -0
  112. data/lib/one_apm/inst/http_clients/net.rb +34 -0
  113. data/lib/one_apm/inst/http_clients/typhoeus.rb +76 -0
  114. data/lib/one_apm/inst/nosql/memcache.rb +134 -0
  115. data/lib/one_apm/inst/nosql/mongo.rb +126 -0
  116. data/lib/one_apm/inst/nosql/mongo_moped.rb +85 -0
  117. data/lib/one_apm/inst/nosql/redis.rb +83 -0
  118. data/lib/one_apm/inst/orm/active_record.rb +99 -0
  119. data/lib/one_apm/inst/orm/active_record_4.rb +28 -0
  120. data/lib/one_apm/inst/orm/data_mapper.rb +180 -0
  121. data/lib/one_apm/inst/orm/sequel.rb +47 -0
  122. data/lib/one_apm/inst/rack.rb +38 -0
  123. data/lib/one_apm/inst/rack/rack.rb +44 -0
  124. data/lib/one_apm/inst/rack/rack_builder.rb +51 -0
  125. data/lib/one_apm/inst/rails/action_controller.rb +118 -0
  126. data/lib/one_apm/inst/rails/action_web_service.rb +44 -0
  127. data/lib/one_apm/inst/rails/errors.rb +43 -0
  128. data/lib/one_apm/inst/rails3/action_controller.rb +172 -0
  129. data/lib/one_apm/inst/rails3/errors.rb +43 -0
  130. data/lib/one_apm/inst/rails4/action_controller.rb +27 -0
  131. data/lib/one_apm/inst/rails4/action_controller_subscriber.rb +121 -0
  132. data/lib/one_apm/inst/rails4/action_view.rb +23 -0
  133. data/lib/one_apm/inst/rails4/action_view_subscriber.rb +93 -0
  134. data/lib/one_apm/inst/rails4/active_record_subscriber.rb +96 -0
  135. data/lib/one_apm/inst/rails4/errors.rb +42 -0
  136. data/lib/one_apm/inst/rails_middleware.rb +40 -0
  137. data/lib/one_apm/inst/support/evented_subscriber.rb +98 -0
  138. data/lib/one_apm/inst/support/ignore_actions.rb +39 -0
  139. data/lib/one_apm/inst/support/queue_time.rb +76 -0
  140. data/lib/one_apm/inst/transaction_base.rb +405 -0
  141. data/lib/one_apm/logger/agent_logger.rb +206 -0
  142. data/lib/one_apm/logger/audit_logger.rb +78 -0
  143. data/lib/one_apm/logger/memory_logger.rb +50 -0
  144. data/lib/one_apm/logger/null_logger.rb +19 -0
  145. data/lib/one_apm/metrics/metric_data.rb +72 -0
  146. data/lib/one_apm/metrics/metric_spec.rb +82 -0
  147. data/lib/one_apm/metrics/stats.rb +173 -0
  148. data/lib/one_apm/probe.rb +16 -0
  149. data/lib/one_apm/probe/framework_loader.rb +53 -0
  150. data/lib/one_apm/probe/instance_methods.rb +105 -0
  151. data/lib/one_apm/probe/instrumentation.rb +60 -0
  152. data/lib/one_apm/rack/browser_monitoring.rb +144 -0
  153. data/lib/one_apm/rack/middleware_base.rb +27 -0
  154. data/lib/one_apm/rack/middleware_hooks.rb +17 -0
  155. data/lib/one_apm/rack/middleware_tracing.rb +81 -0
  156. data/lib/one_apm/rack/middleware_wrapper.rb +86 -0
  157. data/lib/one_apm/support/chained_call.rb +15 -0
  158. data/lib/one_apm/support/coerce.rb +81 -0
  159. data/lib/one_apm/support/collection_helper.rb +79 -0
  160. data/lib/one_apm/support/dotted_hash.rb +45 -0
  161. data/lib/one_apm/support/encoders.rb +34 -0
  162. data/lib/one_apm/support/environment_report.rb +127 -0
  163. data/lib/one_apm/support/event_buffer.rb +82 -0
  164. data/lib/one_apm/support/event_buffer/sampled_buffer.rb +45 -0
  165. data/lib/one_apm/support/event_buffer/sized_buffer.rb +21 -0
  166. data/lib/one_apm/support/event_buffer/synthetics_event_buffer.rb +40 -0
  167. data/lib/one_apm/support/helper.rb +49 -0
  168. data/lib/one_apm/support/hostname.rb +13 -0
  169. data/lib/one_apm/support/http_clients/curb_wrappers.rb +65 -0
  170. data/lib/one_apm/support/http_clients/excon_wrappers.rb +63 -0
  171. data/lib/one_apm/support/http_clients/httpclient_wrappers.rb +61 -0
  172. data/lib/one_apm/support/http_clients/net_http_wrappers.rb +48 -0
  173. data/lib/one_apm/support/http_clients/typhoeus_wrappers.rb +73 -0
  174. data/lib/one_apm/support/http_clients/uri_util.rb +39 -0
  175. data/lib/one_apm/support/json_marshaller.rb +68 -0
  176. data/lib/one_apm/support/json_wrapper.rb +130 -0
  177. data/lib/one_apm/support/language_support.rb +142 -0
  178. data/lib/one_apm/support/library_detection.rb +119 -0
  179. data/lib/one_apm/support/local_environment.rb +196 -0
  180. data/lib/one_apm/support/marshaller.rb +62 -0
  181. data/lib/one_apm/support/method_tracer.rb +334 -0
  182. data/lib/one_apm/support/method_tracer/helpers.rb +92 -0
  183. data/lib/one_apm/support/method_tracer/traced_method_stack.rb +103 -0
  184. data/lib/one_apm/support/obfuscator.rb +47 -0
  185. data/lib/one_apm/support/okjson.rb +601 -0
  186. data/lib/one_apm/support/parameter_filtering.rb +35 -0
  187. data/lib/one_apm/support/rules_engine.rb +56 -0
  188. data/lib/one_apm/support/rules_engine/replacement_rule.rb +80 -0
  189. data/lib/one_apm/support/rules_engine/segment_terms_rule.rb +46 -0
  190. data/lib/one_apm/support/server.rb +11 -0
  191. data/lib/one_apm/support/supported_versions.rb +257 -0
  192. data/lib/one_apm/support/system_info.rb +211 -0
  193. data/lib/one_apm/support/timer_lib.rb +29 -0
  194. data/lib/one_apm/support/version_number.rb +51 -0
  195. data/lib/one_apm/support/vm.rb +30 -0
  196. data/lib/one_apm/support/vm/jruby_vm.rb +38 -0
  197. data/lib/one_apm/support/vm/monotonic_gc_profiler.rb +43 -0
  198. data/lib/one_apm/support/vm/mri_vm.rb +85 -0
  199. data/lib/one_apm/support/vm/rubinius_vm.rb +129 -0
  200. data/lib/one_apm/support/vm/snapshot.rb +18 -0
  201. data/lib/one_apm/transaction.rb +336 -0
  202. data/lib/one_apm/transaction/class_methods.rb +132 -0
  203. data/lib/one_apm/transaction/instance_helpers.rb +82 -0
  204. data/lib/one_apm/transaction/metric_constants.rb +42 -0
  205. data/lib/one_apm/transaction/sample_buffer/force_persist_sample_buffer.rb +21 -0
  206. data/lib/one_apm/transaction/sample_buffer/slowest_sample_buffer.rb +21 -0
  207. data/lib/one_apm/transaction/sample_buffer/synthetics_sample_buffer.rb +21 -0
  208. data/lib/one_apm/transaction/sample_buffer/transaction_sample_buffer.rb +101 -0
  209. data/lib/one_apm/transaction/sample_buffer/xray_sample_buffer.rb +60 -0
  210. data/lib/one_apm/transaction/segment.rb +193 -0
  211. data/lib/one_apm/transaction/segment_summary.rb +51 -0
  212. data/lib/one_apm/transaction/thread_local_access.rb +73 -0
  213. data/lib/one_apm/transaction/transaction_analysis.rb +78 -0
  214. data/lib/one_apm/transaction/transaction_apdex.rb +20 -0
  215. data/lib/one_apm/transaction/transaction_cpu.rb +22 -0
  216. data/lib/one_apm/transaction/transaction_finish_append.rb +67 -0
  217. data/lib/one_apm/transaction/transaction_ignore.rb +33 -0
  218. data/lib/one_apm/transaction/transaction_jruby_functions.rb +40 -0
  219. data/lib/one_apm/transaction/transaction_metrics.rb +53 -0
  220. data/lib/one_apm/transaction/transaction_name.rb +90 -0
  221. data/lib/one_apm/transaction/transaction_namer.rb +49 -0
  222. data/lib/one_apm/transaction/transaction_sample.rb +204 -0
  223. data/lib/one_apm/transaction/transaction_sample_builder.rb +168 -0
  224. data/lib/one_apm/transaction/transaction_state.rb +149 -0
  225. data/lib/one_apm/transaction/transaction_summary.rb +28 -0
  226. data/lib/one_apm/transaction/transaction_synthetics.rb +40 -0
  227. data/lib/one_apm/transaction/transaction_timings.rb +54 -0
  228. data/lib/one_apm/version.rb +13 -0
  229. data/lib/oneapm_rpm.rb +16 -0
  230. data/lib/sequel/extensions/oneapm_instrumentation.rb +84 -0
  231. data/lib/sequel/plugins/oneapm_instrumentation.rb +66 -0
  232. data/oneapm.yml +135 -0
  233. data/oneapm_rpm.gemspec +58 -0
  234. metadata +474 -0
@@ -0,0 +1,331 @@
1
+ # encoding: utf-8
2
+
3
+ require 'zlib'
4
+ require 'base64'
5
+ require 'digest/md5'
6
+
7
+ require 'one_apm/agent'
8
+ require 'one_apm/probe'
9
+
10
+ module OneApm
11
+ module Collector
12
+ # This class contains the logic of recording slow SQL traces, which may
13
+ # represent multiple aggregated SQL queries.
14
+ #
15
+ # A slow SQL trace consists of a collection of SQL instrumented SQL queries
16
+ # that all normalize to the same text. For example, the following two
17
+ # queries would be aggregated together into a single slow SQL trace:
18
+ #
19
+ # SELECT * FROM table WHERE id=42
20
+ # SELECT * FROM table WHERE id=1234
21
+ #
22
+ # Each slow SQL trace keeps track of the number of times the same normalized
23
+ # query was seen, the min, max, and total time spent executing those
24
+ # queries, and an example backtrace from one of the aggregated queries.
25
+ #
26
+ # @api public
27
+ class SqlSampler
28
+
29
+ # Module defining methods stubbed out when the agent is disabled
30
+ module Shim
31
+ def on_start_transaction(*args); end
32
+ def on_finishing_transaction(*args); end
33
+ end
34
+
35
+ attr_reader :disabled
36
+
37
+ # this is for unit tests only
38
+ attr_reader :sql_traces
39
+
40
+ MAX_SAMPLES = 10
41
+
42
+ def initialize
43
+ @sql_traces = {}
44
+
45
+ # This lock is used to synchronize access to @sql_traces
46
+ # and related variables. It can become necessary on JRuby or
47
+ # any 'honest-to-god'-multithreaded system
48
+ @samples_lock = Mutex.new
49
+ end
50
+
51
+ def enabled?
52
+ Agent.config[:'slow_sql.enabled'] &&
53
+ Agent.config[:'transaction_tracer.enabled'] &&
54
+ OneApm::Agent::Database.should_record_sql?(:slow_sql)
55
+ end
56
+
57
+ def on_start_transaction(state, start_time, uri=nil)
58
+ return unless enabled?
59
+
60
+ state.sql_sampler_transaction_data = TransactionSqlData.new
61
+
62
+ if state.transaction_sample_builder
63
+ guid = state.transaction_sample_builder.sample.guid
64
+ end
65
+
66
+ if Agent.config[:'slow_sql.enabled'] && state.sql_sampler_transaction_data
67
+ state.sql_sampler_transaction_data.set_transaction_info(uri, guid)
68
+ end
69
+ end
70
+
71
+ def tl_transaction_data # only used for testing
72
+ OneApm::TransactionState.tl_get.sql_sampler_transaction_data
73
+ end
74
+
75
+ # This is called when we are done with the transaction.
76
+ def on_finishing_transaction(state, name, time=Time.now)
77
+ return unless enabled?
78
+
79
+ data = state.sql_sampler_transaction_data
80
+ return unless data
81
+
82
+ data.set_transaction_name(name)
83
+ if data.sql_data.size > 0
84
+ @samples_lock.synchronize do
85
+ ::OneApm::Agent.logger.debug "Examining #{data.sql_data.size} slow transaction sql statement(s)"
86
+ save_slow_sql data
87
+ end
88
+ end
89
+ end
90
+
91
+ # this should always be called under the @samples_lock
92
+ def save_slow_sql(transaction_sql_data)
93
+ path = transaction_sql_data.path
94
+ uri = transaction_sql_data.uri
95
+
96
+ transaction_sql_data.sql_data.each do |sql_item|
97
+ normalized_sql = sql_item.normalize
98
+ sql_trace = @sql_traces[normalized_sql]
99
+ if sql_trace
100
+ sql_trace.aggregate(sql_item, path, uri)
101
+ else
102
+ if has_room?
103
+ sql_trace = SqlTrace.new(normalized_sql, sql_item, path, uri)
104
+ elsif should_add_trace?(sql_item)
105
+ remove_shortest_trace
106
+ sql_trace = SqlTrace.new(normalized_sql, sql_item, path, uri)
107
+ end
108
+
109
+ if sql_trace
110
+ @sql_traces[normalized_sql] = sql_trace
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ # this should always be called under the @samples_lock
117
+ def should_add_trace?(sql_item)
118
+ @sql_traces.any? do |(_, existing_trace)|
119
+ existing_trace.max_call_time < sql_item.duration
120
+ end
121
+ end
122
+
123
+ # this should always be called under the @samples_lock
124
+ def has_room?
125
+ @sql_traces.size < MAX_SAMPLES
126
+ end
127
+
128
+ # this should always be called under the @samples_lock
129
+ def remove_shortest_trace
130
+ shortest_key, _ = @sql_traces.min_by { |(_, trace)| trace.max_call_time }
131
+ @sql_traces.delete(shortest_key)
132
+ end
133
+
134
+ # Records an SQL query, potentially creating a new slow SQL trace, or
135
+ # aggregating the query into an existing slow SQL trace.
136
+ #
137
+ # This method should be used only by gem authors wishing to extend
138
+ # the Ruby agent to instrument new database interfaces - it should
139
+ # generally not be called directly from application code.
140
+ #
141
+ # @param sql [String] the SQL query being recorded
142
+ # @param metric_name [String] is the metric name under which this query will be recorded
143
+ # @param config [Object] is the driver configuration for the connection
144
+ # @param duration [Float] number of seconds the query took to execute
145
+ # @param explainer [Proc] for internal use only - 3rd-party clients must
146
+ # not pass this parameter.
147
+ #
148
+ # @api public
149
+ #
150
+ def notice_sql(sql, metric_name, config, duration, state=nil, &explainer) #THREAD_LOCAL_ACCESS sometimes
151
+ state ||= OneApm::TransactionState.tl_get
152
+ data = state.sql_sampler_transaction_data
153
+ return unless data
154
+
155
+ if state.is_sql_recorded?
156
+ if duration > Agent.config[:'slow_sql.explain_threshold']
157
+ backtrace = caller
158
+ backtrace.reject! { |t| t.include?('one_apm') }
159
+ data.sql_data << SlowSql.new(OneApm::Agent::Database.capture_query(sql),
160
+ metric_name, config,
161
+ duration, backtrace, &explainer)
162
+ end
163
+ end
164
+ end
165
+
166
+ def merge!(sql_traces)
167
+ @samples_lock.synchronize do
168
+ sql_traces.each do |trace|
169
+ existing_trace = @sql_traces[trace.sql]
170
+ if existing_trace
171
+ existing_trace.aggregate_trace(trace)
172
+ else
173
+ @sql_traces[trace.sql] = trace
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ def harvest!
180
+ return [] unless enabled?
181
+
182
+ slowest = []
183
+ @samples_lock.synchronize do
184
+ slowest = @sql_traces.values
185
+ @sql_traces = {}
186
+ end
187
+ slowest.each {|trace| trace.prepare_to_send }
188
+ slowest
189
+ end
190
+
191
+ def reset!
192
+ @samples_lock.synchronize do
193
+ @sql_traces = {}
194
+ end
195
+ end
196
+ end
197
+
198
+ class TransactionSqlData
199
+ attr_reader :path
200
+ attr_reader :uri
201
+ attr_reader :sql_data
202
+ attr_reader :guid
203
+
204
+ def initialize
205
+ @sql_data = []
206
+ end
207
+
208
+ def set_transaction_info(uri, guid)
209
+ @uri = uri
210
+ @guid = guid
211
+ end
212
+
213
+ def set_transaction_name(name)
214
+ @path = name
215
+ end
216
+ end
217
+
218
+ class SlowSql
219
+ attr_reader :sql
220
+ attr_reader :metric_name
221
+ attr_reader :duration
222
+ attr_reader :backtrace
223
+
224
+ def initialize(sql, metric_name, config, duration, backtrace=nil,
225
+ &explainer)
226
+ @sql = sql
227
+ @metric_name = metric_name
228
+ @config = config
229
+ @duration = duration
230
+ @backtrace = backtrace
231
+ @explainer = explainer
232
+ end
233
+
234
+ def obfuscate
235
+ OneApm::Agent::Database.obfuscate_sql(@sql)
236
+ end
237
+
238
+ def normalize
239
+ OneApm::Agent::Database::Obfuscator.instance \
240
+ .default_sql_obfuscator(@sql).gsub(/\?\s*\,\s*/, '').gsub(/\s/, '')
241
+ end
242
+
243
+ def explain
244
+ if @config && @explainer
245
+ OneApm::Agent::Database.explain_sql(@sql, @config, &@explainer)
246
+ end
247
+ end
248
+
249
+ # We can't serialize the explainer, so clear it before we transmit
250
+ def prepare_to_send
251
+ @explainer = nil
252
+ end
253
+ end
254
+
255
+ class SqlTrace < OneApm::Metrics::Stats
256
+ attr_reader :path
257
+ attr_reader :url
258
+ attr_reader :sql_id
259
+ attr_reader :sql
260
+ attr_reader :database_metric_name
261
+ attr_reader :params
262
+ attr_reader :slow_sql
263
+
264
+ def initialize(normalized_query, slow_sql, path, uri)
265
+ super()
266
+ @params = {}
267
+ @sql_id = consistent_hash(normalized_query)
268
+ set_primary slow_sql, path, uri
269
+ record_data_point(float(slow_sql.duration))
270
+ end
271
+
272
+ def set_primary(slow_sql, path, uri)
273
+ @slow_sql = slow_sql
274
+ @sql = slow_sql.sql
275
+ @database_metric_name = slow_sql.metric_name
276
+ @path = path
277
+ @url = uri
278
+ @params[:backtrace] = slow_sql.backtrace if slow_sql.backtrace
279
+ end
280
+
281
+ def aggregate(slow_sql, path, uri)
282
+ if slow_sql.duration > max_call_time
283
+ set_primary slow_sql, path, uri
284
+ end
285
+
286
+ record_data_point(float(slow_sql.duration))
287
+ end
288
+
289
+ def aggregate_trace(trace)
290
+ aggregate(trace.slow_sql, trace.path, trace.url)
291
+ end
292
+
293
+ def prepare_to_send
294
+ params[:explain_plan] = @slow_sql.explain if need_to_explain?
295
+ @sql = @slow_sql.obfuscate if need_to_obfuscate?
296
+ @slow_sql.prepare_to_send
297
+ end
298
+
299
+ def need_to_obfuscate?
300
+ Agent.config[:'slow_sql.record_sql'].to_s == 'obfuscated'
301
+ end
302
+
303
+ def need_to_explain?
304
+ Agent.config[:'slow_sql.explain_enabled']
305
+ end
306
+
307
+ include OneApm::Coerce
308
+
309
+ def to_collector_array(encoder)
310
+ [ string(@path),
311
+ string(@url),
312
+ int(@sql_id),
313
+ string(@sql),
314
+ string(@database_metric_name),
315
+ int(@call_count),
316
+ Helper.time_to_millis(@total_call_time),
317
+ Helper.time_to_millis(@min_call_time),
318
+ Helper.time_to_millis(@max_call_time),
319
+ encoder.encode(@params) ]
320
+ end
321
+
322
+ private
323
+
324
+ def consistent_hash(string)
325
+ # need to hash the same way in every process
326
+ Digest::MD5.hexdigest(string).hex \
327
+ .modulo(2**31-1) # ensure sql_id fits in an INT(11)
328
+ end
329
+ end
330
+ end
331
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/collector/stats_engine/metric_stats'
4
+ require 'one_apm/collector/stats_engine/gc_profiler'
5
+ require 'one_apm/collector/stats_engine/stats_hash'
6
+
7
+ module OneApm
8
+ module Collector
9
+ # This class handles all the statistics gathering for the agent
10
+ class StatsEngine
11
+ include MetricStats
12
+
13
+ module Shim
14
+ def add_sampler(*args); end
15
+ def add_harvest_sampler(*args); end
16
+ def start_sampler_thread(*args); end
17
+ end
18
+
19
+ attr_accessor :metric_rules
20
+
21
+ def initialize
22
+ @stats_lock = Mutex.new
23
+ @stats_hash = StatsHash.new
24
+ @metric_rules = OneApm::Support::RulesEngine.new
25
+ end
26
+
27
+ # All access to the @stats_hash ivar should be funnelled through this
28
+ # method to ensure thread-safety.
29
+ def with_stats_lock
30
+ @stats_lock.synchronize { yield }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,249 @@
1
+ # encoding: utf-8
2
+
3
+ require 'monitor'
4
+
5
+ class OneApm::Collector::TransactionEventAggregator
6
+ include OneApm::Coerce, MonitorMixin
7
+
8
+ # The type field of the sample
9
+ SAMPLE_TYPE = 'Transaction'.freeze
10
+
11
+ # Strings for static keys of the sample structure
12
+ TYPE_KEY = 'type'.freeze
13
+ TIMESTAMP_KEY = 'timestamp'.freeze
14
+ NAME_KEY = 'name'.freeze
15
+ DURATION_KEY = 'duration'.freeze
16
+ HTTP_RESPONSE_CODE_KEY = 'httpResponseCode'.freeze
17
+ GUID_KEY = 'bw.guid'.freeze
18
+ REFERRING_TRANSACTION_GUID_KEY = 'bw.referringTransactionGuid'.freeze
19
+ CAT_TRIP_ID_KEY = 'bw.tripId'.freeze
20
+ CAT_PATH_HASH_KEY = 'bw.pathHash'.freeze
21
+ CAT_REFERRING_PATH_HASH_KEY = 'bw.referringPathHash'.freeze
22
+ CAT_ALTERNATE_PATH_HASHES_KEY = 'bw.alternatePathHashes'.freeze
23
+ APDEX_PERF_ZONE_KEY = 'bw.apdexPerfZone'.freeze
24
+ SYNTHETICS_RESOURCE_ID_KEY = "bw.syntheticsResourceId".freeze
25
+ SYNTHETICS_JOB_ID_KEY = "bw.syntheticsJobId".freeze
26
+ SYNTHETICS_MONITOR_ID_KEY = "bw.syntheticsMonitorId".freeze
27
+
28
+ def initialize(event_listener)
29
+ super()
30
+
31
+ @enabled = false
32
+ @notified_full = false
33
+
34
+ @samples = ::OneApm::Agent::SampledBuffer.new(OneApm::Agent.config[:'analytics_events.max_samples_stored'])
35
+ @synthetics_samples = ::OneApm::Agent::SyntheticsEventBuffer.new(OneApm::Agent.config[:'synthetics.events_limit'])
36
+
37
+ event_listener.subscribe(:transaction_finished, &method(:on_transaction_finished))
38
+ self.register_config_callbacks
39
+ end
40
+
41
+ # Fetch a copy of the sampler's gathered samples. (Synchronized)
42
+ def samples
43
+ self.synchronize { @samples.to_a.concat(@synthetics_samples.to_a) }
44
+ end
45
+
46
+ def reset!
47
+ sample_count, request_count, synthetics_dropped = 0
48
+ old_samples = nil
49
+
50
+ self.synchronize do
51
+ sample_count = @samples.size
52
+ request_count = @samples.num_seen
53
+
54
+ synthetics_dropped = @synthetics_samples.num_dropped
55
+
56
+ old_samples = @samples.to_a + @synthetics_samples.to_a
57
+ @samples.reset!
58
+ @synthetics_samples.reset!
59
+
60
+ @notified_full = false
61
+ end
62
+
63
+ [old_samples, sample_count, request_count, synthetics_dropped]
64
+ end
65
+
66
+ # Clear any existing samples, reset the last sample time, and return the
67
+ # previous set of samples. (Synchronized)
68
+ def harvest!
69
+ old_samples, sample_count, request_count, synthetics_dropped = reset!
70
+ record_sampling_rate(request_count, sample_count) if @enabled
71
+ record_dropped_synthetics(synthetics_dropped)
72
+ old_samples
73
+ end
74
+
75
+ # Merge samples back into the buffer, for example after a failed
76
+ # transmission to the collector. (Synchronized)
77
+ def merge!(old_samples)
78
+ self.synchronize do
79
+ old_samples.each { |s| append_event(s) }
80
+ end
81
+ end
82
+
83
+ def record_sampling_rate(request_count, sample_count) #THREAD_LOCAL_ACCESS
84
+ request_count_lifetime = @samples.seen_lifetime
85
+ sample_count_lifetime = @samples.captured_lifetime
86
+ OneApm::Agent.logger.debug("Sampled %d / %d (%.1f %%) requests this cycle, %d / %d (%.1f %%) since startup" % [
87
+ sample_count,
88
+ request_count,
89
+ (sample_count.to_f / request_count * 100.0),
90
+ sample_count_lifetime,
91
+ request_count_lifetime,
92
+ (sample_count_lifetime.to_f / request_count_lifetime * 100.0)
93
+ ])
94
+
95
+ engine = OneApm::Agent.instance.stats_engine
96
+ engine.tl_record_supportability_metric_count("TransactionEventAggregator/requests", request_count)
97
+ engine.tl_record_supportability_metric_count("TransactionEventAggregator/samples", sample_count)
98
+ end
99
+
100
+ def record_dropped_synthetics(synthetics_dropped)
101
+ return unless synthetics_dropped > 0
102
+
103
+ OneApm::Agent.logger.debug("Synthetics transaction event limit (#{@samples.capacity}) reached. Further synthetics events this harvest period dropped.")
104
+
105
+ engine = OneApm::Agent.instance.stats_engine
106
+ engine.tl_record_supportability_metric_count("TransactionEventAggregator/synthetics_events_dropped", synthetics_dropped)
107
+ end
108
+
109
+ def register_config_callbacks
110
+ OneApm::Agent.config.register_callback(:'analytics_events.max_samples_stored') do |max_samples|
111
+ OneApm::Agent.logger.debug "TransactionEventAggregator max_samples set to #{max_samples}"
112
+ self.synchronize { @samples.capacity = max_samples }
113
+ end
114
+
115
+ OneApm::Agent.config.register_callback(:'synthetics.events_limit') do |max_samples|
116
+ OneApm::Agent.logger.debug "TransactionEventAggregator limit for synthetics events set to #{max_samples}"
117
+ self.synchronize { @synthetics_samples.capacity = max_samples }
118
+ end
119
+
120
+ OneApm::Agent.config.register_callback(:'analytics_events.enabled') do |enabled|
121
+ @enabled = enabled
122
+ end
123
+ end
124
+
125
+ def notify_full
126
+ OneApm::Agent.logger.debug "Transaction event capacity of #{@samples.capacity} reached, beginning sampling"
127
+ @notified_full = true
128
+ end
129
+
130
+ # Event handler for the :transaction_finished event.
131
+ def on_transaction_finished(payload)
132
+ return unless @enabled
133
+
134
+ main_event = create_main_event(payload)
135
+ custom_params = create_custom_parameters(payload)
136
+
137
+ self.synchronize { append_event([main_event, custom_params]) }
138
+ notify_full if !@notified_full && @samples.full?
139
+ end
140
+
141
+ def append_event(event)
142
+ main_event, _ = event
143
+
144
+ if main_event.include?(SYNTHETICS_RESOURCE_ID_KEY)
145
+ # Try adding to synthetics buffer. If anything is rejected, give it a
146
+ # shot in the main transaction events (where it may get sampled)
147
+ result, rejected = @synthetics_samples.append_with_reject(event)
148
+
149
+ if rejected
150
+ @samples.append(rejected)
151
+ end
152
+ else
153
+ @samples.append(event)
154
+ end
155
+ end
156
+
157
+ def self.map_metric(metric_name, to_add={})
158
+ to_add.values.each(&:freeze)
159
+
160
+ mappings = OVERVIEW_SPECS.fetch(metric_name, {})
161
+ mappings.merge!(to_add)
162
+
163
+ OVERVIEW_SPECS[metric_name] = mappings
164
+ end
165
+
166
+ OVERVIEW_SPECS = {}
167
+
168
+ # All Transactions
169
+ # Don't need to use the transaction-type specific metrics since this is
170
+ # scoped to just one transaction, so Datastore/all has what we want.
171
+ map_metric('Datastore/all', :total_call_time => "databaseDuration")
172
+ map_metric('Datastore/all', :call_count => "databaseCallCount")
173
+ map_metric('GC/Transaction/all', :total_call_time => "gcCumulative")
174
+
175
+ # Web Metrics
176
+ map_metric('WebFrontend/QueueTime', :total_call_time => "queueDuration")
177
+ map_metric('Memcache/allWeb', :total_call_time => "memcacheDuration")
178
+
179
+ map_metric('External/allWeb', :total_call_time => "externalDuration")
180
+ map_metric('External/allWeb', :call_count => "externalCallCount")
181
+
182
+ # Background Metrics
183
+ map_metric('Memcache/allOther', :total_call_time => "memcacheDuration")
184
+
185
+ map_metric('External/allOther', :total_call_time => "externalDuration")
186
+ map_metric('External/allOther', :call_count => "externalCallCount")
187
+
188
+ def append_metrics(txn_metrics, sample)
189
+ if txn_metrics
190
+ OVERVIEW_SPECS.each do |(name, extracted_values)|
191
+ if txn_metrics.has_key?(name)
192
+ stat = txn_metrics[name]
193
+ extracted_values.each do |value_name, key_name|
194
+ sample[key_name] = stat.send(value_name)
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ def create_main_event(payload)
202
+ sample = {
203
+ TIMESTAMP_KEY => float(payload[:start_timestamp]),
204
+ NAME_KEY => string(payload[:name]),
205
+ DURATION_KEY => float(payload[:duration]),
206
+ TYPE_KEY => SAMPLE_TYPE,
207
+ }
208
+ append_metrics(payload[:metrics], sample)
209
+ optionally_append(GUID_KEY, :guid, sample, payload)
210
+ optionally_append(REFERRING_TRANSACTION_GUID_KEY, :referring_transaction_guid, sample, payload)
211
+ optionally_append(CAT_TRIP_ID_KEY, :cat_trip_id, sample, payload)
212
+ optionally_append(CAT_PATH_HASH_KEY, :cat_path_hash, sample, payload)
213
+ optionally_append(CAT_REFERRING_PATH_HASH_KEY, :cat_referring_path_hash, sample, payload)
214
+ optionally_append(APDEX_PERF_ZONE_KEY, :apdex_perf_zone, sample, payload)
215
+ optionally_append(SYNTHETICS_RESOURCE_ID_KEY, :synthetics_resource_id, sample, payload)
216
+ optionally_append(SYNTHETICS_JOB_ID_KEY, :synthetics_job_id, sample, payload)
217
+ optionally_append(SYNTHETICS_MONITOR_ID_KEY, :synthetics_monitor_id, sample, payload)
218
+ append_http_response_code(sample, payload)
219
+ append_cat_alternate_path_hashes(sample, payload)
220
+ sample
221
+ end
222
+
223
+ def append_http_response_code(sample, payload)
224
+ unless OneApm::Agent.config[:disable_rack_middleware]
225
+ optionally_append(HTTP_RESPONSE_CODE_KEY, :http_response_code, sample, payload)
226
+ end
227
+ end
228
+
229
+ def append_cat_alternate_path_hashes(sample, payload)
230
+ if payload.include?(:cat_alternate_path_hashes)
231
+ sample[CAT_ALTERNATE_PATH_HASHES_KEY] = payload[:cat_alternate_path_hashes].sort.join(',')
232
+ end
233
+ end
234
+
235
+ def optionally_append(sample_key, payload_key, sample, payload)
236
+ if payload.include?(payload_key)
237
+ sample[sample_key] = string(payload[payload_key])
238
+ end
239
+ end
240
+
241
+ def create_custom_parameters(payload)
242
+ custom_params = {}
243
+ if ::OneApm::Agent.config[:'analytics_events.capture_attributes']
244
+ custom_params.merge!(event_params(payload[:custom_params] || {}))
245
+ end
246
+ custom_params
247
+ end
248
+
249
+ end