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,101 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
module Inst
|
|
6
|
+
module EventMachine
|
|
7
|
+
module HttpConnection
|
|
8
|
+
def setup_request_with_appoptics(*args, &block)
|
|
9
|
+
context = AppOpticsAPM::Context.toString
|
|
10
|
+
blacklisted = AppOpticsAPM::API.blacklisted?(@uri)
|
|
11
|
+
|
|
12
|
+
if AppOpticsAPM.tracing?
|
|
13
|
+
report_kvs = {}
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
report_kvs[:Spec] = 'rsc'
|
|
17
|
+
report_kvs[:IsService] = 1
|
|
18
|
+
report_kvs[:RemoteURL] = @uri
|
|
19
|
+
report_kvs[:HTTPMethod] = args[0]
|
|
20
|
+
report_kvs[:Blacklisted] = true if blacklisted
|
|
21
|
+
report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:em_http_request][:collect_backtraces]
|
|
22
|
+
rescue => e
|
|
23
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/debug] em-http-request KV error: #{e.inspect}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context = AppOpticsAPM::API.log_entry('em-http-request', report_kvs)
|
|
27
|
+
end
|
|
28
|
+
client = setup_request_without_appoptics(*args, &block)
|
|
29
|
+
client.req.headers['X-Trace'] = context unless blacklisted
|
|
30
|
+
client
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module HttpClient
|
|
35
|
+
def parse_response_header_with_appoptics(*args, &block)
|
|
36
|
+
report_kvs = {}
|
|
37
|
+
xtrace = nil
|
|
38
|
+
blacklisted = AppOpticsAPM::API.blacklisted?(@uri)
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
report_kvs[:HTTPStatus] = args[2]
|
|
42
|
+
report_kvs[:Async] = 1
|
|
43
|
+
rescue => e
|
|
44
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/debug] em-http-request KV error: #{e.inspect}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
parse_response_header_without_appoptics(*args, &block)
|
|
48
|
+
|
|
49
|
+
unless blacklisted
|
|
50
|
+
headers = args[0]
|
|
51
|
+
context = AppOpticsAPM::Context.toString
|
|
52
|
+
task_id = AppOpticsAPM::XTrace.task_id(context)
|
|
53
|
+
|
|
54
|
+
if headers.is_a?(Hash) && headers.key?('X-Trace')
|
|
55
|
+
xtrace = headers['X-Trace']
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if AppOpticsAPM::XTrace.valid?(xtrace) && AppOpticsAPM.tracing?
|
|
59
|
+
|
|
60
|
+
# Assure that we received back a valid X-Trace with the same task_id
|
|
61
|
+
if task_id == AppOpticsAPM::XTrace.task_id(xtrace)
|
|
62
|
+
AppOpticsAPM::Context.fromString(xtrace)
|
|
63
|
+
else
|
|
64
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/em-http] Mismatched returned X-Trace ID : #{xtrace}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
ensure
|
|
70
|
+
AppOpticsAPM::API.log_exit(:'em-http-request', report_kvs)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if defined?(EventMachine::HttpConnection) && defined?(EventMachine::HttpClient) && AppOpticsAPM::Config[:em_http_request][:enabled]
|
|
78
|
+
AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting em-http-request' if AppOpticsAPM::Config[:verbose]
|
|
79
|
+
|
|
80
|
+
class EventMachine::HttpConnection
|
|
81
|
+
include AppOpticsAPM::Inst::EventMachine::HttpConnection
|
|
82
|
+
|
|
83
|
+
if method_defined?(:setup_request)
|
|
84
|
+
class_eval 'alias :setup_request_without_appoptics :setup_request'
|
|
85
|
+
class_eval 'alias :setup_request :setup_request_with_appoptics'
|
|
86
|
+
else
|
|
87
|
+
AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument em-http-request (:setup_request). Partial traces may occur.'
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class EventMachine::HttpClient
|
|
92
|
+
include AppOpticsAPM::Inst::EventMachine::HttpClient
|
|
93
|
+
|
|
94
|
+
if method_defined?(:parse_response_header)
|
|
95
|
+
class_eval 'alias :parse_response_header_without_appoptics :parse_response_header'
|
|
96
|
+
class_eval 'alias :parse_response_header :parse_response_header_with_appoptics'
|
|
97
|
+
else
|
|
98
|
+
AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument em-http-request (:parse_response_header). Partial traces may occur.'
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
module Inst
|
|
6
|
+
module ExconConnection
|
|
7
|
+
def self.included(klass)
|
|
8
|
+
AppOpticsAPM::Util.method_alias(klass, :request, ::Excon::Connection)
|
|
9
|
+
AppOpticsAPM::Util.method_alias(klass, :requests, ::Excon::Connection)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def appoptics_collect(params)
|
|
15
|
+
kvs = {}
|
|
16
|
+
kvs[:Spec] = 'rsc'
|
|
17
|
+
kvs[:IsService] = 1
|
|
18
|
+
|
|
19
|
+
# Conditionally log query args
|
|
20
|
+
if AppOpticsAPM::Config[:excon][:log_args] && @data[:query]
|
|
21
|
+
if @data[:query].is_a?(Hash)
|
|
22
|
+
service_arg = "#{@data[:path]}?#{URI.encode_www_form(@data[:query])}"
|
|
23
|
+
else
|
|
24
|
+
service_arg = "#{@data[:path]}?#{@data[:query]}"
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
service_arg = @data[:path]
|
|
28
|
+
end
|
|
29
|
+
kvs[:RemoteURL] = "#{@data[:scheme]}://#{@data[:host]}:#{@data[:port]}#{service_arg}"
|
|
30
|
+
|
|
31
|
+
# In the case of HTTP pipelining, params could be an array of
|
|
32
|
+
# request hashes.
|
|
33
|
+
if params.is_a?(Array)
|
|
34
|
+
methods = []
|
|
35
|
+
params.each do |p|
|
|
36
|
+
methods << AppOpticsAPM::Util.upcase(p[:method])
|
|
37
|
+
end
|
|
38
|
+
kvs[:HTTPMethods] = methods.join(',')[0..1024]
|
|
39
|
+
kvs[:Pipeline] = true
|
|
40
|
+
else
|
|
41
|
+
kvs[:HTTPMethod] = AppOpticsAPM::Util.upcase(params[:method])
|
|
42
|
+
end
|
|
43
|
+
kvs
|
|
44
|
+
rescue => e
|
|
45
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error capturing excon KVs: #{e.message}"
|
|
46
|
+
AppOpticsAPM.logger.debug e.backtrace.join('\n') if AppOpticsAPM::Config[:verbose]
|
|
47
|
+
ensure
|
|
48
|
+
return kvs
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
public
|
|
52
|
+
|
|
53
|
+
def requests_with_appoptics(pipeline_params)
|
|
54
|
+
responses = nil
|
|
55
|
+
kvs = appoptics_collect(pipeline_params)
|
|
56
|
+
AppOpticsAPM::API.trace(:excon, kvs) do
|
|
57
|
+
kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:excon][:collect_backtraces]
|
|
58
|
+
responses = requests_without_appoptics(pipeline_params)
|
|
59
|
+
kvs[:HTTPStatuses] = responses.map { |r| r.status }.join(',')
|
|
60
|
+
end
|
|
61
|
+
responses
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def request_with_appoptics(params={}, &block)
|
|
65
|
+
# Avoid cross host tracing for blacklisted domains
|
|
66
|
+
blacklisted = AppOpticsAPM::API.blacklisted?(@data[:hostname] || @data[:host])
|
|
67
|
+
|
|
68
|
+
# If we're not tracing, just do a fast return.
|
|
69
|
+
# If making HTTP pipeline requests (ordered batched)
|
|
70
|
+
# then just return as we're tracing from parent
|
|
71
|
+
# <tt>requests</tt>
|
|
72
|
+
if !AppOpticsAPM.tracing? || params[:pipeline]
|
|
73
|
+
@data[:headers]['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid && !blacklisted
|
|
74
|
+
return request_without_appoptics(params, &block)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
response_context = nil
|
|
79
|
+
|
|
80
|
+
kvs = appoptics_collect(params)
|
|
81
|
+
kvs[:Blacklisted] = true if blacklisted
|
|
82
|
+
|
|
83
|
+
AppOpticsAPM::API.log_entry(:excon, kvs)
|
|
84
|
+
kvs.clear
|
|
85
|
+
|
|
86
|
+
req_context = AppOpticsAPM::Context.toString
|
|
87
|
+
@data[:headers]['X-Trace'] = req_context unless blacklisted
|
|
88
|
+
|
|
89
|
+
# The core excon call
|
|
90
|
+
response = request_without_appoptics(params, &block)
|
|
91
|
+
|
|
92
|
+
# excon only passes back a hash (datum) for HTTP pipelining...
|
|
93
|
+
# In that case, we should never arrive here but for the OCD, double check
|
|
94
|
+
# the datatype before trying to extract pertinent info
|
|
95
|
+
if response.is_a?(Excon::Response)
|
|
96
|
+
response_context = response.headers['X-Trace']
|
|
97
|
+
kvs[:HTTPStatus] = response.status
|
|
98
|
+
|
|
99
|
+
# If we get a redirect, report the location header
|
|
100
|
+
if ((300..308).to_a.include? response.status.to_i) && response.headers.key?('Location')
|
|
101
|
+
kvs[:Location] = response.headers['Location']
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
if response_context && !blacklisted
|
|
105
|
+
AppOpticsAPM::XTrace.continue_service_context(req_context, response_context)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
response
|
|
110
|
+
rescue => e
|
|
111
|
+
AppOpticsAPM::API.log_exception(:excon, e)
|
|
112
|
+
raise e
|
|
113
|
+
ensure
|
|
114
|
+
kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:excon][:collect_backtraces]
|
|
115
|
+
AppOpticsAPM::API.log_exit(:excon, kvs) unless params[:pipeline]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if AppOpticsAPM::Config[:excon][:enabled] && defined?(Excon)
|
|
123
|
+
AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting excon' if AppOpticsAPM::Config[:verbose]
|
|
124
|
+
AppOpticsAPM::Util.send_include(Excon::Connection, AppOpticsAPM::Inst::ExconConnection)
|
|
125
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
module Inst
|
|
6
|
+
module FaradayConnection
|
|
7
|
+
def self.included(klass)
|
|
8
|
+
AppOpticsAPM::Util.method_alias(klass, :run_request, ::Faraday::Connection)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run_request_with_appoptics(method, url, body, headers, &block)
|
|
12
|
+
blacklisted = url_blacklisted?
|
|
13
|
+
remote_call = remote_call?
|
|
14
|
+
|
|
15
|
+
unless AppOpticsAPM.tracing?
|
|
16
|
+
if remote_call && !blacklisted
|
|
17
|
+
xtrace = AppOpticsAPM::Context.toString
|
|
18
|
+
@headers['X-Trace'] = xtrace if AppOpticsAPM::XTrace.valid?(xtrace)
|
|
19
|
+
end
|
|
20
|
+
return run_request_without_appoptics(method, url, body, headers, &block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
AppOpticsAPM::API.log_entry(:faraday)
|
|
25
|
+
xtrace = nil
|
|
26
|
+
if remote_call && !blacklisted
|
|
27
|
+
xtrace = AppOpticsAPM::Context.toString
|
|
28
|
+
@headers['X-Trace'] = xtrace if AppOpticsAPM::XTrace.valid?(xtrace)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
result = run_request_without_appoptics(method, url, body, headers, &block)
|
|
32
|
+
|
|
33
|
+
# Re-attach edge unless it's blacklisted
|
|
34
|
+
# or if we don't have a valid X-Trace header
|
|
35
|
+
if remote_call && !blacklisted
|
|
36
|
+
xtrace_new = result.headers['X-Trace']
|
|
37
|
+
AppOpticsAPM::XTrace.continue_service_context(xtrace, xtrace_new)
|
|
38
|
+
end
|
|
39
|
+
kvs = {}
|
|
40
|
+
kvs[:Middleware] = @builder.handlers
|
|
41
|
+
kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:faraday][:collect_backtraces]
|
|
42
|
+
|
|
43
|
+
# Only send service KVs if we're not using an adapter
|
|
44
|
+
# Otherwise, the adapter instrumentation will send the service KVs
|
|
45
|
+
if remote_call
|
|
46
|
+
kvs.merge!(rsc_kvs(url, method, result))
|
|
47
|
+
unless blacklisted
|
|
48
|
+
xtrace_new = result.headers['X-Trace']
|
|
49
|
+
AppOpticsAPM::XTrace.continue_service_context(xtrace, xtrace_new)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
result
|
|
54
|
+
rescue => e
|
|
55
|
+
AppOpticsAPM::API.log_exception(:faraday, e)
|
|
56
|
+
raise e
|
|
57
|
+
ensure
|
|
58
|
+
AppOpticsAPM::API.log_exit(:faraday, kvs)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def url_blacklisted?
|
|
65
|
+
url = @url_prefix ? @url_prefix.to_s : @host
|
|
66
|
+
AppOpticsAPM::API.blacklisted?(url)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# This is only considered a remote service call if the middleware/adapter is not instrumented
|
|
70
|
+
def remote_call?
|
|
71
|
+
(@builder.handlers.map(&:name) & APPOPTICS_INSTR_ADAPTERS).count == 0
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def rsc_kvs(url, method, result)
|
|
75
|
+
kvs = { :Spec => 'rsc',
|
|
76
|
+
:IsService => 1,
|
|
77
|
+
:HTTPMethod => method.upcase,
|
|
78
|
+
:HTTPStatus => result.status, }
|
|
79
|
+
kvs[:Blacklisted] = true if url_blacklisted?
|
|
80
|
+
kvs[:RemoteURL] = result.to_hash[:url].to_s
|
|
81
|
+
kvs[:RemoteURL].split('?').first unless AppOpticsAPM::Config[:faraday][:log_args]
|
|
82
|
+
|
|
83
|
+
kvs
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if defined?(Faraday) && AppOpticsAPM::Config[:faraday][:enabled]
|
|
90
|
+
AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting faraday' if AppOpticsAPM::Config[:verbose]
|
|
91
|
+
AppOpticsAPM::Util.send_include(Faraday::Connection, AppOpticsAPM::Inst::FaradayConnection)
|
|
92
|
+
APPOPTICS_INSTR_ADAPTERS = ["Faraday::Adapter::NetHttp", "Faraday::Adapter::Excon", "Faraday::Adapter::Typhoeus"]
|
|
93
|
+
APPOPTICS_INSTR_ADAPTERS << "Faraday::Adapter::HTTPClient" if defined?Faraday::Adapter::HTTPClient
|
|
94
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Copyright (c) 2018 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
module GRPC
|
|
6
|
+
|
|
7
|
+
module ActiveCall
|
|
8
|
+
if defined? ::GRPC
|
|
9
|
+
StatusCodes = {}
|
|
10
|
+
::GRPC::Core::StatusCodes.constants.each { |code| StatusCodes[::GRPC::Core::StatusCodes.const_get(code)] = code }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.included(klass)
|
|
14
|
+
AppOpticsAPM::Util.method_alias(klass, :request_response, ::GRPC::ActiveCall)
|
|
15
|
+
AppOpticsAPM::Util.method_alias(klass, :client_streamer, ::GRPC::ActiveCall)
|
|
16
|
+
AppOpticsAPM::Util.method_alias(klass, :server_streamer, ::GRPC::ActiveCall)
|
|
17
|
+
AppOpticsAPM::Util.method_alias(klass, :bidi_streamer, ::GRPC::ActiveCall)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def grpc_tags(method_type, method)
|
|
21
|
+
tags = { 'Spec' => 'rsc',
|
|
22
|
+
'RemoteURL' => "grpc://#{peer}#{method}",
|
|
23
|
+
'GRPCMethodType' => method_type,
|
|
24
|
+
'IsService' => 'True'
|
|
25
|
+
}
|
|
26
|
+
tags
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def request_response_with_appoptics(req, metadata: {})
|
|
30
|
+
unary_response(req, type: 'UNARY', metadata: metadata, without: :request_response_without_appoptics)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def client_streamer_with_appoptics(req, metadata: {})
|
|
34
|
+
unary_response(req, type: 'CLIENT_STREAMING', metadata: metadata, without: :client_streamer_without_appoptics)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def server_streamer_with_appoptics(req, metadata: {}, &blk)
|
|
38
|
+
@tags = grpc_tags('SERVER_STREAMING', metadata[:method] || metadata_to_send[:method])
|
|
39
|
+
AppOpticsAPM::API.log_entry('grpc-client', @tags)
|
|
40
|
+
metadata['x-trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
|
|
41
|
+
AppOpticsAPM::SDK.set_transaction_name(metadata[:method]) if AppOpticsAPM.transaction_name.nil?
|
|
42
|
+
|
|
43
|
+
patch_receive_and_check_status # need to patch this so that log_exit can be called after the enum is consumed
|
|
44
|
+
|
|
45
|
+
response = server_streamer_without_appoptics(req, metadata: metadata)
|
|
46
|
+
block_given? ? response.each { |r| yield r } : response
|
|
47
|
+
rescue => e
|
|
48
|
+
# this check is needed because the exception may have been logged in patch_receive_and_check_status
|
|
49
|
+
unless e.instance_variable_get(:@exn_logged)
|
|
50
|
+
context_from_incoming
|
|
51
|
+
AppOpticsAPM::API.log_exception('grpc-client', e)
|
|
52
|
+
AppOpticsAPM::API.log_exit('grpc-client', exit_tags(@tags))
|
|
53
|
+
end
|
|
54
|
+
raise e
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def bidi_streamer_with_appoptics(req, metadata: {}, &blk)
|
|
58
|
+
@tags = grpc_tags('BIDI_STREAMING', metadata[:method] || metadata_to_send[:method])
|
|
59
|
+
AppOpticsAPM::API.log_entry('grpc-client', @tags)
|
|
60
|
+
metadata['x-trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
|
|
61
|
+
AppOpticsAPM::SDK.set_transaction_name(metadata[:method]) if AppOpticsAPM.transaction_name.nil?
|
|
62
|
+
|
|
63
|
+
patch_set_input_stream_done
|
|
64
|
+
|
|
65
|
+
response = bidi_streamer_without_appoptics(req, metadata: metadata)
|
|
66
|
+
block_given? ? response.each { |r| yield r } : response
|
|
67
|
+
rescue => e
|
|
68
|
+
unless e.instance_variable_get(:@exn_logged)
|
|
69
|
+
context_from_incoming
|
|
70
|
+
AppOpticsAPM::API.log_exception('grpc-client', e)
|
|
71
|
+
AppOpticsAPM::API.log_exit('grpc-client', exit_tags(@tags))
|
|
72
|
+
end
|
|
73
|
+
raise e
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def unary_response(req, type: , metadata: , without:)
|
|
79
|
+
tags = grpc_tags(type, metadata[:method] || metadata_to_send[:method])
|
|
80
|
+
AppOpticsAPM::SDK.trace('grpc-client', tags) do
|
|
81
|
+
metadata['x-trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
|
|
82
|
+
AppOpticsAPM::SDK.set_transaction_name(metadata[:method]) if AppOpticsAPM.transaction_name.nil?
|
|
83
|
+
begin
|
|
84
|
+
send(without, req, metadata: metadata)
|
|
85
|
+
ensure
|
|
86
|
+
exit_tags(tags)
|
|
87
|
+
context_from_incoming
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def patch_receive_and_check_status
|
|
93
|
+
def self.receive_and_check_status # need to patch this so that log_exit can be called after the enum is consumed
|
|
94
|
+
super
|
|
95
|
+
context_from_incoming
|
|
96
|
+
rescue => e
|
|
97
|
+
context_from_incoming
|
|
98
|
+
AppOpticsAPM::API.log_exception('grpc-client', e)
|
|
99
|
+
raise e
|
|
100
|
+
ensure
|
|
101
|
+
AppOpticsAPM::API.log_exit('grpc-client', exit_tags(@tags))
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def patch_set_input_stream_done
|
|
106
|
+
# need to patch this instance method so that log_exit can be called after the enum is consumed
|
|
107
|
+
def self.set_input_stream_done
|
|
108
|
+
return if status.nil?
|
|
109
|
+
context_from_incoming
|
|
110
|
+
if status.code > 0
|
|
111
|
+
AppOpticsAPM::API.log_exception('grpc-client', $!)
|
|
112
|
+
end
|
|
113
|
+
AppOpticsAPM::API.log_exit('grpc-client', exit_tags(@tags))
|
|
114
|
+
super
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def context_from_incoming
|
|
119
|
+
if AppOpticsAPM::Context.isValid
|
|
120
|
+
xtrace ||= @call.trailing_metadata['x-trace'] if @call.trailing_metadata && @call.trailing_metadata['x-trace']
|
|
121
|
+
xtrace ||= @call.metadata['x-trace'] if @call.metadata && @call.metadata['x-trace']
|
|
122
|
+
AppOpticsAPM::Context.fromString(xtrace) if xtrace
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def exit_tags(tags)
|
|
127
|
+
# we need to translate the status.code, it is not the status.details we want, they are not matching 1:1
|
|
128
|
+
tags['GRPCStatus'] ||= @call.status ? StatusCodes[@call.status.code].to_s : 'UNKNOWN'
|
|
129
|
+
tags['Backtrace'] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:grpc_client][:collect_backtraces]
|
|
130
|
+
tags
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
if defined?(GRPC) && AppOpticsAPM::Config[:grpc_client][:enabled]
|
|
138
|
+
AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting GRPC' if AppOpticsAPM::Config[:verbose]
|
|
139
|
+
|
|
140
|
+
# Client side is instrumented in ActiveCall and ClientStub
|
|
141
|
+
AppOpticsAPM::Util.send_include(GRPC::ActiveCall, AppOpticsAPM::GRPC::ActiveCall)
|
|
142
|
+
|
|
143
|
+
GRPC_ClientStub_ops = [:request_response, :client_streamer, :server_streamer, :bidi_streamer]
|
|
144
|
+
module GRPC
|
|
145
|
+
class ClientStub
|
|
146
|
+
GRPC_ClientStub_ops.reject { |m| !method_defined?(m) }.each do |m|
|
|
147
|
+
define_method("#{m}_with_appoptics") do |method, req, marshal, unmarshal, deadline: nil,
|
|
148
|
+
return_op: false, parent: nil,
|
|
149
|
+
credentials: nil, metadata: {}, &blk|
|
|
150
|
+
|
|
151
|
+
metadata[:method] = method
|
|
152
|
+
return send("#{m}_without_appoptics", method, req, marshal, unmarshal, deadline: deadline,
|
|
153
|
+
return_op: return_op, parent: parent,
|
|
154
|
+
credentials: credentials, metadata: metadata, &blk)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
AppOpticsAPM::Util.method_alias(GRPC::ClientStub, m)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|