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,181 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
require 'one_apm/agent/inbound_request_monitor'
|
6
|
+
require 'one_apm/agent/threading/agent_thread'
|
7
|
+
require 'one_apm/transaction/transaction_state'
|
8
|
+
|
9
|
+
module OneApm
|
10
|
+
module Agent
|
11
|
+
class CrossAppMonitor < InboundRequestMonitor
|
12
|
+
|
13
|
+
ONEAPM_ID_HEADER = 'X-OneApm-ID'
|
14
|
+
ONEAPM_TXN_HEADER = 'X-OneApm-Transaction'
|
15
|
+
ONEAPM_APPDATA_HEADER = 'X-OneApm-App-Data'
|
16
|
+
|
17
|
+
ONEAPM_ID_HEADER_KEY = 'HTTP_X_ONEAPM_ID'.freeze
|
18
|
+
ONEAPM_TXN_HEADER_KEY = 'HTTP_X_ONEAPM_TRANSACTION'.freeze
|
19
|
+
CONTENT_LENGTH_HEADER_KEY = 'HTTP_CONTENT_LENGTH'.freeze
|
20
|
+
|
21
|
+
def on_finished_configuring(events)
|
22
|
+
register_event_listeners(events)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Expected sequence of events:
|
26
|
+
# :before_call will save our cross application request id to the thread
|
27
|
+
# :after_call will write our response headers/metrics and clean up the thread
|
28
|
+
def register_event_listeners(events)
|
29
|
+
OneApm::Agent.logger.
|
30
|
+
debug("Wiring up Cross Application Tracing to events after finished configuring")
|
31
|
+
|
32
|
+
events.subscribe(:before_call) do |env| #THREAD_LOCAL_ACCESS
|
33
|
+
if should_process_request(env)
|
34
|
+
state = OneApm::TransactionState.tl_get
|
35
|
+
|
36
|
+
save_client_cross_app_id(state, env)
|
37
|
+
save_referring_transaction_info(state, env)
|
38
|
+
set_transaction_custom_parameters(state)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
events.subscribe(:after_call) do |env, (_status_code, headers, _body)| #THREAD_LOCAL_ACCESS
|
43
|
+
state = OneApm::TransactionState.tl_get
|
44
|
+
|
45
|
+
insert_response_header(state, env, headers)
|
46
|
+
end
|
47
|
+
|
48
|
+
events.subscribe(:notice_error) do |_, options| #THREAD_LOCAL_ACCESS
|
49
|
+
state = OneApm::TransactionState.tl_get
|
50
|
+
|
51
|
+
set_error_custom_parameters(state, options)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def save_client_cross_app_id(state, request_headers)
|
56
|
+
state.client_cross_app_id = decoded_id(request_headers)
|
57
|
+
end
|
58
|
+
|
59
|
+
def clear_client_cross_app_id(state)
|
60
|
+
state.client_cross_app_id = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def save_referring_transaction_info(state, request_headers)
|
64
|
+
txn_header = request_headers[ONEAPM_TXN_HEADER_KEY] or return
|
65
|
+
txn_info = deserialize_header(txn_header, ONEAPM_TXN_HEADER)
|
66
|
+
state.referring_transaction_info = txn_info
|
67
|
+
end
|
68
|
+
|
69
|
+
def client_referring_transaction_guid(state)
|
70
|
+
info = state.referring_transaction_info or return nil
|
71
|
+
return info[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
def client_referring_transaction_record_flag(state)
|
75
|
+
info = state.referring_transaction_info or return nil
|
76
|
+
return info[1]
|
77
|
+
end
|
78
|
+
|
79
|
+
def client_referring_transaction_trip_id(state)
|
80
|
+
info = state.referring_transaction_info or return nil
|
81
|
+
return info[2].is_a?(String) && info[2]
|
82
|
+
end
|
83
|
+
|
84
|
+
def client_referring_transaction_path_hash(state)
|
85
|
+
info = state.referring_transaction_info or return nil
|
86
|
+
return info[3].is_a?(String) && info[3]
|
87
|
+
end
|
88
|
+
|
89
|
+
def insert_response_header(state, request_headers, response_headers)
|
90
|
+
unless state.client_cross_app_id.nil?
|
91
|
+
txn = state.current_transaction
|
92
|
+
unless txn.nil?
|
93
|
+
txn.freeze_name_and_execute_if_not_ignored do
|
94
|
+
timings = state.timings
|
95
|
+
content_length = content_length_from_request(request_headers)
|
96
|
+
|
97
|
+
set_response_headers(state, response_headers, timings, content_length)
|
98
|
+
set_metrics(state.client_cross_app_id, timings)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
clear_client_cross_app_id(state)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def should_process_request(request_headers)
|
106
|
+
return cross_app_enabled? && trusts?(request_headers)
|
107
|
+
end
|
108
|
+
|
109
|
+
def cross_app_enabled?
|
110
|
+
OneApm::Agent::CrossAppTracing.cross_app_enabled?
|
111
|
+
end
|
112
|
+
|
113
|
+
# Expects an ID of format "12#345", and will only accept that!
|
114
|
+
def trusts?(request)
|
115
|
+
id = decoded_id(request)
|
116
|
+
split_id = id.match(/(\d+)#\d+/)
|
117
|
+
return false if split_id.nil?
|
118
|
+
|
119
|
+
OneApm::Agent.config[:trusted_account_ids].include?(split_id.captures.first.to_i)
|
120
|
+
end
|
121
|
+
|
122
|
+
def set_response_headers(state, response_headers, timings, content_length)
|
123
|
+
response_headers[ONEAPM_APPDATA_HEADER] = build_payload(state, timings, content_length)
|
124
|
+
end
|
125
|
+
|
126
|
+
def build_payload(state, timings, content_length)
|
127
|
+
payload = [
|
128
|
+
OneApm::Agent.config[:cross_process_id],
|
129
|
+
timings.transaction_name,
|
130
|
+
timings.queue_time_in_seconds.to_f,
|
131
|
+
timings.app_time_in_seconds.to_f,
|
132
|
+
content_length,
|
133
|
+
state.request_guid
|
134
|
+
]
|
135
|
+
payload = obfuscator.obfuscate(OneApm::JSONWrapper.dump(payload))
|
136
|
+
end
|
137
|
+
|
138
|
+
def set_transaction_custom_parameters(state)
|
139
|
+
# We expect to get the before call to set the id (if we have it) before
|
140
|
+
# this, and then write our custom parameter when the transaction starts
|
141
|
+
OneApm::Agent.add_custom_parameters(:client_cross_process_id => state.client_cross_app_id) if state.client_cross_app_id
|
142
|
+
|
143
|
+
referring_guid = client_referring_transaction_guid(state)
|
144
|
+
if referring_guid
|
145
|
+
OneApm::Agent.add_custom_parameters(:referring_transaction_guid => referring_guid)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def set_error_custom_parameters(state, options)
|
150
|
+
options[:client_cross_process_id] = state.client_cross_app_id if state.client_cross_app_id
|
151
|
+
end
|
152
|
+
|
153
|
+
def set_metrics(id, timings)
|
154
|
+
metric_name = "ClientApplication/#{id}/all"
|
155
|
+
OneApm::Agent.record_metric(metric_name, timings.app_time_in_seconds)
|
156
|
+
end
|
157
|
+
|
158
|
+
def decoded_id(request)
|
159
|
+
encoded_id = request[ONEAPM_ID_HEADER_KEY]
|
160
|
+
return "" if encoded_id.nil?
|
161
|
+
|
162
|
+
obfuscator.deobfuscate(encoded_id)
|
163
|
+
end
|
164
|
+
|
165
|
+
def content_length_from_request(request)
|
166
|
+
request[CONTENT_LENGTH_HEADER_KEY] || -1
|
167
|
+
end
|
168
|
+
|
169
|
+
def hash_transaction_name(identifier)
|
170
|
+
Digest::MD5.digest(identifier).unpack("@12N").first & 0xffffffff
|
171
|
+
end
|
172
|
+
|
173
|
+
def path_hash(txn_name, seed)
|
174
|
+
rotated = ((seed << 1) | (seed >> 31)) & 0xffffffff
|
175
|
+
app_name = OneApm::Agent.config.app_names.first
|
176
|
+
identifier = "#{app_name};#{txn_name}"
|
177
|
+
sprintf("%08x", rotated ^ hash_transaction_name(identifier))
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module OneApm
|
4
|
+
module Agent
|
5
|
+
module CrossAppTracing
|
6
|
+
|
7
|
+
# Exception raised if there is a problem with cross app transactions.
|
8
|
+
class Error < RuntimeError; end
|
9
|
+
|
10
|
+
# The cross app response header for "outgoing" calls
|
11
|
+
OA_APPDATA_HEADER = 'X-OneApm-App-Data'
|
12
|
+
|
13
|
+
# The cross app id header for "outgoing" calls
|
14
|
+
OA_ID_HEADER = 'X-OneApm-ID'
|
15
|
+
|
16
|
+
# The cross app transaction header for "outgoing" calls
|
17
|
+
OA_TXN_HEADER = 'X-OneApm-Transaction'
|
18
|
+
|
19
|
+
# The cross app synthetics header
|
20
|
+
OA_SYNTHETICS_HEADER = 'X-OneApm-Synthetics'
|
21
|
+
|
22
|
+
# The index of the transaction GUID in the appdata header of responses
|
23
|
+
APPDATA_TXN_GUID_INDEX = 5
|
24
|
+
|
25
|
+
|
26
|
+
###############
|
27
|
+
module_function
|
28
|
+
###############
|
29
|
+
|
30
|
+
# Send the given +request+, adding metrics appropriate to the
|
31
|
+
# response when it comes back.
|
32
|
+
#
|
33
|
+
# See the documentation for +start_trace+ for an explanation of what
|
34
|
+
# +request+ should look like.
|
35
|
+
#
|
36
|
+
def tl_trace_http_request(request)
|
37
|
+
state = OneApm::TransactionState.tl_get
|
38
|
+
return yield unless state.is_execution_traced?
|
39
|
+
|
40
|
+
# It's important to set t0 outside the ensured block, otherwise there's
|
41
|
+
# a race condition if we raise after begin but before t0's set.
|
42
|
+
t0 = Time.now
|
43
|
+
begin
|
44
|
+
segment = start_trace(state, t0, request)
|
45
|
+
response = yield
|
46
|
+
ensure
|
47
|
+
finish_trace(state, t0, segment, request, response)
|
48
|
+
end
|
49
|
+
|
50
|
+
return response
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set up the necessary state for cross-application tracing before the
|
54
|
+
# given +request+ goes out.
|
55
|
+
#
|
56
|
+
# The +request+ object passed in must respond to the following methods:
|
57
|
+
#
|
58
|
+
# * type - Return a String describing the underlying library being used
|
59
|
+
# to make the request (e.g. 'Net::HTTP' or 'Typhoeus')
|
60
|
+
# * host - Return a String with the hostname or IP of the host being
|
61
|
+
# communicated with.
|
62
|
+
# * method - Return a String with the HTTP method name for this request
|
63
|
+
# * [](key) - Lookup an HTTP request header by name
|
64
|
+
# * []=(key, val) - Set an HTTP request header by name
|
65
|
+
# * uri - Full URI of the request
|
66
|
+
#
|
67
|
+
# This method returns the transaction segment if it was sucessfully pushed.
|
68
|
+
def start_trace(state, t0, request)
|
69
|
+
inject_request_headers(state, request) if cross_app_enabled?
|
70
|
+
stack = state.traced_method_stack
|
71
|
+
segment = stack.push_frame(state, :http_request, t0)
|
72
|
+
|
73
|
+
return segment
|
74
|
+
rescue => err
|
75
|
+
OneApm::Agent.logger.error "Uncaught exception while tracing HTTP request", err
|
76
|
+
return nil
|
77
|
+
rescue Exception => e
|
78
|
+
OneApm::Agent.logger.debug "Unexpected exception raised while tracing HTTP request", e
|
79
|
+
|
80
|
+
raise e
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Finish tracing the HTTP +request+ that started at +t0+ with the information in
|
85
|
+
# +response+ and the given +http+ connection.
|
86
|
+
#
|
87
|
+
# The +request+ must conform to the same interface described in the documentation
|
88
|
+
# for +start_trace+.
|
89
|
+
#
|
90
|
+
# The +response+ must respond to the following methods:
|
91
|
+
#
|
92
|
+
# * [](key) - Reads response headers.
|
93
|
+
# * to_hash - Converts response headers to a Hash
|
94
|
+
#
|
95
|
+
def finish_trace(state, t0, segment, request, response)
|
96
|
+
unless t0
|
97
|
+
OneApm::Agent.logger.error("HTTP request trace finished without start time. This is probably an agent bug.")
|
98
|
+
return
|
99
|
+
end
|
100
|
+
|
101
|
+
t1 = Time.now
|
102
|
+
duration = t1.to_f - t0.to_f
|
103
|
+
|
104
|
+
begin
|
105
|
+
if request
|
106
|
+
# Figure out which metrics we need to report based on the request and response
|
107
|
+
# The last (most-specific) one is scoped.
|
108
|
+
metrics = metrics_for(request, response)
|
109
|
+
scoped_metric = metrics.pop
|
110
|
+
|
111
|
+
stats_engine.record_scoped_and_unscoped_metrics(
|
112
|
+
state, scoped_metric, metrics, duration)
|
113
|
+
|
114
|
+
# If we don't have segment, something failed during start_trace so
|
115
|
+
# the current segment isn't the HTTP call it should have been.
|
116
|
+
if segment
|
117
|
+
segment.name = scoped_metric
|
118
|
+
add_transaction_trace_parameters(request, response)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
ensure
|
122
|
+
# If we have a segment, always pop the traced method stack to avoid
|
123
|
+
# an inconsistent state, which prevents tracing of whole transaction.
|
124
|
+
if segment
|
125
|
+
stack = state.traced_method_stack
|
126
|
+
stack.pop_frame(state, segment, scoped_metric, t1)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
rescue OneApm::Agent::CrossAppTracing::Error => err
|
130
|
+
OneApm::Agent.logger.debug "while cross app tracing", err
|
131
|
+
rescue => err
|
132
|
+
OneApm::Agent.logger.error "Uncaught exception while finishing an HTTP request trace", err
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return +true+ if cross app tracing is enabled in the config.
|
136
|
+
def cross_app_enabled?
|
137
|
+
valid_cross_process_id? &&
|
138
|
+
valid_encoding_key? &&
|
139
|
+
cross_application_tracer_enabled?
|
140
|
+
end
|
141
|
+
|
142
|
+
def valid_cross_process_id?
|
143
|
+
OneApm::Agent.config[:cross_process_id] && OneApm::Agent.config[:cross_process_id].length > 0
|
144
|
+
end
|
145
|
+
|
146
|
+
def valid_encoding_key?
|
147
|
+
OneApm::Agent.config[:encoding_key] && OneApm::Agent.config[:encoding_key].length > 0
|
148
|
+
end
|
149
|
+
|
150
|
+
def cross_application_tracer_enabled?
|
151
|
+
OneApm::Agent.config[:"cross_application_tracer.enabled"]
|
152
|
+
end
|
153
|
+
|
154
|
+
# Fetcher for the cross app encoding key. Raises a
|
155
|
+
# OneApm::Agent::CrossAppTracing::Error if the key isn't configured.
|
156
|
+
def cross_app_encoding_key
|
157
|
+
OneApm::Agent.config[:encoding_key] or
|
158
|
+
raise OneApm::Agent::CrossAppTracing::Error, "No encoding_key set."
|
159
|
+
end
|
160
|
+
|
161
|
+
def obfuscator
|
162
|
+
@obfuscator ||= OneApm::Agent::Obfuscator.new(cross_app_encoding_key)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Inject the X-Process header into the outgoing +request+.
|
166
|
+
def inject_request_headers(state, request)
|
167
|
+
cross_app_id = OneApm::Agent.config[:cross_process_id] or
|
168
|
+
raise OneApm::Agent::CrossAppTracing::Error, "no cross app ID configured"
|
169
|
+
|
170
|
+
state.is_cross_app_caller = true
|
171
|
+
txn_guid = state.request_guid
|
172
|
+
txn = state.current_transaction
|
173
|
+
if txn
|
174
|
+
trip_id = txn.cat_trip_id(state)
|
175
|
+
path_hash = txn.cat_path_hash(state)
|
176
|
+
|
177
|
+
if txn.raw_synthetics_header
|
178
|
+
request[OA_SYNTHETICS_HEADER] = txn.raw_synthetics_header
|
179
|
+
end
|
180
|
+
end
|
181
|
+
txn_data = OneApm::JSONWrapper.dump([txn_guid, false, trip_id, path_hash])
|
182
|
+
|
183
|
+
request[OA_ID_HEADER] = obfuscator.obfuscate(cross_app_id)
|
184
|
+
request[OA_TXN_HEADER] = obfuscator.obfuscate(txn_data)
|
185
|
+
|
186
|
+
rescue OneApm::Agent::CrossAppTracing::Error => err
|
187
|
+
OneApm::Agent.logger.debug "Not injecting x-process header", err
|
188
|
+
end
|
189
|
+
|
190
|
+
def add_transaction_trace_parameters(request, response)
|
191
|
+
filtered_uri = ::OneApm::Support::HTTPClients::URIUtil.filter_uri(request.uri)
|
192
|
+
transaction_sampler.add_segment_parameters(:uri => filtered_uri)
|
193
|
+
if response && response_is_crossapp?(response)
|
194
|
+
add_cat_transaction_trace_parameters(response)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
# Extract any custom parameters from +response+ if it's cross-application and
|
200
|
+
# add them to the current TT node.
|
201
|
+
def add_cat_transaction_trace_parameters( response )
|
202
|
+
appdata = extract_appdata( response )
|
203
|
+
transaction_sampler.add_segment_parameters( \
|
204
|
+
:transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] )
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
# Return the set of metric names that correspond to
|
209
|
+
# the given +request+ and +response+.
|
210
|
+
# +response+ may be nil in the case that the request produced an error
|
211
|
+
# without ever receiving an HTTP response.
|
212
|
+
def metrics_for( request, response )
|
213
|
+
metrics = common_metrics( request )
|
214
|
+
|
215
|
+
if response && response_is_crossapp?( response )
|
216
|
+
begin
|
217
|
+
metrics.concat metrics_for_crossapp_response( request, response )
|
218
|
+
rescue => err
|
219
|
+
# Fall back to regular metrics if there's a problem with x-process metrics
|
220
|
+
OneApm::Agent.logger.debug "%p while fetching x-process metrics: %s" %
|
221
|
+
[ err.class, err.message ]
|
222
|
+
metrics.concat metrics_for_regular_request( request )
|
223
|
+
end
|
224
|
+
else
|
225
|
+
metrics.concat metrics_for_regular_request( request )
|
226
|
+
end
|
227
|
+
|
228
|
+
return metrics
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
# Return an Array of metrics used for every response.
|
233
|
+
def common_metrics( request )
|
234
|
+
metrics = [ "External/all" ]
|
235
|
+
metrics << "External/#{request.host}/all"
|
236
|
+
|
237
|
+
if OneApm::Transaction.recording_web_transaction?
|
238
|
+
metrics << "External/allWeb"
|
239
|
+
else
|
240
|
+
metrics << "External/allOther"
|
241
|
+
end
|
242
|
+
|
243
|
+
return metrics
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
# Returns +true+ if Cross Application Tracing is enabled, and the given +response+
|
248
|
+
# has the appropriate headers.
|
249
|
+
def response_is_crossapp?( response )
|
250
|
+
return false unless cross_app_enabled?
|
251
|
+
unless response[OA_APPDATA_HEADER]
|
252
|
+
return false
|
253
|
+
end
|
254
|
+
|
255
|
+
return true
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
# Return the set of metric objects appropriate for the given cross app
|
260
|
+
# +response+.
|
261
|
+
def metrics_for_crossapp_response( request, response )
|
262
|
+
xp_id, txn_name, _q_time, _r_time, _req_len, _ = extract_appdata( response )
|
263
|
+
|
264
|
+
check_crossapp_id( xp_id )
|
265
|
+
check_transaction_name( txn_name )
|
266
|
+
|
267
|
+
metrics = []
|
268
|
+
metrics << "ExternalApp/#{request.host}/#{xp_id}/all"
|
269
|
+
metrics << "ExternalTransaction/#{request.host}/#{xp_id}/#{txn_name}"
|
270
|
+
|
271
|
+
return metrics
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
# Extract x-process application data from the specified +response+ and return
|
276
|
+
# it as an array of the form:
|
277
|
+
#
|
278
|
+
# [
|
279
|
+
# <cross app ID>,
|
280
|
+
# <transaction name>,
|
281
|
+
# <queue time in seconds>,
|
282
|
+
# <response time in seconds>,
|
283
|
+
# <request content length in bytes>,
|
284
|
+
# <transaction GUID>
|
285
|
+
# ]
|
286
|
+
def extract_appdata( response )
|
287
|
+
appdata = response[OA_APPDATA_HEADER] or
|
288
|
+
raise OneApm::Agent::CrossAppTracing::Error,
|
289
|
+
"Can't derive metrics for response: no #{OA_APPDATA_HEADER} header!"
|
290
|
+
|
291
|
+
decoded_appdata = obfuscator.deobfuscate( appdata )
|
292
|
+
decoded_appdata.set_encoding( ::Encoding::UTF_8 ) if
|
293
|
+
decoded_appdata.respond_to?( :set_encoding )
|
294
|
+
|
295
|
+
return OneApm::JSONWrapper.load( decoded_appdata )
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
# Return the set of metric objects appropriate for the given (non-cross app)
|
300
|
+
# +request+.
|
301
|
+
def metrics_for_regular_request( request )
|
302
|
+
metrics = []
|
303
|
+
metrics << "External/#{request.host}/#{request.type}/#{request.method}"
|
304
|
+
|
305
|
+
return metrics
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
# Fetch a reference to the stats engine.
|
310
|
+
def stats_engine
|
311
|
+
OneApm::Agent.instance.stats_engine
|
312
|
+
end
|
313
|
+
|
314
|
+
def transaction_sampler
|
315
|
+
OneApm::Agent.instance.transaction_sampler
|
316
|
+
end
|
317
|
+
|
318
|
+
# Check the given +id+ to ensure it conforms to the format of a cross-application
|
319
|
+
# ID. Raises an OneApm::Agent::CrossAppTracing::Error if it doesn't.
|
320
|
+
def check_crossapp_id( id )
|
321
|
+
id =~ /\A\d+#\d+\z/ or
|
322
|
+
raise OneApm::Agent::CrossAppTracing::Error,
|
323
|
+
"malformed cross application ID %p" % [ id ]
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
# Check the given +name+ to ensure it conforms to the format of a valid transaction
|
328
|
+
# name.
|
329
|
+
def check_transaction_name( name )
|
330
|
+
# No-op -- apparently absolutely anything is a valid transaction name?
|
331
|
+
# This is here for when that inevitably comes back to haunt us.
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|