appoptics_apm 4.11.0 → 4.12.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,23 +13,23 @@
13
13
  # ____ what is in the graphql gem and vice-versa
14
14
 
15
15
 
16
- if defined?(GraphQL::Tracing)
16
+ if defined?(GraphQL::Tracing) && !(AppOpticsAPM::Config[:graphql][:enabled] == false)
17
17
  module GraphQL
18
18
  module Tracing
19
19
  # AppOpticsTracing in the graphql gem may be a different version than the
20
20
  # one defined here, we want to use the newer one
21
- dont_redefine = false
21
+ redefine = true
22
22
  this_version = Gem::Version.new('1.0.0')
23
23
 
24
24
  if defined?(GraphQL::Tracing::AppOpticsTracing)
25
25
  if this_version > GraphQL::Tracing::AppOpticsTracing.version
26
26
  send(:remove_const, :AppOpticsTracing)
27
27
  else
28
- dont_redefine = true
28
+ redefine = false
29
29
  end
30
30
  end
31
31
 
32
- unless dont_redefine
32
+ if redefine
33
33
  #-----------------------------------------------------------------------------#
34
34
  #----- this class is duplicated in the graphql gem ---------------------------#
35
35
  #-----------------------------------------------------------------------------#
@@ -84,7 +84,7 @@ if defined?(GraphQL::Tracing)
84
84
 
85
85
  def transaction_name(query)
86
86
  return if gql_config[:transaction_name] == false ||
87
- ::AppOpticsAPM::SDK.get_transaction_name
87
+ ::AppOpticsAPM::SDK.get_transaction_name
88
88
 
89
89
  split_query = query.strip.split(/\W+/, 3)
90
90
  split_query[0] = 'query' if split_query[0].empty?
@@ -95,7 +95,7 @@ if defined?(GraphQL::Tracing)
95
95
 
96
96
  def multiplex_transaction_name(names)
97
97
  return if gql_config[:transaction_name] == false ||
98
- ::AppOpticsAPM::SDK.get_transaction_name
98
+ ::AppOpticsAPM::SDK.get_transaction_name
99
99
 
100
100
  name = "graphql.multiplex.#{names.join('.')}"
101
101
  name = "#{name[0..251]}..." if name.length > 254
@@ -167,8 +167,8 @@ if defined?(GraphQL::Tracing)
167
167
 
168
168
  # remove arguments
169
169
  query.gsub(/"[^"]*"/, '"?"') # strings
170
- .gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats
171
- .gsub(/\[[^\]]*\]/, '[?]') # arrays
170
+ .gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats
171
+ .gsub(/\[[^\]]*\]/, '[?]') # arrays
172
172
  end
173
173
 
174
174
  def remove_comments(query)
@@ -184,10 +184,16 @@ if defined?(GraphQL::Tracing)
184
184
 
185
185
  module AppOpticsAPM
186
186
  module GraphQLSchemaPrepend
187
- def use(plugin, options = {})
188
- super unless GraphQL::Schema.plugins.find { |pl| pl[0].to_s == plugin.to_s }
187
+ def use(plugin, **options)
188
+ # super unless GraphQL::Schema.plugins.find { |pl| pl[0].to_s == plugin.to_s }
189
+ super unless self.plugins.find { |pl| pl[0].to_s == plugin.to_s }
189
190
 
190
- GraphQL::Schema.plugins
191
+ self.plugins
192
+ end
193
+
194
+ def inherited(subclass)
195
+ subclass.use(GraphQL::Tracing::AppOpticsTracing)
196
+ super
191
197
  end
192
198
  end
193
199
 
@@ -200,16 +206,35 @@ if defined?(GraphQL::Tracing)
200
206
  end
201
207
  end
202
208
  # rubocop:enable Style/RedundantSelf
203
- end
204
209
 
205
- if defined?(GraphQL::Schema)
206
- GraphQL::Schema.singleton_class.prepend(AppOpticsAPM::GraphQLSchemaPrepend)
207
- GraphQL::Schema.use(GraphQL::Tracing::AppOpticsTracing)
210
+ # a different way of autoinstrumenting for graphql 1.7.4 - < 1.8.0
211
+ module GraphQLSchemaPrepend17
212
+ def initialize
213
+ super
214
+ unless tracers.find { |tr| tr.is_a? GraphQL::Tracing::AppOpticsTracing }
215
+ tracer = GraphQL::Tracing::AppOpticsTracing.new
216
+ tracers.push(tracer)
217
+ instrumenters[:field] << tracer
218
+ end
219
+ end
220
+ end
208
221
  end
209
222
 
210
- # rubocop:disable Style/IfUnlessModifier
211
- if defined?(GraphQL::Error)
212
- GraphQL::Error.prepend(AppOpticsAPM::GraphQLErrorPrepend)
223
+ if Gem.loaded_specs['graphql'] && Gem.loaded_specs['graphql'].version >= Gem::Version.new('1.8.0')
224
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting GraphQL' if AppOpticsAPM::Config[:verbose]
225
+ if defined?(GraphQL::Schema)
226
+ GraphQL::Schema.singleton_class.prepend(AppOpticsAPM::GraphQLSchemaPrepend)
227
+ end
228
+
229
+ # rubocop:disable Style/IfUnlessModifier
230
+ if defined?(GraphQL::Error)
231
+ GraphQL::Error.prepend(AppOpticsAPM::GraphQLErrorPrepend)
232
+ end
233
+ # rubocop:enable Style/IfUnlessModifier
234
+ elsif Gem.loaded_specs['graphql'] && Gem.loaded_specs['graphql'].version >= Gem::Version.new('1.7.4')
235
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting GraphQL' if AppOpticsAPM::Config[:verbose]
236
+ if defined?(GraphQL::Schema)
237
+ GraphQL::Schema.prepend(AppOpticsAPM::GraphQLSchemaPrepend17)
238
+ end
213
239
  end
214
- # rubocop:enable Style/IfUnlessModifier
215
240
  end
@@ -156,4 +156,4 @@ if defined?(GRPC) && AppOpticsAPM::Config[:grpc_client][:enabled]
156
156
 
157
157
  end
158
158
  end
159
- end
159
+ end
@@ -4,70 +4,78 @@
4
4
  require 'net/http'
5
5
 
6
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)
7
+ module AppOpticsAPM
8
+ module Inst
9
+ module NetHttp
10
+ # Net::HTTP.class_eval do
11
+ # def request_with_appoptics(*args, &block)
12
+ def request(*args, &block)
13
+ # Avoid cross host tracing for blacklisted domains
14
+ blacklisted = AppOpticsAPM::API.blacklisted?(addr_port)
15
+
16
+ # If we're not tracing, just do a fast return. Since
17
+ # net/http.request calls itself, only trace
18
+ # once the http session has been started.
19
+ if !AppOpticsAPM.tracing? || !started?
20
+ if blacklisted # if the other site is blacklisted, we don't want to leak its X-trace
21
+ resp = super
22
+ resp.delete('X-Trace') # if resp['X-Trace']
23
+ return resp
24
+ else
25
+ xtrace = AppOpticsAPM::Context.toString
26
+ args[0]['X-Trace'] = xtrace if AppOpticsAPM::XTrace.valid?(xtrace)
27
+ return super
28
+ end
54
29
  end
55
30
 
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"]
31
+ opts = {}
32
+ AppOpticsAPM::API.trace(:'net-http', opts) do
33
+ context = AppOpticsAPM::Context.toString
34
+
35
+ # Collect KVs to report in the exit event
36
+ if args.respond_to?(:first) && args.first
37
+ req = args.first
38
+
39
+ opts[:Spec] = 'rsc'
40
+ opts[:IsService] = 1
41
+ opts[:RemoteURL] = "#{use_ssl? ? 'https' : 'http'}://#{addr_port}"
42
+ opts[:RemoteURL] << (AppOpticsAPM::Config[:nethttp][:log_args] ? req.path : req.path.split('?').first)
43
+ opts[:HTTPMethod] = req.method
44
+ opts[:Blacklisted] = true if blacklisted
45
+ opts[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:nethttp][:collect_backtraces]
46
+
47
+ req['X-Trace'] = context unless blacklisted
48
+ end
49
+
50
+ begin
51
+ # The actual net::http call
52
+ resp = super
53
+ # Re-attach net::http edge unless blacklisted and is a valid X-Trace ID
54
+ xtrace = resp.get_fields('X-Trace')
55
+ if blacklisted
56
+ # we don't want the x-trace if it is from a blacklisted address
57
+ resp.delete('X-Trace') # if xtrace
58
+ else
59
+ xtrace = xtrace[0] if xtrace && xtrace.is_a?(Array)
60
+ AppOpticsAPM::XTrace.continue_service_context(context, xtrace)
61
+ end
62
+
63
+ opts[:HTTPStatus] = resp.code
64
+
65
+ # If we get a redirect, report the location header
66
+ if ((300..308).to_a.include? resp.code.to_i) && resp.header["Location"]
67
+ opts[:Location] = resp.header["Location"]
68
+ end
69
+
70
+ next resp
71
+ end
61
72
  end
62
-
63
- next resp
64
73
  end
74
+
65
75
  end
66
76
  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
77
  end
78
+
79
+ Net::HTTP.prepend(AppOpticsAPM::Inst::NetHttp)
80
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting net/http' if AppOpticsAPM::Config[:verbose]
73
81
  end
@@ -0,0 +1,35 @@
1
+ # Copyright (c) 2020 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module RackCacheContext
6
+
7
+ ###
8
+ # This adds a controller.action like transaction name for
9
+ # requests directly served from the cache without involving a controller.
10
+ # The resulting transaction name is `rack-cache.<cache-store>`,
11
+ # e.g. `rack-cache.memcached`
12
+ #
13
+ # It is not a full instrumentation, no span is added.
14
+ #
15
+ def call!(env)
16
+ metastore_type = begin
17
+ if options['rack-cache.metastore']
18
+ options['rack-cache.metastore'].match(/^([^\:]*)\:/)[1]
19
+ end || 'unknown_store'
20
+ rescue
21
+ 'unknown_store'
22
+ end
23
+
24
+ env['appoptics_apm.action'] = metastore_type
25
+ env['appoptics_apm.controller'] = 'rack-cache'
26
+
27
+ super
28
+ end
29
+ end
30
+ end
31
+
32
+ if AppOpticsAPM::Config[:rack_cache][:transaction_name] && defined?(Rack::Cache::Context)
33
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting rack_cache' if AppOpticsAPM::Config[:verbose]
34
+ Rack::Cache::Context.send(:prepend, ::AppOpticsAPM::RackCacheContext)
35
+ end
@@ -10,8 +10,6 @@ module AppOpticsAPM
10
10
  # noop version of :toString
11
11
  # toString would return the current context (xtrace) as string
12
12
  #
13
- # the noop version returns an empty string
14
- #
15
13
  def self.toString
16
14
  '2B0000000000000000000000000000000000000000000000000000000000'
17
15
  end
@@ -23,4 +21,4 @@ module AppOpticsAPM
23
21
 
24
22
  end
25
23
  end
26
- end
24
+ end
@@ -8,7 +8,7 @@ module AppOpticsAPM
8
8
  class OboeInitOptions
9
9
  include Singleton
10
10
 
11
- attr_reader :reporter, :host, :service_name, :ec2_md_timeout # exposing these mainly for testing
11
+ attr_reader :reporter, :host, :service_name, :ec2_md_timeout, :grpc_proxy # exposing these mainly for testing
12
12
 
13
13
  # TODO decide if these globals are useful when testing
14
14
  # OBOE_HOSTNAME_ALIAS = 0
@@ -71,6 +71,7 @@ module AppOpticsAPM
71
71
  @file_single = (ENV['APPOPTICS_REPORTER_FILE_SINGLE'].to_s.downcase == 'true') ? 1 : 0
72
72
  # timeout for ec2 metadata
73
73
  @ec2_md_timeout = read_and_validate_ec2_md_timeout
74
+ @grpc_proxy = read_and_validate_proxy
74
75
  end
75
76
 
76
77
  def re_init # for testing with changed ENV vars
@@ -97,7 +98,8 @@ module AppOpticsAPM
97
98
  @token_bucket_capacity,
98
99
  @token_bucket_rate,
99
100
  @file_single,
100
- @ec2_md_timeout
101
+ @ec2_md_timeout,
102
+ @grpc_proxy
101
103
  ]
102
104
  end
103
105
 
@@ -183,11 +185,23 @@ module AppOpticsAPM
183
185
  end
184
186
 
185
187
  def read_and_validate_ec2_md_timeout
186
- timeout = (ENV['APPOPTICS_EC2_METADATA_TIMEOUT'] || AppOpticsAPM::Config[:ec2_metadata_timeout])
188
+ timeout = ENV['APPOPTICS_EC2_METADATA_TIMEOUT'] || AppOpticsAPM::Config[:ec2_metadata_timeout]
187
189
  return 1000 unless timeout.is_a?(Integer) || timeout =~ /^\d+$/
188
190
  timeout = timeout.to_i
189
191
  return timeout.between?(0, 3000) ? timeout : 1000
190
192
  end
193
+
194
+ def read_and_validate_proxy
195
+ proxy = ENV['APPOPTICS_PROXY'] || AppOpticsAPM::Config[:http_proxy] || ''
196
+ return proxy if proxy == ''
197
+
198
+ unless proxy =~ /http:\/\/.*:\d+$/
199
+ AppOpticsAPM.logger.error "[appoptics_apm/oboe_options] APPOPTICS_PROXY/http_proxy doesn't start with 'http://', #{proxy}"
200
+ return '' # try without proxy, it may work, shouldn't crash but may not report
201
+ end
202
+
203
+ proxy
204
+ end
191
205
  end
192
206
  end
193
207
 
@@ -37,14 +37,10 @@ module AppOpticsAPM
37
37
  attr_reader :id
38
38
 
39
39
  def initialize
40
- if AppOpticsAPM::Config[:log_traceId] == :never
41
- @id = '0000000000000000000000000000000000000000-0'
42
- else
43
- @xtrace = AppOpticsAPM::Context.toString
44
- task_id = AppOpticsAPM::XTrace.task_id(@xtrace)
45
- sampled = AppOpticsAPM::XTrace.sampled?(@xtrace)
46
- @id = "#{task_id}-#{sampled ? 1 : 0}"
47
- end
40
+ @xtrace = AppOpticsAPM::Context.toString
41
+ task_id = AppOpticsAPM::XTrace.task_id(@xtrace)
42
+ sampled = AppOpticsAPM::XTrace.sampled?(@xtrace)
43
+ @id = "#{task_id}-#{sampled ? 1 : 0}"
48
44
  end
49
45
 
50
46
  # for_log returns a string in the format 'traceId=<current_trace.id>' or ''.
@@ -78,4 +74,4 @@ module AppOpticsAPM
78
74
 
79
75
  extend CurrentTrace
80
76
  end
81
- end
77
+ end
@@ -29,7 +29,7 @@ module AppOpticsAPM
29
29
  # end
30
30
  #
31
31
  # === Returns:
32
- # * true on success, false on failure
32
+ # * 0 on success, error code on failure
33
33
  #
34
34
  def increment_metric(name, count = 1, with_hostname = false, tags_kvs = {})
35
35
  return true unless AppOpticsAPM.loaded
@@ -62,7 +62,7 @@ module AppOpticsAPM
62
62
  # end
63
63
  #
64
64
  # === Returns:
65
- # * true on success, false on failure
65
+ # * 0 on success, error code on failure
66
66
  #
67
67
  def summary_metric(name, value, count = 1, with_hostname = false, tags_kvs = {})
68
68
  return true unless AppOpticsAPM.loaded
@@ -64,4 +64,4 @@ module AppOpticsAPM
64
64
 
65
65
  end
66
66
  end
67
- end
67
+ end
@@ -55,7 +55,7 @@ module AppOpticsAPM
55
55
  args << options.timestamp
56
56
  end
57
57
 
58
- metrics, sample, @rate, @source, @type, @auth, @status_msg, @auth_msg, @status =
58
+ metrics, sample, @rate, @source, @bucket_rate, @bucket_cap, @type, @auth, @status_msg, @auth_msg, @status =
59
59
  AppOpticsAPM::Context.getDecisions(*args)
60
60
 
61
61
  if @status > AO_TRACING_DECISIONS_OK
@@ -53,13 +53,13 @@ module AppOpticsAPM
53
53
  def set_postgresql_env
54
54
  if ENV.key?('TRAVIS_PSQL_PASS')
55
55
  ENV['DATABASE_URL'] = "postgresql://postgres:#{ENV['TRAVIS_PSQL_PASS']}@127.0.0.1:5432/travis_ci_test"
56
- elsif ENV.key?('DOCKER_PSQL_PASS')
57
- ENV['DATABASE_URL'] = "postgresql://docker:#{ENV['DOCKER_PSQL_PASS']}@127.0.0.1:5432/travis_ci_test"
56
+ elsif ENV.key?('POSTGRES_USER')
57
+ ENV['DATABASE_URL'] = "postgresql://#{ENV['POSTGRES_PASSWORD']}:#{ENV['POSTGRES_USER']}@#{ENV['PSQL_HOST']}:5432/travis_ci_test"
58
+ # ENV['DATABASE_URL'] = "postgresql://postgres@#{ENV['PSQL_HOST']}:5432/travis_ci_test"
58
59
  else
59
60
  ENV['DATABASE_URL'] = 'postgresql://postgres@127.0.0.1:5432/travis_ci_test'
60
61
  end
61
62
  end
62
-
63
63
  ##
64
64
  # set_mysql_env
65
65
  #
@@ -271,12 +271,13 @@ module AppOpticsAPM
271
271
  platform_info['Force'] = true
272
272
  platform_info['Ruby.Platform.Version'] = RUBY_PLATFORM
273
273
  platform_info['Ruby.Version'] = RUBY_VERSION
274
- platform_info['Ruby.AppOptics.Version'] = AppOpticsAPM::Version::STRING
274
+ platform_info['Ruby.AppOptics.Version'] = AppOpticsAPM::Version::STRING
275
275
 
276
+ # oboe not loaded yet, can't use oboe_api function to read oboe VERSION
276
277
  clib_version_file = File.join(Gem::Specification.find_by_name('appoptics_apm').gem_dir, 'ext', 'oboe_metal', 'src', 'VERSION')
277
- platform_info['Ruby.clib.Version'] = File.read(clib_version_file).chomp
278
+ platform_info['Ruby.AppOpticsExtension.Version'] = File.read(clib_version_file).chomp
278
279
  platform_info['RubyHeroku.AppOpticsAPM.Version'] = AppOpticsAPMHeroku::Version::STRING if defined?(AppOpticsAPMHeroku)
279
- platform_info['Ruby.TraceMode.Version'] = AppOpticsAPM::Config[:tracing_mode]
280
+ platform_info['Ruby.TraceMode.Version'] = AppOpticsAPM::Config[:tracing_mode]
280
281
 
281
282
  # Collect up the loaded gems
282
283
  if defined?(Gem) && Gem.respond_to?(:loaded_specs)