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,243 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/support/language_support'
4
+
5
+ module OneApm
6
+ module Collector
7
+ class StatsEngine
8
+ # Handles methods related to actual Metric collection
9
+ module MetricStats
10
+ SCOPE_PLACEHOLDER = '__SCOPE__'.freeze
11
+
12
+ # Update the unscoped metrics given in metric_names.
13
+ # metric_names may be either a single name, or an array of names.
14
+ #
15
+ # This is an internal method, subject to change at any time. Client apps
16
+ # and gems should use the public API (OneApm::Agent.record_metric)
17
+ # instead.
18
+ #
19
+ # There are four ways to use this method:
20
+ #
21
+ # 1. With a numeric value, it will update the Stats objects associated
22
+ # with the given metrics by calling record_data_point(value, aux).
23
+ # aux will be treated in this case as the exclusive time associated
24
+ # with the call being recorded.
25
+ #
26
+ # 2. With a value of :apdex_s, :apdex_t, or :apdex_f, it will treat the
27
+ # associated stats as an Apdex metric, updating it to reflect the
28
+ # occurrence of a transaction falling into the given category.
29
+ # The aux value in this case should be the apdex threshold used in
30
+ # bucketing the request.
31
+ #
32
+ # 3. If a block is given, value and aux will be ignored, and instead the
33
+ # Stats object associated with each named unscoped metric will be
34
+ # yielded to the block for customized update logic.
35
+ #
36
+ # 4. If value is a Stats instance, it will be merged into the Stats
37
+ # associated with each named unscoped metric.
38
+ #
39
+ # If this method is called during a transaction, the metrics will be
40
+ # attached to the Transaction, and not merged into the global set until
41
+ # the end of the transaction.
42
+ #
43
+ # Otherwise, the metrics will be recorded directly into the global set
44
+ # of metrics, under a lock.
45
+ #
46
+ # @api private
47
+ #
48
+ def tl_record_unscoped_metrics(metric_names, value=nil, aux=nil, &blk)
49
+ state = OneApm::TransactionState.tl_get
50
+ record_unscoped_metrics(state, metric_names, value, aux, &blk)
51
+ end
52
+
53
+ def record_unscoped_metrics(state, metric_names, value=nil, aux=nil, &blk)
54
+ txn = state.current_transaction
55
+ if txn
56
+ txn.metrics.record_unscoped(metric_names, value, aux, &blk)
57
+ else
58
+ specs = coerce_to_metric_spec_array(metric_names, nil)
59
+ with_stats_lock do
60
+ @stats_hash.record(specs, value, aux, &blk)
61
+ end
62
+ end
63
+ end
64
+
65
+ # Like tl_record_unscoped_metrics, but records a scoped metric as well.
66
+ #
67
+ # This is an internal method, subject to change at any time. Client apps
68
+ # and gems should use the public API (OneApm::Agent.record_metric)
69
+ # instead.
70
+ #
71
+ # The given scoped_metric will be recoded as both a scoped *and* an
72
+ # unscoped metric. The summary_metrics will be recorded as unscoped
73
+ # metrics only.
74
+ #
75
+ # If called during a transaction, all metrics will be attached to the
76
+ # Transaction, and not merged into the global set of metrics until the
77
+ # end of the transaction.
78
+ #
79
+ # If called outside of a transaction, only the *unscoped* metrics will
80
+ # be recorded, directly into the global set of metrics (under a lock).
81
+ #
82
+ # @api private
83
+ #
84
+ def tl_record_scoped_and_unscoped_metrics(scoped_metric, summary_metrics=nil, value=nil, aux=nil, &blk)
85
+ state = OneApm::TransactionState.tl_get
86
+ record_scoped_and_unscoped_metrics(state, scoped_metric, summary_metrics, value, aux, &blk)
87
+ end
88
+
89
+ def record_scoped_and_unscoped_metrics(state, scoped_metric, summary_metrics=nil, value=nil, aux=nil, &blk)
90
+ txn = state.current_transaction
91
+ if txn
92
+ txn.metrics.record_scoped(scoped_metric, value, aux, &blk)
93
+ txn.metrics.record_unscoped(scoped_metric, value, aux, &blk)
94
+ if summary_metrics
95
+ txn.metrics.record_unscoped(summary_metrics, value, aux, &blk)
96
+ end
97
+ else
98
+ specs = coerce_to_metric_spec_array(scoped_metric, nil)
99
+ if summary_metrics
100
+ specs.concat(coerce_to_metric_spec_array(summary_metrics, nil))
101
+ end
102
+ with_stats_lock do
103
+ @stats_hash.record(specs, value, aux, &blk)
104
+ end
105
+ end
106
+ end
107
+
108
+ # This method is deprecated and not thread safe, and should not be used
109
+ # by any new client code. Use OneApm::Agent.record_metric instead.
110
+ #
111
+ # Lookup the Stats object for a given unscoped metric, returning a new
112
+ # Stats object if one did not exist previously.
113
+ #
114
+ # @api public
115
+ # @deprecated
116
+ #
117
+ def get_stats_no_scope(metric_name)
118
+ get_stats(metric_name, false)
119
+ end
120
+
121
+ # This method is deprecated and not thread safe, and should not be used
122
+ # by any new client code. Use OneApm::Agent.record_metric instead.
123
+ #
124
+ # If scoped_metric_only is true, only a scoped metric is created (used
125
+ # by rendering metrics which by definition are per controller only)
126
+ # Leaving second, unused parameter for compatibility
127
+ #
128
+ # @api public
129
+ # @deprecated
130
+ #
131
+ def get_stats(metric_name, _ = true, scoped_metric_only = false, scope = nil)
132
+ stats = nil
133
+ with_stats_lock do
134
+ if scoped_metric_only
135
+ stats = @stats_hash[OneApm::MetricSpec.new(metric_name, scope)]
136
+ else
137
+ unscoped_spec = OneApm::MetricSpec.new(metric_name)
138
+ unscoped_stats = @stats_hash[unscoped_spec]
139
+ if scope && scope != metric_name
140
+ scoped_spec = OneApm::MetricSpec.new(metric_name, scope)
141
+ scoped_stats = @stats_hash[scoped_spec]
142
+ stats = OneApm::Metrics::ChainedStats.new(scoped_stats, unscoped_stats)
143
+ else
144
+ stats = unscoped_stats
145
+ end
146
+ end
147
+ end
148
+ stats
149
+ end
150
+
151
+ # Returns a stat if one exists, otherwise returns nil.
152
+ def lookup_stats(metric_name, scope_name = '')
153
+ spec = OneApm::MetricSpec.new(metric_name, scope_name)
154
+ with_stats_lock do
155
+ @stats_hash.has_key?(spec) ? @stats_hash[spec] : nil
156
+ end
157
+ end
158
+
159
+ # Helper for recording a straight value into the count
160
+ def tl_record_supportability_metric_count(metric, value)
161
+ real_name = "Supportability/#{metric}"
162
+ tl_record_unscoped_metrics(real_name) do |stat|
163
+ stat.call_count = value
164
+ end
165
+ end
166
+
167
+ def reset!
168
+ with_stats_lock do
169
+ old = @stats_hash
170
+ @stats_hash = StatsHash.new
171
+ old
172
+ end
173
+ end
174
+
175
+ # merge data from previous harvests into this stats engine
176
+ def merge!(other_stats_hash)
177
+ with_stats_lock do
178
+ @stats_hash.merge!(other_stats_hash)
179
+ @stats_hash
180
+ end
181
+ end
182
+
183
+ def merge_transaction_metrics!(txn_metrics, scope)
184
+ with_stats_lock do
185
+ @stats_hash.merge_transaction_metrics!(txn_metrics, scope)
186
+ end
187
+ end
188
+
189
+ def harvest!
190
+ now = Time.now
191
+ snapshot = reset!
192
+ snapshot = apply_rules_to_metric_data(@metric_rules, snapshot)
193
+ snapshot.harvested_at = now
194
+ snapshot
195
+ end
196
+
197
+ def apply_rules_to_metric_data(rules_engine, stats_hash)
198
+ renamed_stats = OneApm::Collector::StatsHash.new(stats_hash.started_at)
199
+ stats_hash.each do |spec, stats|
200
+ new_name = rules_engine.rename(spec.name)
201
+ unless new_name.nil?
202
+ new_spec = OneApm::MetricSpec.new(new_name, spec.scope)
203
+ renamed_stats[new_spec].merge!(stats)
204
+ end
205
+ end
206
+ renamed_stats
207
+ end
208
+
209
+ def coerce_to_metric_spec_array(metric_names_or_specs, scope)
210
+ specs = []
211
+ Array(metric_names_or_specs).map do |name_or_spec|
212
+ case name_or_spec
213
+ when String
214
+ specs << OneApm::MetricSpec.new(name_or_spec)
215
+ specs << OneApm::MetricSpec.new(name_or_spec, scope) if scope
216
+ when OneApm::MetricSpec
217
+ specs << name_or_spec
218
+ end
219
+ end
220
+ specs
221
+ end
222
+
223
+ # For use by test code only.
224
+ def clear_stats
225
+ reset!
226
+ OneApm::Agent::BusyCalculator.reset
227
+ end
228
+
229
+ # Returns all of the metric names of all the stats in the engine.
230
+ # For use by test code only.
231
+ def metrics
232
+ with_stats_lock do
233
+ @stats_hash.keys.map { |spec| spec.to_s }
234
+ end
235
+ end
236
+
237
+ def metric_specs
238
+ with_stats_lock { @stats_hash.keys }
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: utf-8
2
+
3
+ # A Hash-descended class for storing metric data in the OneApm Agent.
4
+ #
5
+ # Keys are OneApm::MetricSpec objects.
6
+ # Values are OneApm::Metrics::Stats objects.
7
+ #
8
+ # Missing keys will be automatically created as empty OneApm::Metrics::Stats
9
+ # instances, so use has_key? explicitly to check for key existence.
10
+ #
11
+ # Note that instances of this class are intended to be append-only with respect
12
+ # to new metrics. That is, you should not attempt to *remove* an entry after it
13
+ # has been added, only update it (create a new instance if you need to start
14
+ # over with a blank slate).
15
+ #
16
+ # This class makes no provisions for safe usage from multiple threads, such
17
+ # measures should be externally provided.
18
+
19
+ require 'one_apm/errors/internal_agent_error'
20
+
21
+ module OneApm
22
+ module Collector
23
+ class StatsHash < ::Hash
24
+
25
+ attr_accessor :started_at, :harvested_at
26
+
27
+ def initialize(started_at=Time.now)
28
+ @started_at = started_at.to_f
29
+ super() { |hash, key| hash[key] = OneApm::Metrics::Stats.new }
30
+ end
31
+
32
+ def marshal_dump
33
+ [@started_at, Hash[self]]
34
+ end
35
+
36
+ def marshal_load(data)
37
+ @started_at = data.shift
38
+ self.merge!(data.shift)
39
+ end
40
+
41
+ def ==(other)
42
+ Hash[self] == Hash[other]
43
+ end
44
+
45
+ class StatsHashLookupError < OneApm::Agent::InternalAgentError
46
+ def initialize(original_error, hash, metric_spec)
47
+ super("Lookup error in StatsHash: #{original_error.class}: #{original_error.message}. Falling back adding #{metric_spec.inspect}")
48
+ end
49
+ end
50
+
51
+ def record(metric_specs, value=nil, aux=nil, &blk)
52
+ Array(metric_specs).each do |metric_spec|
53
+ stats = nil
54
+ begin
55
+ stats = self[metric_spec]
56
+ rescue NoMethodError => e
57
+ # This only happen in the case of a corrupted default_proc
58
+ # Side-step it manually, notice the issue, and carry on....
59
+ OneApm::Agent.instance.error_collector. \
60
+ notice_agent_error(StatsHashLookupError.new(e, self, metric_spec))
61
+
62
+ stats = OneApm::Metrics::Stats.new
63
+ self[metric_spec] = stats
64
+
65
+ # Try to restore the default_proc so we won't continually trip the error
66
+ if respond_to?(:default_proc=)
67
+ self.default_proc = Proc.new { |hash, key| hash[key] = OneApm::Metrics::Stats.new }
68
+ end
69
+ end
70
+
71
+ stats.record(value, aux, &blk)
72
+ end
73
+ end
74
+
75
+ def merge!(other)
76
+ if other.is_a?(StatsHash) && other.started_at < @started_at
77
+ @started_at = other.started_at
78
+ end
79
+ other.each do |key, val|
80
+ merge_or_insert(key, val)
81
+ end
82
+ self
83
+ end
84
+
85
+ def merge_transaction_metrics!(txn_metrics, scope)
86
+ txn_metrics.each_unscoped do |name, stats|
87
+ spec = OneApm::MetricSpec.new(name)
88
+ merge_or_insert(spec, stats)
89
+ end
90
+ txn_metrics.each_scoped do |name, stats|
91
+ spec = OneApm::MetricSpec.new(name, scope)
92
+ merge_or_insert(spec, stats)
93
+ end
94
+ end
95
+
96
+ def merge_or_insert(metric_spec, stats)
97
+ if self.has_key?(metric_spec)
98
+ self[metric_spec].merge!(stats)
99
+ else
100
+ self[metric_spec] = stats
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,429 @@
1
+ # encoding: utf-8
2
+
3
+ require 'forwardable'
4
+ require 'one_apm/support/dotted_hash'
5
+ require 'one_apm/configuration/yaml_source'
6
+ require 'one_apm/configuration/server_source'
7
+ require 'one_apm/configuration/manual_source'
8
+ require 'one_apm/configuration/default_source'
9
+ require 'one_apm/configuration/environment_source'
10
+ require 'one_apm/configuration/high_security_source'
11
+
12
+ module OneApm
13
+ module Configuration
14
+
15
+ MASK_DEFAULTS = {
16
+ :'thread_profiler' => Proc.new { !OneApm::Agent::Threading::BacktraceService.is_supported? },
17
+ :'thread_profiler.enabled' => Proc.new { !OneApm::Agent::Threading::BacktraceService.is_supported? }
18
+ }
19
+
20
+ class Manager
21
+
22
+ attr_reader :stripped_exceptions_whitelist
23
+
24
+ def initialize
25
+ reset_to_defaults
26
+ @callbacks = Hash.new {|hash,key| hash[key] = [] }
27
+
28
+ register_callback(:'strip_exception_messages.whitelist') do |whitelist|
29
+ if whitelist
30
+ @stripped_exceptions_whitelist = parse_constant_list(whitelist).compact
31
+ else
32
+ @stripped_exceptions_whitelist = []
33
+ end
34
+ end
35
+ end
36
+
37
+ # Defining these explicitly saves object allocations that we incur
38
+ # if we use Forwardable and def_delegators.
39
+ def [](key)
40
+ @cache[key]
41
+ end
42
+
43
+ def has_key?(key)
44
+ @cache.has_key?[key]
45
+ end
46
+
47
+ def keys
48
+ @cache.keys
49
+ end
50
+
51
+ def add_config_for_testing(source, level=0)
52
+ raise 'Invalid config type for testing' unless [Hash, OneApm::Support::DottedHash].include?(source.class)
53
+ invoke_callbacks(:add, source)
54
+ @configs_for_testing << [source.freeze, level]
55
+ reset_cache
56
+ log_config(:add, source)
57
+ end
58
+
59
+ def remove_config_type(sym)
60
+ source = case sym
61
+ when :high_security then @high_security_source
62
+ when :environment then @environment_source
63
+ when :server then @server_source
64
+ when :manual then @manual_source
65
+ when :yaml then @yaml_source
66
+ when :default then @default_source
67
+ end
68
+
69
+ remove_config(source)
70
+ end
71
+
72
+ def remove_config(source)
73
+ case source
74
+ when HighSecuritySource then @high_security_source = nil
75
+ when EnvironmentSource then @environment_source = nil
76
+ when ServerSource then @server_source = nil
77
+ when ManualSource then @manual_source = nil
78
+ when YamlSource then @yaml_source = nil
79
+ when DefaultSource then @default_source = nil
80
+ else
81
+ @configs_for_testing.delete_if {|src,lvl| src == source}
82
+ end
83
+
84
+ reset_cache
85
+ invoke_callbacks(:remove, source)
86
+ log_config(:remove, source)
87
+ end
88
+
89
+ def replace_or_add_config(source)
90
+ source.freeze
91
+ was_finished = finished_configuring?
92
+
93
+ invoke_callbacks(:add, source)
94
+ case source
95
+ when HighSecuritySource then @high_security_source = source
96
+ when EnvironmentSource then @environment_source = source
97
+ when ServerSource then @server_source = source
98
+ when ManualSource then @manual_source = source
99
+ when YamlSource then @yaml_source = source
100
+ when DefaultSource then @default_source = source
101
+ else
102
+ OneApm::Agent.logger.warn("Invalid config format; config will be ignored: #{source}")
103
+ end
104
+
105
+ reset_cache
106
+ log_config(:add, source)
107
+
108
+ notify_finished_configuring if !was_finished && finished_configuring?
109
+ end
110
+
111
+ def source(key)
112
+ config_stack.each do |config|
113
+ if config.respond_to?(key.to_sym) || config.has_key?(key.to_sym)
114
+ return config
115
+ end
116
+ end
117
+ end
118
+
119
+ def fetch(key)
120
+ config_stack.each do |config|
121
+ next unless config
122
+ accessor = key.to_sym
123
+
124
+ if config.has_key?(accessor)
125
+ evaluated = evaluate_procs(config[accessor])
126
+
127
+ begin
128
+ return apply_transformations(accessor, evaluated)
129
+ rescue
130
+ next
131
+ end
132
+ end
133
+ end
134
+
135
+ nil
136
+ end
137
+
138
+ def evaluate_procs(value)
139
+ if value.respond_to?(:call)
140
+ instance_eval(&value)
141
+ else
142
+ value
143
+ end
144
+ end
145
+
146
+ def apply_transformations(key, value)
147
+ if transform = transform_from_default(key)
148
+ begin
149
+ transform.call(value)
150
+ rescue => e
151
+ ::OneApm::Agent.logger.error("Error applying transformation for #{key}, falling back to #{value}.", e)
152
+ raise e
153
+ end
154
+ else
155
+ value
156
+ end
157
+ end
158
+
159
+ def transform_from_default(key)
160
+ ::OneApm::Configuration::DefaultSource.transform_for(key)
161
+ end
162
+
163
+ def register_callback(key, &proc)
164
+ @callbacks[key] << proc
165
+ proc.call(@cache[key])
166
+ end
167
+
168
+ def invoke_callbacks(direction, source)
169
+ return unless source
170
+ source.keys.each do |key|
171
+
172
+ if @cache[key] != source[key]
173
+ @callbacks[key].each do |proc|
174
+ if direction == :add
175
+ proc.call(source[key])
176
+ else
177
+ proc.call(@cache[key])
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ def notify_finished_configuring
185
+ OneApm::Agent.instance.events.notify(:finished_configuring)
186
+ end
187
+
188
+ def finished_configuring?
189
+ !@server_source.nil?
190
+ end
191
+
192
+ def flattened
193
+ config_stack.reverse.inject({}) do |flat,layer|
194
+ thawed_layer = layer.to_hash.dup
195
+ thawed_layer.each do |k,v|
196
+ begin
197
+ thawed_layer[k] = instance_eval(&v) if v.respond_to?(:call)
198
+ rescue => e
199
+ ::OneApm::Agent.logger.debug("#{e.class.name} : #{e.message} - when accessing config key #{k}")
200
+ thawed_layer[k] = nil
201
+ end
202
+ thawed_layer.delete(:config)
203
+ end
204
+ flat.merge(thawed_layer.to_hash)
205
+ end
206
+ end
207
+
208
+ def apply_mask(hash)
209
+ MASK_DEFAULTS. \
210
+ select {|_, proc| proc.call}. \
211
+ each {|key, _| hash.delete(key) }
212
+ hash
213
+ end
214
+
215
+ def to_collector_hash
216
+ OneApm::Support::DottedHash.new(apply_mask(flattened)).to_hash.delete_if do |k, v|
217
+ default = DEFAULTS[k]
218
+ if default
219
+ default[:local_only]
220
+ else
221
+ # In our tests, we add totally bogus configs, because testing.
222
+ # In those cases, there will be no default. So we'll just let
223
+ # them through.
224
+ false
225
+ end
226
+ end
227
+ end
228
+
229
+ def app_names
230
+ case OneApm::Agent.config[:app_name]
231
+ when Array then OneApm::Agent.config[:app_name]
232
+ when String then OneApm::Agent.config[:app_name].split(';')
233
+ else []
234
+ end
235
+ end
236
+
237
+ MALFORMED_LABELS_WARNING = "Skipping malformed labels configuration"
238
+ PARSING_LABELS_FAILURE = "Failure during parsing labels. Ignoring and carrying on with connect."
239
+
240
+ MAX_LABEL_COUNT = 64
241
+ MAX_LABEL_LENGTH = 255
242
+
243
+ def parsed_labels
244
+ case OneApm::Agent.config[:labels]
245
+ when String
246
+ parse_labels_from_string
247
+ else
248
+ parse_labels_from_dictionary
249
+ end
250
+ rescue => e
251
+ OneApm::Agent.logger.error(PARSING_LABELS_FAILURE, e)
252
+ []
253
+ end
254
+
255
+ def parse_labels_from_string
256
+ labels = OneApm::Agent.config[:labels]
257
+ label_pairs = break_label_string_into_pairs(labels)
258
+ make_label_hash(label_pairs, labels)
259
+ end
260
+
261
+ def break_label_string_into_pairs(labels)
262
+ stripped_labels = labels.strip
263
+ stripped_labels.split(';').map do |pair|
264
+ pair.split(':').map(&:strip)
265
+ end
266
+ end
267
+
268
+ def valid_label_pairs?(label_pairs)
269
+ label_pairs.all? do |pair|
270
+ pair.length == 2 && valid_label_item?(pair.first) && valid_label_item?(pair.last)
271
+ end
272
+ end
273
+
274
+ def valid_label_item?(item)
275
+ case item
276
+ when String then !item.empty?
277
+ when Numeric then true
278
+ when true then true
279
+ when false then true
280
+ else false
281
+ end
282
+ end
283
+
284
+ def make_label_hash(pairs, labels = nil)
285
+ # This can accept a hash, so force it down to an array of pairs first
286
+ pairs = Array(pairs)
287
+
288
+ unless valid_label_pairs?(pairs)
289
+ OneApm::Agent.logger.warn("#{MALFORMED_LABELS_WARNING}: #{labels||pairs}")
290
+ return []
291
+ end
292
+
293
+ pairs = limit_number_of_labels(pairs)
294
+ pairs = remove_duplicates(pairs)
295
+ pairs.map do |key, value|
296
+ {
297
+ 'label_type' => truncate(key),
298
+ 'label_value' => truncate(value.to_s, key)
299
+ }
300
+ end
301
+ end
302
+
303
+ def truncate(text, key=nil)
304
+ if text.length > MAX_LABEL_LENGTH
305
+ if key
306
+ msg = "The value for the label '#{key}' is longer than the allowed #{MAX_LABEL_LENGTH} and will be truncated. Value = '#{text}'"
307
+ else
308
+ msg = "Label name longer than the allowed #{MAX_LABEL_LENGTH} will be truncated. Name = '#{text}'"
309
+ end
310
+ OneApm::Agent.logger.warn(msg)
311
+ text[0..MAX_LABEL_LENGTH-1]
312
+ else
313
+ text
314
+ end
315
+ end
316
+
317
+ def limit_number_of_labels(pairs)
318
+ if pairs.length > MAX_LABEL_COUNT
319
+ OneApm::Agent.logger.warn("Too many labels defined. Only taking first #{MAX_LABEL_COUNT}")
320
+ pairs[0...64]
321
+ else
322
+ pairs
323
+ end
324
+ end
325
+
326
+ # We only take the last value provided for a given label type key
327
+ def remove_duplicates(pairs)
328
+ grouped_by_type = pairs.group_by(&:first)
329
+ grouped_by_type.values.map(&:last)
330
+ end
331
+
332
+ def parse_labels_from_dictionary
333
+ make_label_hash(OneApm::Agent.config[:labels])
334
+ end
335
+
336
+ # Generally only useful during initial construction and tests
337
+ def reset_to_defaults
338
+ @high_security_source = nil
339
+ @environment_source = EnvironmentSource.new
340
+ @server_source = nil
341
+ @manual_source = nil
342
+ @yaml_source = nil
343
+ @default_source = DefaultSource.new
344
+
345
+ @configs_for_testing = []
346
+
347
+ reset_cache
348
+ end
349
+
350
+ def reset_cache
351
+ @cache = Hash.new {|hash,key| hash[key] = self.fetch(key) }
352
+ end
353
+
354
+ def log_config(direction, source)
355
+ # Just generating this log message (specifically calling
356
+ # flattened.inspect) is expensive enough that we don't want to do it
357
+ # unless we're actually going to be logging the message based on our
358
+ # current log level.
359
+ ::OneApm::Agent.logger.debug do
360
+ "Updating config (#{direction}) from #{source.class}. Results: #{flattened.inspect}"
361
+ end
362
+ end
363
+
364
+ def delete_all_configs_for_testing
365
+ @high_security_source = nil
366
+ @environment_source = nil
367
+ @server_source = nil
368
+ @manual_source = nil
369
+ @yaml_source = nil
370
+ @default_source = nil
371
+ @configs_for_testing = []
372
+ end
373
+
374
+ def num_configs_for_testing
375
+ config_stack.size
376
+ end
377
+
378
+ def config_classes_for_testing
379
+ config_stack.map(&:class)
380
+ end
381
+
382
+ private
383
+
384
+ def config_stack
385
+ stack = [
386
+ @high_security_source,
387
+ @environment_source,
388
+ @server_source,
389
+ @manual_source,
390
+ @yaml_source,
391
+ @default_source
392
+ ]
393
+
394
+ stack.compact!
395
+
396
+ @configs_for_testing.each do |config, at_start|
397
+ if at_start
398
+ stack.insert(0, config)
399
+ else
400
+ stack.push(config)
401
+ end
402
+ end
403
+
404
+ stack
405
+ end
406
+
407
+ def parse_constant_list(list)
408
+ list.split(/\s*,\s*/).map do |class_name|
409
+ const = constantize(class_name)
410
+
411
+ unless const
412
+ OneApm::Agent.logger.warn "Configuration referenced undefined constant: #{class_name}"
413
+ end
414
+
415
+ const
416
+ end
417
+ end
418
+
419
+ def constantize(class_name)
420
+ namespaces = class_name.split('::')
421
+
422
+ namespaces.inject(Object) do |namespace, name|
423
+ return unless namespace
424
+ namespace.const_get(name) if namespace.const_defined?(name)
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end