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,78 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/agent/database/obfuscation_helpers'
4
+
5
+ module OneApm
6
+ module Agent
7
+ module Database
8
+ class Obfuscator
9
+ include Singleton
10
+ include ObfuscationHelpers
11
+
12
+ attr_reader :obfuscator
13
+
14
+ QUERY_TOO_LARGE_MESSAGE = "Query too large (over 16k characters) to safely obfuscate"
15
+ FAILED_TO_OBFUSCATE_MESSAGE = "Failed to obfuscate SQL query - quote characters remained after obfuscation"
16
+
17
+ def initialize
18
+ reset
19
+ end
20
+
21
+ def reset
22
+ @obfuscator = method(:default_sql_obfuscator)
23
+ end
24
+
25
+ # Sets the sql obfuscator used to clean up sql when sending it
26
+ # to the server. Possible types are:
27
+ #
28
+ # :before => sets the block to run before the existing
29
+ # obfuscators
30
+ #
31
+ # :after => sets the block to run after the existing
32
+ # obfuscator(s)
33
+ #
34
+ # :replace => removes the current obfuscator and replaces it
35
+ # with the provided block
36
+ def set_sql_obfuscator(type, &block)
37
+ if type == :before
38
+ @obfuscator = OneApm::ChainedCall.new(block, @obfuscator)
39
+ elsif type == :after
40
+ @obfuscator = OneApm::ChainedCall.new(@obfuscator, block)
41
+ elsif type == :replace
42
+ @obfuscator = block
43
+ else
44
+ fail "unknown sql_obfuscator type #{type}"
45
+ end
46
+ end
47
+
48
+ def default_sql_obfuscator(sql)
49
+ if sql[-3,3] == '...'
50
+ return QUERY_TOO_LARGE_MESSAGE
51
+ end
52
+
53
+ stmt = sql.kind_of?(Statement) ? sql : Statement.new(sql)
54
+ obfuscate_double_quotes = stmt.adapter.to_s !~ /postgres|sqlite/
55
+
56
+ obfuscated = obfuscate_numeric_literals(stmt)
57
+
58
+ if obfuscate_double_quotes
59
+ obfuscated = obfuscate_quoted_literals(obfuscated)
60
+ obfuscated = remove_comments(obfuscated)
61
+ if contains_quotes?(obfuscated)
62
+ obfuscated = FAILED_TO_OBFUSCATE_MESSAGE
63
+ end
64
+ else
65
+ obfuscated = obfuscate_single_quote_literals(obfuscated)
66
+ obfuscated = remove_comments(obfuscated)
67
+ if contains_single_quotes?(obfuscated)
68
+ obfuscated = FAILED_TO_OBFUSCATE_MESSAGE
69
+ end
70
+ end
71
+
72
+
73
+ obfuscated.to_s # return back to a regular String
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/agent/database'
4
+ require 'one_apm/agent/database/obfuscation_helpers'
5
+
6
+ module OneApm
7
+ module Agent
8
+ module Database
9
+ module PostgresExplainObfuscator
10
+ extend self
11
+
12
+ # Note that this regex can't be shared with the ones in the
13
+ # Database::Obfuscator class because here we don't look for
14
+ # backslash-escaped strings (and those regexes are backwards).
15
+ QUOTED_STRINGS_REGEX = /'(?:[^']|'')*'|"(?:[^"]|"")*"/
16
+ LABEL_LINE_REGEX = /^([^:\n]*:\s+).*$/.freeze
17
+
18
+ def obfuscate(explain)
19
+ # First, we replace all single-quoted strings.
20
+ # This is necessary in order to deal with multi-line string constants
21
+ # embedded in the explain output.
22
+ #
23
+ # Note that we look for both single or double quotes but do not
24
+ # replace double quotes in order to avoid accidentally latching onto a
25
+ # single quote character embedded within a quoted identifier (such as
26
+ # a table name).
27
+ #
28
+ # Note also that we make no special provisions for backslash-escaped
29
+ # single quotes (\') because these are canonicalized to two single
30
+ # quotes ('') in the explain output.
31
+ explain.gsub!(QUOTED_STRINGS_REGEX) do |match|
32
+ match.start_with?('"') ? match : '?'
33
+ end
34
+
35
+ # Now, mask anything after the first colon (:).
36
+ # All parts of the query that can appear in the explain output are
37
+ # prefixed with "<label>: ", so we want to preserve the label, but
38
+ # remove the rest of the line.
39
+ explain.gsub!(LABEL_LINE_REGEX, '\1?')
40
+ explain
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,175 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/agent/datastores/metric_helper'
4
+
5
+ module OneApm
6
+ module Agent
7
+ module Datastores
8
+
9
+ # Add Datastore tracing to a method. This properly generates the metrics
10
+ # for OneApm's Datastore features. It does not capture the actual
11
+ # query content into Transaction Traces. Use wrap if you want to provide
12
+ # that functionality.
13
+ #
14
+ # @param [Class] clazz the class to instrument
15
+ #
16
+ # @param [String, Symbol] method_name the name of instance method to
17
+ # instrument
18
+ #
19
+ # @param [String] product name of your datastore for use in metric naming, e.g. "Redis"
20
+ #
21
+ # @param [optional,String] operation the name of operation if different
22
+ # than the instrumented method name
23
+ #
24
+ # @api public
25
+ #
26
+ def self.trace(clazz, method_name, product, operation = method_name)
27
+ clazz.class_eval do
28
+ method_name_without_oneapm = "#{method_name}_without_oneapm"
29
+
30
+ if OneApm::Helper.instance_methods_include?(clazz, method_name) &&
31
+ !OneApm::Helper.instance_methods_include?(clazz, method_name_without_oneapm)
32
+
33
+ visibility = OneApm::Helper.instance_method_visibility(clazz, method_name)
34
+
35
+ alias_method method_name_without_oneapm, method_name
36
+
37
+ define_method(method_name) do |*args, &blk|
38
+ metrics = MetricHelper.metrics_for(product, operation)
39
+ OneApm::Support::MethodTracer.trace_execution_scoped(metrics) do
40
+ send(method_name_without_oneapm, *args, &blk)
41
+ end
42
+ end
43
+
44
+ send visibility, method_name
45
+ send visibility, method_name_without_oneapm
46
+ end
47
+ end
48
+ end
49
+
50
+ # Wrap a call to a datastore and record OneApm Datastore metrics. This
51
+ # method can be used when a collection (i.e. table or model name) is
52
+ # known at runtime to be included in the metric naming. It is intended
53
+ # for situations that the simpler OneApm::Agent::Datastores.trace can't
54
+ # properly handle.
55
+ #
56
+ # To use this, wrap the datastore operation in the block passed to wrap.
57
+ #
58
+ # OneApm::Agent::Datastores.wrap("FauxDB", "find", "items") do
59
+ # FauxDB.find(query)
60
+ # end
61
+ #
62
+ # @param [String] product the datastore name for use in metric naming,
63
+ # e.g. "FauxDB"
64
+ #
65
+ # @param [String,Symbol] operation the name of operation (e.g. "select"),
66
+ # often named after the method that's being instrumented.
67
+ #
68
+ # @param [optional, String] collection the collection name for use in
69
+ # statement-level metrics (i.e. table or model name)
70
+ #
71
+ # @param [Proc,#call] callback proc or other callable to invoke after
72
+ # running the datastore block. Receives three arguments: result of the
73
+ # yield, the most specific (scoped) metric name, and elapsed time of the
74
+ # call. An example use is attaching SQL to Transaction Traces at the end
75
+ # of a wrapped datastore call.
76
+ #
77
+ # callback = Proc.new do |result, metrics, elapsed|
78
+ # OneApm::Agent::Datastores.notice_sql(query, metrics, elapsed)
79
+ # end
80
+ #
81
+ # OneApm::Agent::Datastores.wrap("FauxDB", "find", "items", callback) do
82
+ # FauxDB.find(query)
83
+ # end
84
+ #
85
+ # **NOTE: THERE ARE SECURITY CONCERNS WHEN CAPTURING SQL!**
86
+ # OneApm's Transaction Tracing and Slow SQL features will
87
+ # attempt to apply obfuscation to the passed queries, but it is possible
88
+ # for a query format to be unsupported and result in exposing user
89
+ # information embedded within captured queries.
90
+ #
91
+ # @api public
92
+ #
93
+ def self.wrap(product, operation, collection = nil, callback = nil)
94
+ return yield unless operation
95
+
96
+ metrics = MetricHelper.metrics_for(product, operation, collection)
97
+ scoped_metric = metrics.first
98
+ OneApm::Support::MethodTracer.trace_execution_scoped(metrics) do
99
+ t0 = Time.now
100
+ begin
101
+ result = yield
102
+ ensure
103
+ if callback
104
+ elapsed_time = (Time.now - t0).to_f
105
+ callback.call(result, scoped_metric, elapsed_time)
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ # Wrapper for simplifying attaching SQL queries during a transaction.
112
+ #
113
+ # If you are recording non-SQL data, please use the notice_statement
114
+ # method instead.
115
+ #
116
+ # OneApm::Agent::Datastores.notice_sql(query, metrics, elapsed)
117
+ #
118
+ # @param [String] query the SQL text to be captured. Note that depending
119
+ # on user settings, this string will be run through obfuscation, but
120
+ # some dialects of SQL (or non-SQL queries) are not guaranteed to be
121
+ # properly obfuscated by these routines!
122
+ #
123
+ # @param [String] scoped_metric The most specific metric relating to this
124
+ # query. Typically the result of
125
+ # OneApm::Agent::Datastores::MetricHelper#metrics_for
126
+ #
127
+ # @param [Float] elapsed the elapsed time during query execution
128
+ #
129
+ # **NOTE: THERE ARE SECURITY CONCERNS WHEN CAPTURING SQL!**
130
+ # OneApm's Transaction Tracing and Slow SQL features will
131
+ # attempt to apply obfuscation to the passed queries, but it is possible
132
+ # for a query format to be unsupported and result in exposing user
133
+ # information embedded within captured queries.
134
+ #
135
+ def self.notice_sql(query, scoped_metric, elapsed)
136
+ agent = OneApm::Agent.instance
137
+ agent.transaction_sampler.notice_sql(query, nil, elapsed)
138
+ agent.sql_sampler.notice_sql(query, scoped_metric, nil, elapsed)
139
+ nil
140
+ end
141
+
142
+ # Wrapper for simplifying attaching non-SQL data statements to a
143
+ # transaction. For instance, Mongo or CQL queries, Memcached or Redis
144
+ # keys would all be appropriate data to attach as statements.
145
+ #
146
+ # Data passed to this method is NOT obfuscated by OneApm, so please
147
+ # ensure that user information is obfuscated if the agent setting
148
+ # `transaction_tracer.record_sql` is set to `obfuscated`
149
+ #
150
+ # OneApm::Agent::Datastores.notice_statement("key", elapsed)
151
+ #
152
+ # @param [String] statement text of the statement to capture.
153
+ #
154
+ # @param [Float] elapsed the elapsed time during query execution
155
+ #
156
+ # **NOTE: THERE ARE SECURITY CONCERNS WHEN CAPTURING STATEMENTS!**
157
+ # This method will properly ignore statements when the user has turned
158
+ # off capturing queries, but it is not able to obfuscate arbitrary data!
159
+ # To prevent exposing user information embedded in captured queries,
160
+ # please ensure all data passed to this method is safe to transmit to
161
+ # OneApm.
162
+ #
163
+ def self.notice_statement(statement, elapsed)
164
+ # Settings may change eventually, but for now we follow the same
165
+ # capture rules as SQL for non-SQL statements.
166
+ return unless OneApm::Agent::Database.should_record_sql?
167
+
168
+ agent = OneApm::Agent.instance
169
+ agent.transaction_sampler.notice_nosql_statement(statement, elapsed)
170
+ nil
171
+ end
172
+
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+
3
+ module OneApm
4
+ module Agent
5
+ module Datastores
6
+ module MetricHelper
7
+ ROLLUP_METRIC = "Datastore/all".freeze
8
+ WEB_ROLLUP_METRIC = "Datastore/allWeb".freeze
9
+ OTHER_ROLLUP_METRIC = "Datastore/allOther".freeze
10
+ DEFAULT_PRODUCT_NAME = "ActiveRecord".freeze
11
+ OTHER = "Other".freeze
12
+
13
+ ALL = "all".freeze
14
+ ALL_WEB = "allWeb".freeze
15
+ ALL_OTHER = "allOther".freeze
16
+
17
+ def self.statement_metric_for(product, collection, operation)
18
+ "Datastore/statement/#{product}/#{collection}/#{operation}"
19
+ end
20
+
21
+ def self.operation_metric_for(product, operation)
22
+ "Datastore/operation/#{product}/#{operation}"
23
+ end
24
+
25
+ def self.product_suffixed_rollup(product, suffix)
26
+ "Datastore/#{product}/#{suffix}"
27
+ end
28
+
29
+ def self.product_rollup(product)
30
+ "Datastore/#{product}/all"
31
+ end
32
+
33
+ def self.suffixed_rollup(suffix)
34
+ "Datastore/#{suffix}"
35
+ end
36
+
37
+ def self.all_suffix
38
+ if OneApm::Transaction.recording_web_transaction?
39
+ ALL_WEB
40
+ else
41
+ ALL_OTHER
42
+ end
43
+ end
44
+
45
+ def self.metrics_for(product, operation, collection = nil)
46
+ if overrides = overridden_operation_and_collection
47
+ operation, collection = overrides
48
+ end
49
+
50
+ suffix = all_suffix
51
+
52
+ # Order of these metrics matters--the first metric in the list will
53
+ # be treated as the scoped metric in a bunch of different cases.
54
+ metrics = [
55
+ operation_metric_for(product, operation),
56
+ product_suffixed_rollup(product, suffix),
57
+ product_rollup(product),
58
+ suffixed_rollup(suffix),
59
+ ROLLUP_METRIC
60
+ ]
61
+
62
+ metrics.unshift statement_metric_for(product, collection, operation) if collection
63
+
64
+ metrics
65
+ end
66
+
67
+ def self.metrics_from_sql(product, sql)
68
+ operation = OneApm::Agent::Database.parse_operation_from_query(sql) || OTHER
69
+ metrics_for(product, operation)
70
+ end
71
+
72
+ # Allow Transaction#with_database_metric_name to override our
73
+ # collection and operation
74
+ def self.overridden_operation_and_collection #THREAD_LOCAL_ACCESS
75
+ state = OneApm::TransactionState.tl_get
76
+ txn = state.current_transaction
77
+ txn ? txn.instrumentation_state[:datastore_override] : nil
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+
3
+ module OneApm
4
+ module Agent
5
+ module Datastores
6
+ module Mongo
7
+ def self.is_supported_version?
8
+ # No version constant in < 2.0 versions of Mongo :(
9
+ defined?(::Mongo) &&
10
+ defined?(::Mongo::MongoClient) &&
11
+ !is_version2?
12
+ end
13
+
14
+ # At present we explicitly don't support version 2.x of the driver yet
15
+ def self.is_version2?
16
+ defined?(::Mongo::VERSION) &&
17
+ OneApm::VersionNumber.new(::Mongo::VERSION) > OneApm::VersionNumber.new("2.0.0")
18
+ end
19
+
20
+ def self.is_version_1_10_or_later?
21
+ # Again, no VERSION constant in 1.x, so we have to rely on constant checks
22
+ defined?(::Mongo::CollectionOperationWriter)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,189 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/agent/datastores/mongo/obfuscator'
4
+ require 'one_apm/agent/datastores/metric_helper'
5
+
6
+ module OneApm
7
+ module Agent
8
+ module Datastores
9
+ module Mongo
10
+ module MetricTranslator
11
+ def self.metrics_for(name, payload)
12
+ payload ||= {}
13
+
14
+ if collection_in_selector?(payload)
15
+ command_key = command_key_from_selector(payload)
16
+ name = get_name_from_selector(command_key, payload)
17
+ collection = get_collection_from_selector(command_key, payload)
18
+ else
19
+ collection = payload[:collection]
20
+ end
21
+
22
+ # The 1.10.0 version of the mongo driver renamed 'remove' to
23
+ # 'delete', but for metric consistency with previous versions we
24
+ # want to keep it as 'remove'.
25
+ name = 'remove' if name.to_s == 'delete'
26
+
27
+ if self.find_one?(name, payload)
28
+ name = 'findOne'
29
+ elsif self.find_and_remove?(name, payload)
30
+ name = 'findAndRemove'
31
+ elsif self.find_and_modify?(name, payload)
32
+ name = 'findAndModify'
33
+ elsif self.create_indexes?(name, payload)
34
+ name = 'createIndexes'
35
+ elsif self.create_index?(name, payload)
36
+ name = 'createIndex'
37
+ collection = self.collection_name_from_index(payload)
38
+ elsif self.drop_indexes?(name, payload)
39
+ name = 'dropIndexes'
40
+ elsif self.drop_index?(name, payload)
41
+ name = 'dropIndex'
42
+ elsif self.re_index?(name, payload)
43
+ name = 'reIndex'
44
+ elsif self.group?(name, payload)
45
+ name = 'group'
46
+ collection = collection_name_from_group_selector(payload)
47
+ elsif self.rename_collection?(name, payload)
48
+ name = 'renameCollection'
49
+ collection = collection_name_from_rename_selector(payload)
50
+ end
51
+
52
+ build_metrics(name, collection)
53
+ rescue => e
54
+ OneApm::Agent.logger.debug("Failure during Mongo metric generation", e)
55
+ []
56
+ end
57
+
58
+ MONGO_PRODUCT_NAME = "MongoDB".freeze
59
+
60
+ def self.build_metrics(name, collection)
61
+ OneApm::Agent::Datastores::MetricHelper.metrics_for(MONGO_PRODUCT_NAME,
62
+ name,
63
+ collection)
64
+ end
65
+
66
+ def self.collection_in_selector?(payload)
67
+ payload[:collection] == '$cmd' && payload[:selector]
68
+ end
69
+
70
+ NAMES_IN_SELECTOR = [
71
+ :findandmodify,
72
+
73
+ "aggregate",
74
+ "count",
75
+ "group",
76
+ "mapreduce",
77
+
78
+ :distinct,
79
+
80
+ :createIndexes,
81
+ :deleteIndexes,
82
+ :reIndex,
83
+
84
+ :collstats,
85
+ :renameCollection,
86
+ :drop,
87
+ ]
88
+
89
+ def self.command_key_from_selector(payload)
90
+ selector = payload[:selector]
91
+ NAMES_IN_SELECTOR.find do |check_name|
92
+ selector.key?(check_name)
93
+ end
94
+ end
95
+
96
+ def self.get_name_from_selector(command_key, payload)
97
+ if command_key
98
+ command_key.to_sym
99
+ else
100
+ OneApm::Agent.increment_metric("Supportability/Mongo/UnknownCollection")
101
+ payload[:selector].first.first unless command_key
102
+ end
103
+ end
104
+
105
+ CMD_COLLECTION = "$cmd".freeze
106
+
107
+ def self.get_collection_from_selector(command_key, payload)
108
+ if command_key
109
+ payload[:selector][command_key]
110
+ else
111
+ OneApm::Agent.increment_metric("Supportability/Mongo/UnknownCollection")
112
+ CMD_COLLECTION
113
+ end
114
+ end
115
+
116
+ def self.find_one?(name, payload)
117
+ name == :find && payload[:limit] == -1
118
+ end
119
+
120
+ def self.find_and_modify?(name, payload)
121
+ name == :findandmodify
122
+ end
123
+
124
+ def self.find_and_remove?(name, payload)
125
+ name == :findandmodify && payload[:selector] && payload[:selector][:remove]
126
+ end
127
+
128
+ def self.create_indexes?(name, paylod)
129
+ name == :createIndexes
130
+ end
131
+
132
+ def self.create_index?(name, payload)
133
+ name == :insert && payload[:collection] == "system.indexes"
134
+ end
135
+
136
+ def self.drop_indexes?(name, payload)
137
+ name == :deleteIndexes && payload[:selector] && payload[:selector][:index] == "*"
138
+ end
139
+
140
+ def self.drop_index?(name, payload)
141
+ name == :deleteIndexes
142
+ end
143
+
144
+ def self.re_index?(name, payload)
145
+ name == :reIndex && payload[:selector] && payload[:selector][:reIndex]
146
+ end
147
+
148
+ def self.group?(name, payload)
149
+ name == :group
150
+ end
151
+
152
+ def self.rename_collection?(name, payload)
153
+ name == :renameCollection
154
+ end
155
+
156
+ def self.collection_name_from_index(payload)
157
+ if payload[:documents]
158
+ if payload[:documents].is_a?(Array)
159
+ # mongo gem versions pre 1.10.0
160
+ document = payload[:documents].first
161
+ else
162
+ # mongo gem versions 1.10.0 and later
163
+ document = payload[:documents]
164
+ end
165
+
166
+ if document && document[:ns]
167
+ return document[:ns].split('.').last
168
+ end
169
+ end
170
+
171
+ 'system.indexes'
172
+ end
173
+
174
+ def self.collection_name_from_group_selector(payload)
175
+ payload[:selector]["group"]["ns"]
176
+ end
177
+
178
+ def self.collection_name_from_rename_selector(payload)
179
+ parts = payload[:selector][:renameCollection].split('.')
180
+ parts.shift
181
+ parts.join('.')
182
+ end
183
+
184
+ end
185
+
186
+ end
187
+ end
188
+ end
189
+ end