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,153 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/collector/commands/agent_command'
4
+ require 'one_apm/collector/commands/xray_session_collection'
5
+ require 'one_apm/collector/commands/thread_profiler_session'
6
+ require 'one_apm/agent/threading/backtrace_service'
7
+
8
+ module OneApm
9
+ module Collector
10
+ class AgentCommandRouter
11
+ attr_reader :handlers
12
+
13
+ attr_accessor :thread_profiler_session,
14
+ :backtrace_service,
15
+ :xray_session_collection
16
+
17
+ def initialize(event_listener=nil)
18
+ @handlers = Hash.new { |*| Proc.new { |cmd| self.unrecognized_agent_command(cmd) } }
19
+
20
+ @backtrace_service = OneApm::Agent::Threading::BacktraceService.new(event_listener)
21
+
22
+ @thread_profiler_session = OneApm::Collector::Commands::ThreadProfilerSession.new(@backtrace_service)
23
+ @xray_session_collection = OneApm::Collector::Commands::XraySessionCollection.new(@backtrace_service, event_listener)
24
+
25
+ @handlers['restart'] = Proc.new { OneApm::Agent.restart }
26
+ @handlers['start_profiler'] = Proc.new { |cmd| thread_profiler_session.handle_start_command(cmd) }
27
+ @handlers['stop_profiler'] = Proc.new { |cmd| thread_profiler_session.handle_stop_command(cmd) }
28
+ @handlers['active_xray_sessions'] = Proc.new { |cmd| xray_session_collection.handle_active_xray_sessions(cmd) }
29
+
30
+ if event_listener
31
+ event_listener.subscribe(:before_shutdown, &method(:on_before_shutdown))
32
+ end
33
+ end
34
+
35
+ def one_apm_service
36
+ OneApm::Agent.instance.service
37
+ end
38
+
39
+ def check_for_and_handle_agent_commands
40
+ commands = get_agent_commands
41
+
42
+ stop_xray_sessions unless active_xray_command?(commands)
43
+
44
+ results = invoke_commands(commands)
45
+ one_apm_service.agent_command_results(results) unless results.empty?
46
+ end
47
+
48
+ def stop_xray_sessions
49
+ self.xray_session_collection.stop_all_sessions
50
+ end
51
+
52
+ def active_xray_command?(commands)
53
+ commands.any? {|command| command.name == 'active_xray_sessions'}
54
+ end
55
+
56
+ def on_before_shutdown(*args)
57
+ if self.thread_profiler_session.running?
58
+ self.thread_profiler_session.stop(true)
59
+ end
60
+ end
61
+
62
+ def harvest!
63
+ profiles = []
64
+ profiles += harvest_from_xray_session_collection
65
+ profiles += harvest_from_thread_profiler_session
66
+ log_profiles(profiles)
67
+ profiles
68
+ end
69
+
70
+ # We don't currently support merging thread profiles that failed to send
71
+ # back into the AgentCommandRouter, so we just no-op this method.
72
+ # Same with reset! - we don't support asynchronous cancellation of a
73
+ # running thread profile or X-Ray session currently.
74
+ def merge!(*args); end
75
+ def reset!; end
76
+
77
+ def harvest_from_xray_session_collection
78
+ self.xray_session_collection.harvest_thread_profiles
79
+ end
80
+
81
+ def harvest_from_thread_profiler_session
82
+ if self.thread_profiler_session.ready_to_harvest?
83
+ self.thread_profiler_session.stop(true)
84
+ [self.thread_profiler_session.harvest]
85
+ else
86
+ []
87
+ end
88
+ end
89
+
90
+ def log_profiles(profiles)
91
+ if profiles.empty?
92
+ ::OneApm::Agent.logger.debug "No thread profiles with data found to send."
93
+ else
94
+ profile_descriptions = profiles.map { |p| p.to_log_description }
95
+ ::OneApm::Agent.logger.debug "Sending thread profiles [#{profile_descriptions.join(", ")}]"
96
+ end
97
+ end
98
+
99
+ def get_agent_commands
100
+ commands = one_apm_service.get_agent_commands
101
+ OneApm::Agent.logger.info "Received get_agent_commands = #{commands.inspect}" if commands.any?
102
+ commands.map {|collector_command| OneApm::Collector::Commands::AgentCommand.new(collector_command)}
103
+ end
104
+
105
+ def invoke_commands(agent_commands)
106
+ results = {}
107
+
108
+ agent_commands.each do |agent_command|
109
+ results[agent_command.id.to_s] = invoke_command(agent_command)
110
+ end
111
+
112
+ results
113
+ end
114
+
115
+ class AgentCommandError < StandardError
116
+ end
117
+
118
+ def invoke_command(agent_command)
119
+ begin
120
+ call_handler_for(agent_command)
121
+ return success
122
+ rescue AgentCommandError => e
123
+ OneApm::Agent.logger.debug(e)
124
+ error(e)
125
+ end
126
+ end
127
+
128
+ SUCCESS_RESULT = {}.freeze
129
+ ERROR_KEY = "error"
130
+
131
+ def success
132
+ SUCCESS_RESULT
133
+ end
134
+
135
+ def error(err)
136
+ { ERROR_KEY => err.message }
137
+ end
138
+
139
+ def call_handler_for(agent_command)
140
+ handler = select_handler(agent_command)
141
+ handler.call(agent_command)
142
+ end
143
+
144
+ def select_handler(agent_command)
145
+ @handlers[agent_command.name]
146
+ end
147
+
148
+ def unrecognized_agent_command(agent_command)
149
+ OneApm::Agent.logger.debug("Unrecognized agent command #{agent_command.inspect}")
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ require 'one_apm/support/event_buffer/sized_buffer'
4
+
5
+ module OneApm
6
+ module Collector
7
+ class CustomEventAggregator
8
+ include OneApm::Coerce
9
+
10
+ TYPE = 'type'.freeze
11
+ TIMESTAMP = 'timestamp'.freeze
12
+ EVENT_PARAMS_CTX = 'recording custom event'.freeze
13
+ EVENT_TYPE_REGEX = /^[a-zA-Z0-9:_ ]+$/.freeze
14
+
15
+ DEFAULT_CAPACITY_KEY = :'custom_insights_events.max_samples_stored'
16
+
17
+ def initialize
18
+ @lock = Mutex.new
19
+ @buffer = OneApm::Agent::SampledBuffer.new(OneApm::Agent.config[DEFAULT_CAPACITY_KEY])
20
+ @type_strings = Hash.new { |hash, key| hash[key] = key.to_s.freeze }
21
+ register_config_callbacks
22
+ end
23
+
24
+ def register_config_callbacks
25
+ OneApm::Agent.config.register_callback(DEFAULT_CAPACITY_KEY) do |max_samples|
26
+ OneApm::Agent.logger.debug "CustomEventAggregator max_samples set to #{max_samples}"
27
+ @lock.synchronize do
28
+ @buffer.capacity = max_samples
29
+ end
30
+ end
31
+ end
32
+
33
+ def record(type, attributes)
34
+ type = @type_strings[type]
35
+ unless type =~ EVENT_TYPE_REGEX
36
+ note_dropped_event(type)
37
+ return false
38
+ end
39
+
40
+ event = [
41
+ { TYPE => type, TIMESTAMP => Time.now.to_i },
42
+ event_params(attributes, EVENT_PARAMS_CTX)
43
+ ]
44
+
45
+ stored = @lock.synchronize do
46
+ @buffer.append(event)
47
+ end
48
+ stored
49
+ end
50
+
51
+ def harvest!
52
+ results = []
53
+ drop_count = 0
54
+ @lock.synchronize do
55
+ results.concat(@buffer.to_a)
56
+ drop_count += @buffer.num_dropped
57
+ @buffer.reset!
58
+ end
59
+ note_dropped_events(results.size, drop_count)
60
+ results
61
+ end
62
+
63
+ def note_dropped_events(captured_count, dropped_count)
64
+ total_count = captured_count + dropped_count
65
+ if dropped_count > 0
66
+ OneApm::Agent.logger.warn("Dropped #{dropped_count} custom events out of #{total_count}.")
67
+ end
68
+ engine = OneApm::Agent.instance.stats_engine
69
+ engine.tl_record_supportability_metric_count("Events/Customer/Seen" , total_count)
70
+ engine.tl_record_supportability_metric_count("Events/Customer/Sent" , captured_count)
71
+ engine.tl_record_supportability_metric_count("Events/Customer/Dropped", dropped_count)
72
+ end
73
+
74
+ def merge!(events)
75
+ @lock.synchronize do
76
+ events.each do |event|
77
+ @buffer.append(event)
78
+ end
79
+ end
80
+ end
81
+
82
+ def reset!
83
+ @lock.synchronize { @buffer.reset! }
84
+ end
85
+
86
+ def note_dropped_event(type)
87
+ ::OneApm::Agent.logger.log_once(:warn, "dropping_event_of_type:#{type}",
88
+ "Invalid event type name '#{type}', not recording.")
89
+ @buffer.note_dropped
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,349 @@
1
+ # encoding: utf-8
2
+
3
+ module OneApm
4
+ module Collector
5
+ class ErrorCollector
6
+ include OneApm::CollectionHelper
7
+
8
+ # Defined the methods that need to be stubbed out when the agent is disabled
9
+ module Shim
10
+ def notice_error(*args); end
11
+ end
12
+
13
+ # Maximum possible length of the queue - defaults to 20, may be
14
+ # made configurable in the future. This is a tradeoff between
15
+ # memory and data retention
16
+ MAX_ERROR_QUEUE_LENGTH = 20 unless defined? MAX_ERROR_QUEUE_LENGTH
17
+
18
+ attr_accessor :errors
19
+
20
+ # Returns a new error collector
21
+ def initialize
22
+ @errors = []
23
+
24
+ # lookup of exception class names to ignore. Hash for fast access
25
+ @ignore = {}
26
+
27
+ initialize_ignored_errors(Agent.config[:'error_collector.ignore_errors'])
28
+ @lock = Mutex.new
29
+
30
+ Agent.config.register_callback(:'error_collector.enabled') do |config_enabled|
31
+ ::OneApm::Agent.logger.debug "Errors will #{config_enabled ? '' : 'not '}be sent to the OneApm service."
32
+ end
33
+ Agent.config.register_callback(:'error_collector.ignore_errors') do |ignore_errors|
34
+ initialize_ignored_errors(ignore_errors)
35
+ end
36
+ end
37
+
38
+ def initialize_ignored_errors(ignore_errors)
39
+ @ignore.clear
40
+ ignore_errors = ignore_errors.split(",") if ignore_errors.is_a? String
41
+ ignore_errors.each { |error| error.strip! }
42
+ ignore(ignore_errors)
43
+ end
44
+
45
+ def enabled?
46
+ Agent.config[:'error_collector.enabled']
47
+ end
48
+
49
+ def disabled?
50
+ !enabled?
51
+ end
52
+
53
+ # We store the passed block in both an ivar on the class, and implicitly
54
+ # within the body of the ignore_filter_proc method intentionally here.
55
+ # The define_method trick is needed to get around the fact that users may
56
+ # call 'return' from within their filter blocks, which would otherwise
57
+ # result in a LocalJumpError.
58
+ #
59
+ # The raw block is also stored in an instance variable so that we can
60
+ # return it later in its original form.
61
+ #
62
+ # This is all done at the class level in order to avoid the case where
63
+ # the user sets up an ignore filter on one instance of the ErrorCollector,
64
+ # and then that instance subsequently gets discarded during agent startup.
65
+ # (For example, if the agent is initially disabled, and then gets enabled
66
+ # via a call to start later on.)
67
+ #
68
+ def self.ignore_error_filter=(block)
69
+ @ignore_filter = block
70
+ if block
71
+ define_method(:ignore_filter_proc, &block)
72
+ elsif method_defined?(:ignore_filter_proc)
73
+ undef :ignore_filter_proc
74
+ end
75
+ @ignore_filter
76
+ end
77
+
78
+ def self.ignore_error_filter
79
+ @ignore_filter
80
+ end
81
+
82
+ # errors is an array of Exception Class Names
83
+ def ignore(errors)
84
+ errors.each do |error|
85
+ @ignore[error] = true
86
+ ::OneApm::Agent.logger.debug("Ignoring errors of type '#{error}'")
87
+ end
88
+ end
89
+
90
+ # This module was extracted from the notice_error method - it is
91
+ # internally tested and can be refactored without major issues.
92
+ module NoticeError
93
+ # Checks the provided error against the error filter, if there
94
+ # is an error filter
95
+ def filtered_by_error_filter?(error)
96
+ respond_to?(:ignore_filter_proc) && !ignore_filter_proc(error)
97
+ end
98
+
99
+ # Checks the array of error names and the error filter against
100
+ # the provided error
101
+ def filtered_error?(error)
102
+ @ignore[error.class.name] || filtered_by_error_filter?(error)
103
+ end
104
+
105
+ # an error is ignored if it is nil or if it is filtered
106
+ def error_is_ignored?(error)
107
+ error && filtered_error?(error)
108
+ rescue => e
109
+ OneApm::Agent.logger.error("Error '#{error}' will NOT be ignored. Exception '#{e}' while determining whether to ignore or not.", e)
110
+ false
111
+ end
112
+
113
+ def seen?(txn, exception)
114
+ error_ids = txn.nil? ? [] : txn.noticed_error_ids
115
+ error_ids.include?(exception.object_id)
116
+ end
117
+
118
+ def tag_as_seen(state, exception)
119
+ txn = state.current_transaction
120
+ txn.noticed_error_ids << exception.object_id if txn
121
+ end
122
+
123
+ def blamed_metric_name(txn, options)
124
+ if options[:metric] && options[:metric] != OneApm::Transaction::UNKNOWN_METRIC
125
+ "Errors/#{options[:metric]}"
126
+ else
127
+ "Errors/#{txn.best_name}" if txn
128
+ end
129
+ end
130
+
131
+ def aggregated_metric_names(txn)
132
+ metric_names = ["Errors/all"]
133
+ return metric_names unless txn
134
+
135
+ if txn.recording_web_transaction?
136
+ metric_names << "Errors/allWeb"
137
+ else
138
+ metric_names << "Errors/allOther"
139
+ end
140
+
141
+ metric_names
142
+ end
143
+
144
+ # Increments a statistic that tracks total error rate
145
+ def increment_error_count!(state, exception, options={})
146
+ txn = state.current_transaction
147
+
148
+ metric_names = aggregated_metric_names(txn)
149
+ blamed_metric = blamed_metric_name(txn, options)
150
+ metric_names << blamed_metric if blamed_metric
151
+
152
+ stats_engine = OneApm::Agent.instance.stats_engine
153
+ stats_engine.record_unscoped_metrics(state, metric_names) do |stats|
154
+ stats.increment_count
155
+ end
156
+ end
157
+
158
+ def skip_notice_error?(state, exception)
159
+ disabled? ||
160
+ error_is_ignored?(exception) ||
161
+ exception.nil? ||
162
+ seen?(state.current_transaction, exception)
163
+ end
164
+
165
+ # acts just like Hash#fetch, but deletes the key from the hash
166
+ def fetch_from_options(options, key, default=nil)
167
+ options.delete(key) || default
168
+ end
169
+
170
+ # returns some basic option defaults pulled from the provided
171
+ # options hash
172
+ def uri_ref_and_root(options)
173
+ {
174
+ :request_uri => fetch_from_options(options, :uri, ''),
175
+ :request_referer => fetch_from_options(options, :referer, ''),
176
+ :rails_root => OneApm::Probe.instance.root
177
+ }
178
+ end
179
+
180
+ # If anything else is left over, we treat it like a custom param
181
+ def custom_params_from_opts(options)
182
+ # If anything else is left over, treat it like a custom param:
183
+ if Agent.config[:'error_collector.capture_attributes']
184
+ fetch_from_options(options, :custom_params, {}).merge(options)
185
+ else
186
+ {}
187
+ end
188
+ end
189
+
190
+ # takes the request parameters out of the options hash, and
191
+ # returns them if we are capturing parameters, otherwise
192
+ # returns nil
193
+ def request_params_from_opts(options)
194
+ value = options.delete(:request_params)
195
+ if Agent.config[:capture_params]
196
+ value
197
+ else
198
+ nil
199
+ end
200
+ end
201
+
202
+ # normalizes the request and custom parameters before attaching
203
+ # them to the error. See OneApm::CollectionHelper#normalize_params
204
+ def normalized_request_and_custom_params(options)
205
+ {
206
+ :request_params => normalize_params(request_params_from_opts(options)),
207
+ :custom_params => normalize_params(custom_params_from_opts(options))
208
+ }
209
+ end
210
+
211
+ # Merges together many of the options into something that can
212
+ # actually be attached to the error
213
+ def error_params_from_options(options)
214
+ uri_ref_and_root(options).merge(normalized_request_and_custom_params(options))
215
+ end
216
+
217
+ # calls a method on an object, if it responds to it - used for
218
+ # detection and soft fail-safe. Returns nil if the method does
219
+ # not exist
220
+ def sense_method(object, method)
221
+ object.send(method) if object.respond_to?(method)
222
+ end
223
+
224
+ # extracts a stack trace from the exception for debugging purposes
225
+ def extract_stack_trace(exception)
226
+ actual_exception = sense_method(exception, 'original_exception') || exception
227
+ if bt = sense_method(actual_exception, 'backtrace')
228
+ bt.reject! { |t| t.include?('one_apm') }
229
+ bt
230
+ else
231
+ '<no stack trace>'
232
+ end
233
+ end
234
+
235
+ # extracts a bunch of information from the exception to include
236
+ # in the noticed error - some may or may not be available, but
237
+ # we try to include all of it
238
+ def exception_info(exception)
239
+ {
240
+ :file_name => sense_method(exception, 'file_name'),
241
+ :line_number => sense_method(exception, 'line_number'),
242
+ :stack_trace => extract_stack_trace(exception)
243
+ }
244
+ end
245
+
246
+ # checks the size of the error queue to make sure we are under
247
+ # the maximum limit, and logs a warning if we are over the limit.
248
+ def over_queue_limit?(message)
249
+ over_limit = (@errors.reject{|err| err.is_internal}.length >= MAX_ERROR_QUEUE_LENGTH)
250
+ ::OneApm::Agent.logger.warn("The error reporting queue has reached #{MAX_ERROR_QUEUE_LENGTH}. The error detail for this and subsequent errors will not be transmitted to OneApm until the queued errors have been sent: #{message}") if over_limit
251
+ over_limit
252
+ end
253
+
254
+ # Synchronizes adding an error to the error queue, and checks if
255
+ # the error queue is too long - if so, we drop the error on the
256
+ # floor after logging a warning.
257
+ def add_to_error_queue(noticed_error)
258
+ @lock.synchronize do
259
+ if !over_queue_limit?(noticed_error.message) && !@errors.include?(noticed_error)
260
+ @errors << noticed_error
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ include NoticeError
267
+
268
+
269
+ # Notice the error with the given available options:
270
+ #
271
+ # * <tt>:uri</tt> => The request path, minus any request params or query string.
272
+ # * <tt>:referer</tt> => The URI of the referer
273
+ # * <tt>:metric</tt> => The metric name associated with the transaction
274
+ # * <tt>:request_params</tt> => Request parameters, already filtered if necessary
275
+ # * <tt>:custom_params</tt> => Custom parameters
276
+ #
277
+ # If anything is left over, it's added to custom params
278
+ # If exception is nil, the error count is bumped and no traced error is recorded
279
+ def notice_error(exception, options={}) #THREAD_LOCAL_ACCESS
280
+ state = ::OneApm::TransactionState.tl_get
281
+
282
+ return if skip_notice_error?(state, exception)
283
+ tag_as_seen(state, exception)
284
+
285
+ increment_error_count!(state, exception, options)
286
+ OneApm::Agent.instance.events.notify(:notice_error, exception, options)
287
+
288
+ action_path = fetch_from_options(options, :metric, "")
289
+ exception_options = error_params_from_options(options).merge(exception_info(exception))
290
+ add_to_error_queue(OneApm::NoticedError.new(action_path, exception_options, exception))
291
+
292
+ exception
293
+ rescue => e
294
+ ::OneApm::Agent.logger.warn("Failure when capturing error '#{exception}':", e)
295
+ end
296
+
297
+ # *Use sparingly for difficult to track bugs.*
298
+ #
299
+ # Track internal agent errors for communication back to OneApm.
300
+ # To use, make a specific subclass of OneApm::Agent::InternalAgentError,
301
+ # then pass an instance of it to this method when your problem occurs.
302
+ #
303
+ # Limits are treated differently for these errors. We only gather one per
304
+ # class per harvest, disregarding (and not impacting) the app error queue
305
+ # limit.
306
+ def notice_agent_error(exception)
307
+ return unless exception.class < OneApm::Agent::InternalAgentError
308
+
309
+ # Log 'em all!
310
+ OneApm::Agent.logger.info(exception)
311
+
312
+ @lock.synchronize do
313
+ # Already seen this class once? Bail!
314
+ return if @errors.any? { |err| err.exception_class_name == exception.class.name }
315
+
316
+ trace = exception.backtrace || caller.dup
317
+ noticed_error = OneApm::NoticedError.new("OneApm/AgentError",
318
+ {:stack_trace => trace},
319
+ exception)
320
+ @errors << noticed_error
321
+ end
322
+ rescue => e
323
+ OneApm::Agent.logger.info("Unable to capture internal agent error due to an exception:", e)
324
+ end
325
+
326
+ def merge!(errors)
327
+ errors.each do |error|
328
+ add_to_error_queue(error)
329
+ end
330
+ end
331
+
332
+ # Get the errors currently queued up. Unsent errors are left
333
+ # over from a previous unsuccessful attempt to send them to the server.
334
+ def harvest!
335
+ @lock.synchronize do
336
+ errors = @errors
337
+ @errors = []
338
+ errors
339
+ end
340
+ end
341
+
342
+ def reset!
343
+ @lock.synchronize do
344
+ @errors = []
345
+ end
346
+ end
347
+ end
348
+ end
349
+ end