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