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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +5 -0
  3. data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
  4. data/.gitignore +29 -0
  5. data/.rubocop.yml +8 -0
  6. data/.travis.yml +121 -0
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +769 -0
  9. data/CONFIG.md +33 -0
  10. data/Gemfile +29 -0
  11. data/LICENSE +193 -0
  12. data/README.md +393 -0
  13. data/Rakefile +230 -0
  14. data/appoptics_apm.gemspec +61 -0
  15. data/bin/appoptics_apm_config +15 -0
  16. data/build_gem.sh +15 -0
  17. data/build_gem_upload_to_packagecloud.sh +20 -0
  18. data/examples/SDK/01_basic_tracing.rb +67 -0
  19. data/examples/carrying_context.rb +220 -0
  20. data/ext/oboe_metal/extconf.rb +114 -0
  21. data/ext/oboe_metal/lib/.keep +0 -0
  22. data/ext/oboe_metal/noop/noop.c +7 -0
  23. data/ext/oboe_metal/src/VERSION +1 -0
  24. data/init.rb +4 -0
  25. data/lib/appoptics_apm.rb +76 -0
  26. data/lib/appoptics_apm/api.rb +20 -0
  27. data/lib/appoptics_apm/api/layerinit.rb +41 -0
  28. data/lib/appoptics_apm/api/logging.rb +375 -0
  29. data/lib/appoptics_apm/api/memcache.rb +37 -0
  30. data/lib/appoptics_apm/api/metrics.rb +55 -0
  31. data/lib/appoptics_apm/api/profiling.rb +203 -0
  32. data/lib/appoptics_apm/api/tracing.rb +53 -0
  33. data/lib/appoptics_apm/api/util.rb +122 -0
  34. data/lib/appoptics_apm/base.rb +230 -0
  35. data/lib/appoptics_apm/config.rb +254 -0
  36. data/lib/appoptics_apm/frameworks/grape.rb +97 -0
  37. data/lib/appoptics_apm/frameworks/padrino.rb +108 -0
  38. data/lib/appoptics_apm/frameworks/rails.rb +94 -0
  39. data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +104 -0
  40. data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +55 -0
  41. data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
  42. data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  43. data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  44. data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +58 -0
  45. data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +50 -0
  46. data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +27 -0
  47. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
  48. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
  49. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +31 -0
  50. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +119 -0
  51. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +108 -0
  52. data/lib/appoptics_apm/frameworks/sinatra.rb +125 -0
  53. data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
  54. data/lib/appoptics_apm/inst/bunny-consumer.rb +89 -0
  55. data/lib/appoptics_apm/inst/curb.rb +330 -0
  56. data/lib/appoptics_apm/inst/dalli.rb +85 -0
  57. data/lib/appoptics_apm/inst/delayed_job.rb +92 -0
  58. data/lib/appoptics_apm/inst/em-http-request.rb +101 -0
  59. data/lib/appoptics_apm/inst/excon.rb +125 -0
  60. data/lib/appoptics_apm/inst/faraday.rb +94 -0
  61. data/lib/appoptics_apm/inst/grpc_client.rb +162 -0
  62. data/lib/appoptics_apm/inst/grpc_server.rb +120 -0
  63. data/lib/appoptics_apm/inst/http.rb +73 -0
  64. data/lib/appoptics_apm/inst/httpclient.rb +174 -0
  65. data/lib/appoptics_apm/inst/memcached.rb +86 -0
  66. data/lib/appoptics_apm/inst/mongo.rb +246 -0
  67. data/lib/appoptics_apm/inst/mongo2.rb +225 -0
  68. data/lib/appoptics_apm/inst/moped.rb +466 -0
  69. data/lib/appoptics_apm/inst/rack.rb +199 -0
  70. data/lib/appoptics_apm/inst/redis.rb +275 -0
  71. data/lib/appoptics_apm/inst/resque.rb +151 -0
  72. data/lib/appoptics_apm/inst/rest-client.rb +48 -0
  73. data/lib/appoptics_apm/inst/sequel.rb +178 -0
  74. data/lib/appoptics_apm/inst/sidekiq-client.rb +55 -0
  75. data/lib/appoptics_apm/inst/sidekiq-worker.rb +65 -0
  76. data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
  77. data/lib/appoptics_apm/inst/typhoeus.rb +108 -0
  78. data/lib/appoptics_apm/instrumentation.rb +22 -0
  79. data/lib/appoptics_apm/legacy_method_profiling.rb +90 -0
  80. data/lib/appoptics_apm/loading.rb +65 -0
  81. data/lib/appoptics_apm/logger.rb +42 -0
  82. data/lib/appoptics_apm/method_profiling.rb +33 -0
  83. data/lib/appoptics_apm/noop/README.md +9 -0
  84. data/lib/appoptics_apm/noop/context.rb +26 -0
  85. data/lib/appoptics_apm/noop/metadata.rb +22 -0
  86. data/lib/appoptics_apm/ruby.rb +35 -0
  87. data/lib/appoptics_apm/sdk/custom_metrics.rb +92 -0
  88. data/lib/appoptics_apm/sdk/tracing.rb +315 -0
  89. data/lib/appoptics_apm/support.rb +119 -0
  90. data/lib/appoptics_apm/test.rb +94 -0
  91. data/lib/appoptics_apm/thread_local.rb +26 -0
  92. data/lib/appoptics_apm/util.rb +319 -0
  93. data/lib/appoptics_apm/version.rb +15 -0
  94. data/lib/appoptics_apm/xtrace.rb +103 -0
  95. data/lib/joboe_metal.rb +212 -0
  96. data/lib/oboe.rb +7 -0
  97. data/lib/oboe/README +2 -0
  98. data/lib/oboe/backward_compatibility.rb +80 -0
  99. data/lib/oboe/inst/rack.rb +11 -0
  100. data/lib/oboe_metal.rb +198 -0
  101. data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
  102. data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +265 -0
  103. data/yardoc_frontpage.md +26 -0
  104. 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