appoptics_apm_mnfst 4.5.2
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/.dockerignore +5 -0
- data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
- data/.gitignore +29 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +121 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +769 -0
- data/CONFIG.md +33 -0
- data/Gemfile +29 -0
- data/LICENSE +193 -0
- data/README.md +393 -0
- data/Rakefile +230 -0
- data/appoptics_apm.gemspec +61 -0
- data/bin/appoptics_apm_config +15 -0
- data/build_gem.sh +15 -0
- data/build_gem_upload_to_packagecloud.sh +20 -0
- data/examples/SDK/01_basic_tracing.rb +67 -0
- data/examples/carrying_context.rb +220 -0
- data/ext/oboe_metal/extconf.rb +114 -0
- data/ext/oboe_metal/lib/.keep +0 -0
- data/ext/oboe_metal/noop/noop.c +7 -0
- data/ext/oboe_metal/src/VERSION +1 -0
- data/init.rb +4 -0
- data/lib/appoptics_apm.rb +76 -0
- data/lib/appoptics_apm/api.rb +20 -0
- data/lib/appoptics_apm/api/layerinit.rb +41 -0
- data/lib/appoptics_apm/api/logging.rb +375 -0
- data/lib/appoptics_apm/api/memcache.rb +37 -0
- data/lib/appoptics_apm/api/metrics.rb +55 -0
- data/lib/appoptics_apm/api/profiling.rb +203 -0
- data/lib/appoptics_apm/api/tracing.rb +53 -0
- data/lib/appoptics_apm/api/util.rb +122 -0
- data/lib/appoptics_apm/base.rb +230 -0
- data/lib/appoptics_apm/config.rb +254 -0
- data/lib/appoptics_apm/frameworks/grape.rb +97 -0
- data/lib/appoptics_apm/frameworks/padrino.rb +108 -0
- data/lib/appoptics_apm/frameworks/rails.rb +94 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +104 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +55 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +58 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +50 -0
- data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +27 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +31 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +119 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +108 -0
- data/lib/appoptics_apm/frameworks/sinatra.rb +125 -0
- data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
- data/lib/appoptics_apm/inst/bunny-consumer.rb +89 -0
- data/lib/appoptics_apm/inst/curb.rb +330 -0
- data/lib/appoptics_apm/inst/dalli.rb +85 -0
- data/lib/appoptics_apm/inst/delayed_job.rb +92 -0
- data/lib/appoptics_apm/inst/em-http-request.rb +101 -0
- data/lib/appoptics_apm/inst/excon.rb +125 -0
- data/lib/appoptics_apm/inst/faraday.rb +94 -0
- data/lib/appoptics_apm/inst/grpc_client.rb +162 -0
- data/lib/appoptics_apm/inst/grpc_server.rb +120 -0
- data/lib/appoptics_apm/inst/http.rb +73 -0
- data/lib/appoptics_apm/inst/httpclient.rb +174 -0
- data/lib/appoptics_apm/inst/memcached.rb +86 -0
- data/lib/appoptics_apm/inst/mongo.rb +246 -0
- data/lib/appoptics_apm/inst/mongo2.rb +225 -0
- data/lib/appoptics_apm/inst/moped.rb +466 -0
- data/lib/appoptics_apm/inst/rack.rb +199 -0
- data/lib/appoptics_apm/inst/redis.rb +275 -0
- data/lib/appoptics_apm/inst/resque.rb +151 -0
- data/lib/appoptics_apm/inst/rest-client.rb +48 -0
- data/lib/appoptics_apm/inst/sequel.rb +178 -0
- data/lib/appoptics_apm/inst/sidekiq-client.rb +55 -0
- data/lib/appoptics_apm/inst/sidekiq-worker.rb +65 -0
- data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
- data/lib/appoptics_apm/inst/typhoeus.rb +108 -0
- data/lib/appoptics_apm/instrumentation.rb +22 -0
- data/lib/appoptics_apm/legacy_method_profiling.rb +90 -0
- data/lib/appoptics_apm/loading.rb +65 -0
- data/lib/appoptics_apm/logger.rb +42 -0
- data/lib/appoptics_apm/method_profiling.rb +33 -0
- data/lib/appoptics_apm/noop/README.md +9 -0
- data/lib/appoptics_apm/noop/context.rb +26 -0
- data/lib/appoptics_apm/noop/metadata.rb +22 -0
- data/lib/appoptics_apm/ruby.rb +35 -0
- data/lib/appoptics_apm/sdk/custom_metrics.rb +92 -0
- data/lib/appoptics_apm/sdk/tracing.rb +315 -0
- data/lib/appoptics_apm/support.rb +119 -0
- data/lib/appoptics_apm/test.rb +94 -0
- data/lib/appoptics_apm/thread_local.rb +26 -0
- data/lib/appoptics_apm/util.rb +319 -0
- data/lib/appoptics_apm/version.rb +15 -0
- data/lib/appoptics_apm/xtrace.rb +103 -0
- data/lib/joboe_metal.rb +212 -0
- data/lib/oboe.rb +7 -0
- data/lib/oboe/README +2 -0
- data/lib/oboe/backward_compatibility.rb +80 -0
- data/lib/oboe/inst/rack.rb +11 -0
- data/lib/oboe_metal.rb +198 -0
- data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
- data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +265 -0
- data/yardoc_frontpage.md +26 -0
- metadata +266 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Copyright (c) 2018 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
module GRPC
|
|
6
|
+
|
|
7
|
+
if defined? ::GRPC
|
|
8
|
+
STATUSCODES = {}
|
|
9
|
+
::GRPC::Core::StatusCodes.constants.each { |code| STATUSCODES[::GRPC::Core::StatusCodes.const_get(code)] = code }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module RpcDesc
|
|
13
|
+
|
|
14
|
+
def self.included(klass)
|
|
15
|
+
::AppOpticsAPM::Util.method_alias(klass, :handle_request_response, ::GRPC::RpcDesc)
|
|
16
|
+
::AppOpticsAPM::Util.method_alias(klass, :handle_client_streamer, ::GRPC::RpcDesc)
|
|
17
|
+
::AppOpticsAPM::Util.method_alias(klass, :handle_server_streamer, ::GRPC::RpcDesc)
|
|
18
|
+
::AppOpticsAPM::Util.method_alias(klass, :handle_bidi_streamer, ::GRPC::RpcDesc)
|
|
19
|
+
::AppOpticsAPM::Util.method_alias(klass, :run_server_method, ::GRPC::RpcDesc)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def grpc_tags(active_call, mth)
|
|
23
|
+
tags = {
|
|
24
|
+
'Spec' => 'grpc_server',
|
|
25
|
+
'URL' => active_call.metadata['method'],
|
|
26
|
+
'Controller' => mth.owner.to_s,
|
|
27
|
+
'Action' => mth.name.to_s,
|
|
28
|
+
'HTTP-Host' => active_call.peer
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if request_response?
|
|
32
|
+
tags['GRPCMethodType'] = 'UNARY'
|
|
33
|
+
elsif client_streamer?
|
|
34
|
+
tags['GRPCMethodType'] = 'CLIENT_STREAMING'
|
|
35
|
+
elsif server_streamer?
|
|
36
|
+
tags['GRPCMethodType'] = 'SERVER_STREAMING'
|
|
37
|
+
else # is a bidi_stream
|
|
38
|
+
tags['GRPCMethodType'] = 'BIDI_STREAMING'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
tags
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def handle_request_response_with_appoptics(active_call, mth, inter_ctx)
|
|
45
|
+
handle_call('handle_request_response_without_appoptics', active_call, mth, inter_ctx)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def handle_client_streamer_with_appoptics(active_call, mth, inter_ctx)
|
|
49
|
+
handle_call('handle_client_streamer_without_appoptics', active_call, mth, inter_ctx)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def handle_server_streamer_with_appoptics(active_call, mth, inter_ctx)
|
|
53
|
+
handle_call('handle_server_streamer_without_appoptics', active_call, mth, inter_ctx)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_bidi_streamer_with_appoptics(active_call, mth, inter_ctx)
|
|
57
|
+
handle_call('handle_bidi_streamer_without_appoptics', active_call, mth, inter_ctx)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# status codes need to be determined in this lower method, because they may not get raised to the
|
|
61
|
+
# next instrumented method
|
|
62
|
+
def handle_call(without, active_call, mth, inter_ctx)
|
|
63
|
+
begin
|
|
64
|
+
send(without, active_call, mth, inter_ctx)
|
|
65
|
+
rescue ::GRPC::Core::CallError, ::GRPC::BadStatus, ::GRPC::Core::OutOfTime, StandardError, NotImplementedError => e
|
|
66
|
+
log_grpc_exception(active_call, e)
|
|
67
|
+
raise e
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def run_server_method_with_appoptics(active_call, mth, inter_ctx)
|
|
72
|
+
tags = grpc_tags(active_call, mth)
|
|
73
|
+
AppOpticsAPM::API.log_start('grpc-server', active_call.metadata['x-trace'], tags)
|
|
74
|
+
|
|
75
|
+
exit_event = AppOpticsAPM::Event.startTrace(AppOpticsAPM::Context.get)
|
|
76
|
+
active_call.merge_metadata_to_send({ 'x-trace' => exit_event.metadataString })
|
|
77
|
+
begin
|
|
78
|
+
AppOpticsAPM::API.send_metrics('grpc-server', tags) do
|
|
79
|
+
run_server_method_without_appoptics(active_call, mth, inter_ctx)
|
|
80
|
+
end
|
|
81
|
+
rescue => e
|
|
82
|
+
log_grpc_exception(active_call, e)
|
|
83
|
+
raise e
|
|
84
|
+
ensure
|
|
85
|
+
tags['GRPCStatus'] = active_call.metadata_to_send.delete('grpc_status')
|
|
86
|
+
tags['GRPCStatus'] ||= active_call.status ? AppOpticsAPM::GRPC::STATUSCODES[active_call.status.code].to_s : 'OK'
|
|
87
|
+
tags['Backtrace'] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:grpc_server][:collect_backtraces]
|
|
88
|
+
|
|
89
|
+
exit_event.addEdge(AppOpticsAPM::Context.get)
|
|
90
|
+
AppOpticsAPM::API.log_end('grpc-server', tags, exit_event)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def log_grpc_exception(active_call, e)
|
|
97
|
+
unless e.instance_variable_get(:@exn_logged)
|
|
98
|
+
AppOpticsAPM::API.log_exception('grpc-server', e)
|
|
99
|
+
|
|
100
|
+
unless active_call.metadata_sent
|
|
101
|
+
if e.class == ::GRPC::Core::OutOfTime
|
|
102
|
+
active_call.merge_metadata_to_send({ 'grpc_status' => 'DEADLINE_EXCEEDED' })
|
|
103
|
+
elsif e.respond_to?(:code)
|
|
104
|
+
active_call.merge_metadata_to_send({ 'grpc_status' => AppOpticsAPM::GRPC::STATUSCODES[e.code].to_s })
|
|
105
|
+
else
|
|
106
|
+
active_call.merge_metadata_to_send({ 'grpc_status' => 'UNKNOWN' })
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if defined?(GRPC) && AppOpticsAPM::Config['grpc_server'][:enabled]
|
|
118
|
+
# server side is instrumented in RpcDesc
|
|
119
|
+
AppOpticsAPM::Util.send_include(GRPC::RpcDesc, AppOpticsAPM::GRPC::RpcDesc)
|
|
120
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'net/http'
|
|
5
|
+
|
|
6
|
+
if AppOpticsAPM::Config[:nethttp][:enabled]
|
|
7
|
+
|
|
8
|
+
Net::HTTP.class_eval do
|
|
9
|
+
def request_with_appoptics(*args, &block)
|
|
10
|
+
# Avoid cross host tracing for blacklisted domains
|
|
11
|
+
blacklisted = AppOpticsAPM::API.blacklisted?(addr_port)
|
|
12
|
+
|
|
13
|
+
# If we're not tracing, just do a fast return. Since
|
|
14
|
+
# net/http.request calls itself, only trace
|
|
15
|
+
# once the http session has been started.
|
|
16
|
+
if !AppOpticsAPM.tracing? || !started?
|
|
17
|
+
unless blacklisted
|
|
18
|
+
xtrace = AppOpticsAPM::Context.toString
|
|
19
|
+
args[0]['X-Trace'] = xtrace if AppOpticsAPM::XTrace.valid?(xtrace)
|
|
20
|
+
end
|
|
21
|
+
return request_without_appoptics(*args, &block)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
opts = {}
|
|
25
|
+
AppOpticsAPM::API.trace(:'net-http', opts) do
|
|
26
|
+
context = AppOpticsAPM::Context.toString
|
|
27
|
+
# task_id = AppOpticsAPM::XTrace.task_id(context)
|
|
28
|
+
|
|
29
|
+
# Collect KVs to report in the info event
|
|
30
|
+
if args.respond_to?(:first) && args.first
|
|
31
|
+
req = args.first
|
|
32
|
+
|
|
33
|
+
opts[:Spec] = 'rsc'
|
|
34
|
+
opts[:IsService] = 1
|
|
35
|
+
opts[:RemoteURL] = "#{use_ssl? ? 'https' : 'http'}://#{addr_port}"
|
|
36
|
+
opts[:RemoteURL] << (AppOpticsAPM::Config[:nethttp][:log_args] ? req.path : req.path.split('?').first)
|
|
37
|
+
opts[:HTTPMethod] = req.method
|
|
38
|
+
opts[:Blacklisted] = true if blacklisted
|
|
39
|
+
opts[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:nethttp][:collect_backtraces]
|
|
40
|
+
|
|
41
|
+
req['X-Trace'] = context unless blacklisted
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
# The actual net::http call
|
|
46
|
+
resp = request_without_appoptics(*args, &block)
|
|
47
|
+
|
|
48
|
+
# Re-attach net::http edge unless blacklisted and is a valid X-Trace ID
|
|
49
|
+
unless blacklisted
|
|
50
|
+
xtrace = resp.get_fields('X-Trace')
|
|
51
|
+
xtrace = xtrace[0] if xtrace && xtrace.is_a?(Array)
|
|
52
|
+
|
|
53
|
+
AppOpticsAPM::XTrace.continue_service_context(context, xtrace)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
opts[:HTTPStatus] = resp.code
|
|
57
|
+
|
|
58
|
+
# If we get a redirect, report the location header
|
|
59
|
+
if ((300..308).to_a.include? resp.code.to_i) && resp.header["Location"]
|
|
60
|
+
opts[:Location] = resp.header["Location"]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
next resp
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
alias request_without_appoptics request
|
|
69
|
+
alias request request_with_appoptics
|
|
70
|
+
|
|
71
|
+
AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting net/http' if AppOpticsAPM::Config[:verbose]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
module Inst
|
|
6
|
+
module HTTPClient
|
|
7
|
+
def self.included(klass)
|
|
8
|
+
AppOpticsAPM::Util.method_alias(klass, :do_request, ::HTTPClient)
|
|
9
|
+
AppOpticsAPM::Util.method_alias(klass, :do_request_async, ::HTTPClient)
|
|
10
|
+
AppOpticsAPM::Util.method_alias(klass, :do_get_stream, ::HTTPClient)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def appoptics_collect(method, uri, query = nil)
|
|
14
|
+
kvs = {}
|
|
15
|
+
kvs[:Spec] = 'rsc'
|
|
16
|
+
kvs[:IsService] = 1
|
|
17
|
+
|
|
18
|
+
# Conditionally log URL query params
|
|
19
|
+
# Because of the hook points, the query arg can come in under <tt>query</tt>
|
|
20
|
+
# or as a part of <tt>uri</tt> (not both). Here we handle both cases.
|
|
21
|
+
if AppOpticsAPM::Config[:httpclient][:log_args]
|
|
22
|
+
if query
|
|
23
|
+
kvs[:RemoteURL] = uri.to_s + '?' + AppOpticsAPM::Util.to_query(query)
|
|
24
|
+
else
|
|
25
|
+
kvs[:RemoteURL] = uri.to_s
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
kvs[:RemoteURL] = uri.to_s.split('?').first
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
kvs[:HTTPMethod] = AppOpticsAPM::Util.upcase(method)
|
|
32
|
+
kvs
|
|
33
|
+
rescue => e
|
|
34
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error capturing httpclient KVs: #{e.message}"
|
|
35
|
+
AppOpticsAPM.logger.debug e.backtrace.join('\n') if AppOpticsAPM::Config[:verbose]
|
|
36
|
+
ensure
|
|
37
|
+
return kvs
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def do_request_with_appoptics(method, uri, query, body, header, &block)
|
|
41
|
+
# Avoid cross host tracing for blacklisted domains
|
|
42
|
+
blacklisted = AppOpticsAPM::API.blacklisted?(uri.hostname)
|
|
43
|
+
|
|
44
|
+
# If we're not tracing, just do a fast return.
|
|
45
|
+
unless AppOpticsAPM.tracing?
|
|
46
|
+
add_xtrace_header(header) unless blacklisted
|
|
47
|
+
return do_request_without_appoptics(method, uri, query, body, header, &block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
req_context = nil
|
|
52
|
+
response_context = nil
|
|
53
|
+
|
|
54
|
+
kvs = appoptics_collect(method, uri, query)
|
|
55
|
+
kvs[:Blacklisted] = true if blacklisted
|
|
56
|
+
|
|
57
|
+
AppOpticsAPM::API.log_entry(:httpclient, kvs)
|
|
58
|
+
kvs.clear
|
|
59
|
+
kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:httpclient][:collect_backtraces]
|
|
60
|
+
|
|
61
|
+
req_context = add_xtrace_header(header) unless blacklisted
|
|
62
|
+
|
|
63
|
+
# The core httpclient call
|
|
64
|
+
response = do_request_without_appoptics(method, uri, query, body, header, &block)
|
|
65
|
+
|
|
66
|
+
response_context = response.headers['X-Trace']
|
|
67
|
+
kvs[:HTTPStatus] = response.status_code
|
|
68
|
+
|
|
69
|
+
# If we get a redirect, report the location header
|
|
70
|
+
if ((300..308).to_a.include? response.status.to_i) && response.headers.key?('Location')
|
|
71
|
+
kvs[:Location] = response.headers['Location']
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if response_context && !blacklisted
|
|
75
|
+
AppOpticsAPM::XTrace.continue_service_context(req_context, response_context)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
response
|
|
79
|
+
rescue => e
|
|
80
|
+
AppOpticsAPM::API.log_exception(:httpclient, e)
|
|
81
|
+
raise e
|
|
82
|
+
ensure
|
|
83
|
+
AppOpticsAPM::API.log_exit(:httpclient, kvs)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def do_request_async_with_appoptics(method, uri, query, body, header)
|
|
88
|
+
add_xtrace_header(header)
|
|
89
|
+
do_request_async_without_appoptics(method, uri, query, body, header)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def do_get_stream_with_appoptics(req, proxy, conn)
|
|
93
|
+
AppOpticsAPM::Context.fromString(req.header['X-Trace'].first) unless req.header['X-Trace'].empty?
|
|
94
|
+
# Avoid cross host tracing for blacklisted domains
|
|
95
|
+
uri = req.http_header.request_uri
|
|
96
|
+
blacklisted = AppOpticsAPM::API.blacklisted?(uri.hostname)
|
|
97
|
+
|
|
98
|
+
unless AppOpticsAPM.tracing?
|
|
99
|
+
req.header.delete('X-Trace') if blacklisted
|
|
100
|
+
return do_get_stream_without_appoptics(req, proxy, conn)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
begin
|
|
104
|
+
response = nil
|
|
105
|
+
req_context = nil
|
|
106
|
+
method = req.http_header.request_method
|
|
107
|
+
|
|
108
|
+
kvs = appoptics_collect(method, uri)
|
|
109
|
+
kvs[:Blacklisted] = true if blacklisted
|
|
110
|
+
kvs[:Async] = 1
|
|
111
|
+
|
|
112
|
+
AppOpticsAPM::API.log_entry(:httpclient, kvs)
|
|
113
|
+
kvs.clear
|
|
114
|
+
kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:httpclient][:collect_backtraces]
|
|
115
|
+
|
|
116
|
+
blacklisted ? req.header.delete('X-Trace') : req_context = add_xtrace_header(req.header)
|
|
117
|
+
|
|
118
|
+
# The core httpclient call
|
|
119
|
+
result = do_get_stream_without_appoptics(req, proxy, conn)
|
|
120
|
+
|
|
121
|
+
# Older HTTPClient < 2.6.0 returns HTTPClient::Connection
|
|
122
|
+
if result.is_a?(::HTTP::Message)
|
|
123
|
+
response = result
|
|
124
|
+
else
|
|
125
|
+
response = conn.pop
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
response_context = response.headers['X-Trace']
|
|
129
|
+
kvs[:HTTPStatus] = response.status_code
|
|
130
|
+
|
|
131
|
+
# If we get a redirect, report the location header
|
|
132
|
+
if ((300..308).to_a.include? response.status.to_i) && response.headers.key?('Location')
|
|
133
|
+
kvs[:Location] = response.headers['Location']
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if response_context && !blacklisted
|
|
137
|
+
AppOpticsAPM::XTrace.continue_service_context(req_context, response_context)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Older HTTPClient < 2.6.0 returns HTTPClient::Connection
|
|
141
|
+
conn.push response if result.is_a?(::HTTPClient::Connection)
|
|
142
|
+
result
|
|
143
|
+
rescue => e
|
|
144
|
+
AppOpticsAPM::API.log_exception(:httpclient, e)
|
|
145
|
+
raise e
|
|
146
|
+
ensure
|
|
147
|
+
AppOpticsAPM::API.log_exit(:httpclient, kvs)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def add_xtrace_header(headers)
|
|
154
|
+
req_context = AppOpticsAPM::Context.toString
|
|
155
|
+
return nil unless AppOpticsAPM::XTrace.valid?(req_context)
|
|
156
|
+
# Be aware of various ways to call/use httpclient
|
|
157
|
+
if headers.is_a?(Array)
|
|
158
|
+
headers.delete_if { |kv| kv[0] == 'X-Trace' }
|
|
159
|
+
headers.push ['X-Trace', req_context]
|
|
160
|
+
elsif headers.is_a?(Hash)
|
|
161
|
+
headers['X-Trace'] = req_context
|
|
162
|
+
elsif headers.is_a? HTTP::Message::Headers
|
|
163
|
+
headers.set('X-Trace', req_context)
|
|
164
|
+
end
|
|
165
|
+
req_context
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
if AppOpticsAPM::Config[:httpclient][:enabled] && defined?(HTTPClient)
|
|
172
|
+
AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting httpclient' if AppOpticsAPM::Config[:verbose]
|
|
173
|
+
AppOpticsAPM::Util.send_include(HTTPClient, AppOpticsAPM::Inst::HTTPClient)
|
|
174
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
module Inst
|
|
6
|
+
module Memcached
|
|
7
|
+
include AppOpticsAPM::API::Memcache
|
|
8
|
+
|
|
9
|
+
def self.included(cls)
|
|
10
|
+
AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting memcached' if AppOpticsAPM::Config[:verbose]
|
|
11
|
+
|
|
12
|
+
cls.class_eval do
|
|
13
|
+
MEMCACHE_OPS.reject { |m| !method_defined?(m) }.each do |m|
|
|
14
|
+
define_method("#{m}_with_appoptics") do |*args|
|
|
15
|
+
opts = { :KVOp => m }
|
|
16
|
+
|
|
17
|
+
if args.length && !args[0].is_a?(Array)
|
|
18
|
+
opts[:KVKey] = args[0].to_s
|
|
19
|
+
rhost = remote_host(args[0].to_s)
|
|
20
|
+
opts[:RemoteHost] = rhost if rhost
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
AppOpticsAPM::API.trace(:memcache, opts) do
|
|
24
|
+
result = send("#{m}_without_appoptics", *args)
|
|
25
|
+
|
|
26
|
+
opts[:KVHit] = memcache_hit?(result) if m == :get && args.length && args[0].class == String
|
|
27
|
+
opts[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcached][:collect_backtraces]
|
|
28
|
+
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class_eval "alias #{m}_without_appoptics #{m}"
|
|
34
|
+
class_eval "alias #{m} #{m}_with_appoptics"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end # module Memcached
|
|
40
|
+
|
|
41
|
+
module MemcachedRails
|
|
42
|
+
def self.included(cls)
|
|
43
|
+
cls.class_eval do
|
|
44
|
+
if ::Memcached::Rails.method_defined? :get_multi
|
|
45
|
+
alias get_multi_without_appoptics get_multi
|
|
46
|
+
alias get_multi get_multi_with_appoptics
|
|
47
|
+
elsif AppOpticsAPM::Config[:verbose]
|
|
48
|
+
AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument Memcached. Partial traces may occur.'
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def get_multi_with_appoptics(keys, raw = false)
|
|
54
|
+
if AppOpticsAPM.tracing?
|
|
55
|
+
layer_kvs = {}
|
|
56
|
+
layer_kvs[:KVOp] = :get_multi
|
|
57
|
+
|
|
58
|
+
AppOpticsAPM::API.trace(:memcache, layer_kvs || {}, :get_multi) do
|
|
59
|
+
layer_kvs[:KVKeyCount] = keys.flatten.length
|
|
60
|
+
|
|
61
|
+
values = get_multi_without_appoptics(keys, raw)
|
|
62
|
+
|
|
63
|
+
layer_kvs[:KVHitCount] = values.length
|
|
64
|
+
layer_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcached][:collect_backtraces]
|
|
65
|
+
|
|
66
|
+
values
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
get_multi_without_appoptics(keys, raw)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end # module MemcachedRails
|
|
73
|
+
end # module Inst
|
|
74
|
+
end # module AppOpticsAPM
|
|
75
|
+
|
|
76
|
+
if defined?(Memcached) && AppOpticsAPM::Config[:memcached][:enabled]
|
|
77
|
+
Memcached.class_eval do
|
|
78
|
+
include AppOpticsAPM::Inst::Memcached
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if defined?(Memcached::Rails)
|
|
82
|
+
Memcached::Rails.class_eval do
|
|
83
|
+
include AppOpticsAPM::Inst::MemcachedRails
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|