appoptics_apm 4.10.1 → 4.12.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2016 SolarWinds, LLC.
2
4
  # All rights reserved.
3
5
 
@@ -20,7 +22,7 @@ ao_include = File.join(ext_dir, 'src')
20
22
  version = File.read(File.join(ao_include, 'VERSION')).chomp
21
23
  if ENV['APPOPTICS_FROM_S3'].to_s.downcase == 'true'
22
24
  ao_path = File.join('https://s3-us-west-2.amazonaws.com/rc-files-t2/c-lib/', version)
23
- puts "Fetching c-lib from S3"
25
+ puts 'Fetching c-lib from S3'
24
26
  else
25
27
  ao_path = File.join('https://files.appoptics.com/c-lib', version)
26
28
  end
@@ -29,7 +31,7 @@ ao_arch = 'x86_64'
29
31
  if File.exist?('/etc/alpine-release')
30
32
  version = open('/etc/alpine-release').read.chomp
31
33
  ao_arch =
32
- if Gem::Version.new(version) < Gem::Version.new('3.9')
34
+ if Gem::Version.new(version) < Gem::Version.new('3.9')
33
35
  'alpine-libressl-x86_64'
34
36
  else # openssl
35
37
  'alpine-x86_64'
@@ -46,6 +48,8 @@ success = false
46
48
  while retries > 0
47
49
  begin
48
50
  # download
51
+ # TODO warning: calling URI.open via Kernel#open is deprecated, call URI.open directly or use URI#open
52
+ # ____ URI.open is not supported in 2.4.5, let's use open until we can deprecate 2.4.5
49
53
  download = open(ao_item, 'rb')
50
54
  IO.copy_stream(download, clib)
51
55
 
@@ -1 +1 @@
1
- 7.0.0
1
+ 10.0.0
@@ -55,7 +55,7 @@ module AppOpticsAPM
55
55
  # ==== Arguments
56
56
  #
57
57
  # * +layer+ - The layer the reported event belongs to
58
- # * +exception+ - The exception to report
58
+ # * +exception+ - The exception to report, responds to :message and :backtrace(optional)
59
59
  # * +opts+ - Custom params if you want to log extra information
60
60
  #
61
61
  # ==== Example
@@ -80,7 +80,10 @@ module AppOpticsAPM
80
80
  opts.merge!(:Spec => 'error',
81
81
  :ErrorClass => exception.class.name,
82
82
  :ErrorMsg => exception.message)
83
- opts.merge!(:Backtrace => exception.backtrace.join("\r\n")) if exception.backtrace
83
+
84
+ if exception.respond_to?(:backtrace) && exception.backtrace
85
+ opts.merge!(:Backtrace => exception.backtrace.join("\r\n"))
86
+ end
84
87
 
85
88
  exception.instance_variable_set(:@exn_logged, true)
86
89
  log(layer, :error, opts)
@@ -205,6 +208,7 @@ module AppOpticsAPM
205
208
  def log_info(layer, opts = {})
206
209
  return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
207
210
 
211
+ opts[:Spec] = 'info'
208
212
  log_event(layer, :info, AppOpticsAPM::Context.createEvent, opts)
209
213
  end
210
214
 
@@ -45,6 +45,8 @@ module AppOpticsAPM
45
45
  AppOpticsAPM.transaction_name
46
46
  elsif kvs['Controller'] && kvs['Action']
47
47
  [kvs['Controller'], kvs['Action']].join('.')
48
+ elsif kvs[:Controller] && kvs[:Action]
49
+ [kvs[:Controller], kvs[:Action]].join('.')
48
50
  else
49
51
  "custom-#{span}"
50
52
  end
@@ -52,4 +54,4 @@ module AppOpticsAPM
52
54
 
53
55
  end
54
56
  end
55
- end
57
+ end
@@ -24,16 +24,14 @@ module AppOpticsAPM
24
24
 
25
25
  # Internal: Get the current backtrace.
26
26
  #
27
- # ignore - Number of frames to ignore at the top of the backtrace. Use
28
- # when you know how many layers deep in the key call is being
29
- # made.
27
+ # from - int, from position in array of backtraces
28
+ # to - int, end position in array of backtraces, can be negative to count from the end
30
29
  #
31
30
  # Returns a string with each frame of the backtrace separated by '\r\n'.
32
31
  #
33
- def backtrace(ignore = 0)
32
+ def backtrace(from = 0, to = -1)
34
33
  bt = Kernel.caller
35
- bt.slice!(0, ignore)
36
- trim_backtrace(bt).join("\r\n")
34
+ trim_backtrace(bt[from..to]).join("\r\n")
37
35
  end
38
36
 
39
37
  # Internal: Trim a backtrace to a manageable size
@@ -115,7 +113,7 @@ module AppOpticsAPM
115
113
  end
116
114
 
117
115
  def xtrace_v2?(xtr)
118
- return xtr && xtr.start_with?('2B')
116
+ xtr && xtr.start_with?('2B')
119
117
  end
120
118
  end
121
119
  end
@@ -16,7 +16,7 @@ module AppOpticsAPM
16
16
  @@instrumentation = [:action_controller, :action_controller_api, :action_view,
17
17
  :active_record, :bunnyclient, :bunnyconsumer, :cassandra, :curb,
18
18
  :dalli, :delayed_jobclient, :delayed_jobworker,
19
- :excon, :faraday, :grpc_client, :grpc_server, :grape,
19
+ :excon, :faraday, :graphql, :grpc_client, :grpc_server, :grape,
20
20
  :httpclient, :nethttp, :memcached, :mongo, :moped, :padrino, :rack, :redis,
21
21
  :resqueclient, :resqueworker, :rest_client,
22
22
  :sequel, :sidekiqclient, :sidekiqworker, :sinatra, :typhoeus]
@@ -65,15 +65,16 @@ module AppOpticsAPM
65
65
  config_file = File.join(Dir.pwd, 'appoptics_apm_config.rb')
66
66
  config_files << config_file if File.exist?(config_file)
67
67
 
68
- return if config_files.empty? # we use the defaults from the template in this case
69
-
70
- if config_files.size > 1
71
- AppOpticsAPM.logger.warn [
72
- '[appoptics_apm/config] Multiple configuration files configured, using the first one listed: ',
73
- config_files.join(', ')
74
- ].join(' ')
68
+ unless config_files.empty? # we use the defaults from the template if there are no config files
69
+ if config_files.size > 1
70
+ AppOpticsAPM.logger.warn [
71
+ '[appoptics_apm/config] Multiple configuration files configured, using the first one listed: ',
72
+ config_files.join(', ')
73
+ ].join(' ')
74
+ end
75
+ load(config_files[0])
75
76
  end
76
- load(config_files[0])
77
+
77
78
  # sets AppOpticsAPM::Config[:debug_level], AppOpticsAPM.logger.level
78
79
  set_log_level
79
80
 
@@ -53,7 +53,8 @@ module AppOpticsAPM
53
53
  end
54
54
 
55
55
  def error_response_with_appoptics(error = {})
56
- status, headers, body = error_response_without_appoptics(error)
56
+ response = error_response_without_appoptics(error)
57
+ status, headers, _body = response.finish
57
58
 
58
59
  xtrace = AppOpticsAPM::Context.toString
59
60
 
@@ -77,7 +78,7 @@ module AppOpticsAPM
77
78
  end
78
79
  end
79
80
 
80
- [status, headers, body]
81
+ response
81
82
  end
82
83
  end
83
84
  end
@@ -58,7 +58,7 @@ module AppOpticsAPM
58
58
  end
59
59
  end
60
60
 
61
- if defined?(Padrino) && AppopticsAPM::Config[:padrino][:enabled]
61
+ if defined?(Padrino) && AppOpticsAPM::Config[:padrino][:enabled]
62
62
  # This instrumentation is a superset of the Sinatra instrumentation similar
63
63
  # to how Padrino is a superset of Sinatra itself.
64
64
  AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting Padrino' if AppOpticsAPM::Config[:verbose]
@@ -1,6 +1,8 @@
1
1
  # Copyright (c) 2016 SolarWinds, LLC.
2
2
  # All rights reserved.
3
3
 
4
+ require_relative '../../../lib/appoptics_apm/inst/logger_formatter'
5
+
4
6
  module AppOpticsAPM
5
7
  module Rails
6
8
  module Helpers
@@ -75,6 +77,10 @@ if defined?(::Rails)
75
77
  AppOpticsAPM::Rails.include_helpers
76
78
  end
77
79
 
80
+ initializer 'appoptics_apm.controller', before: 'wicked_pdf.register' do
81
+ AppOpticsAPM::Rails.load_instrumentation
82
+ end
83
+
78
84
  initializer 'appoptics_apm.rack' do |app|
79
85
  AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting rack' if AppOpticsAPM::Config[:verbose]
80
86
  app.config.middleware.insert 0, AppOpticsAPM::Rack
@@ -84,7 +90,7 @@ if defined?(::Rails)
84
90
  AppOpticsAPM.logger = ::Rails.logger if ::Rails.logger && !ENV.key?('APPOPTICS_GEM_TEST')
85
91
 
86
92
  AppOpticsAPM::Inst.load_instrumentation
87
- AppOpticsAPM::Rails.load_instrumentation
93
+ # AppOpticsAPM::Rails.load_instrumentation
88
94
 
89
95
  # Report __Init after fork when in Heroku
90
96
  AppOpticsAPM::API.report_init unless AppOpticsAPM.heroku?
@@ -73,7 +73,7 @@ module AppOpticsAPM
73
73
  end
74
74
  end
75
75
 
76
- if defined?(Sinatra) && AppopticsAPM::Config[:sinatra][:enabled]
76
+ if defined?(Sinatra) && AppOpticsAPM::Config[:sinatra][:enabled]
77
77
  require 'appoptics_apm/inst/rack'
78
78
 
79
79
  AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting Sinatra' if AppOpticsAPM::Config[:verbose]
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # Copyright (c) 2019 SolarWinds, LLC.
5
+ # All rights reserved.
6
+ #++
7
+
8
+ # initial version of this instrumentation:
9
+ # https://github.com/librato/api/blob/master/graph/tracing/appoptics.rb
10
+ #
11
+
12
+ # TODO: make sure this stays up to date with
13
+ # ____ what is in the graphql gem and vice-versa
14
+
15
+
16
+ if defined?(GraphQL::Tracing) && !(AppOpticsAPM::Config[:graphql][:enabled] == false)
17
+ module GraphQL
18
+ module Tracing
19
+ # AppOpticsTracing in the graphql gem may be a different version than the
20
+ # one defined here, we want to use the newer one
21
+ redefine = true
22
+ this_version = Gem::Version.new('1.0.0')
23
+
24
+ if defined?(GraphQL::Tracing::AppOpticsTracing)
25
+ if this_version > GraphQL::Tracing::AppOpticsTracing.version
26
+ send(:remove_const, :AppOpticsTracing)
27
+ else
28
+ redefine = false
29
+ end
30
+ end
31
+
32
+ if redefine
33
+ #-----------------------------------------------------------------------------#
34
+ #----- this class is duplicated in the graphql gem ---------------------------#
35
+ #-----------------------------------------------------------------------------#
36
+ class AppOpticsTracing < GraphQL::Tracing::PlatformTracing
37
+ # These GraphQL events will show up as 'graphql.prep' spans
38
+ PREP_KEYS = ['lex', 'parse', 'validate', 'analyze_query', 'analyze_multiplex'].freeze
39
+ EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
40
+
41
+ # During auto-instrumentation this version of AppOpticsTracing is compared
42
+ # with the version provided in the graphql gem, so that the newer
43
+ # version of the class can be used
44
+
45
+ def self.version
46
+ Gem::Version.new('1.0.0')
47
+ end
48
+
49
+ self.platform_keys = {
50
+ 'lex' => 'lex',
51
+ 'parse' => 'parse',
52
+ 'validate' => 'validate',
53
+ 'analyze_query' => 'analyze_query',
54
+ 'analyze_multiplex' => 'analyze_multiplex',
55
+ 'execute_multiplex' => 'execute_multiplex',
56
+ 'execute_query' => 'execute_query',
57
+ 'execute_query_lazy' => 'execute_query_lazy'
58
+ }
59
+
60
+ def platform_trace(platform_key, _key, data)
61
+ return yield if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
62
+
63
+ layer = span_name(platform_key)
64
+ kvs = metadata(data, layer)
65
+ kvs[:Key] = platform_key if (PREP_KEYS + EXEC_KEYS).include?(platform_key)
66
+
67
+ transaction_name(kvs[:InboundQuery]) if kvs[:InboundQuery] && layer == 'graphql.execute'
68
+
69
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
70
+ kvs.clear # we don't have to send them twice
71
+ yield
72
+ end
73
+ end
74
+
75
+ def platform_field_key(type, field)
76
+ "graphql.#{type.name}.#{field.name}"
77
+ end
78
+
79
+ private
80
+
81
+ def gql_config
82
+ ::AppOpticsAPM::Config[:graphql] ||= {}
83
+ end
84
+
85
+ def transaction_name(query)
86
+ return if gql_config[:transaction_name] == false ||
87
+ ::AppOpticsAPM::SDK.get_transaction_name
88
+
89
+ split_query = query.strip.split(/\W+/, 3)
90
+ split_query[0] = 'query' if split_query[0].empty?
91
+ name = "graphql.#{split_query[0..1].join('.')}"
92
+
93
+ ::AppOpticsAPM::SDK.set_transaction_name(name)
94
+ end
95
+
96
+ def multiplex_transaction_name(names)
97
+ return if gql_config[:transaction_name] == false ||
98
+ ::AppOpticsAPM::SDK.get_transaction_name
99
+
100
+ name = "graphql.multiplex.#{names.join('.')}"
101
+ name = "#{name[0..251]}..." if name.length > 254
102
+
103
+ ::AppOpticsAPM::SDK.set_transaction_name(name)
104
+ end
105
+
106
+ def span_name(key)
107
+ return 'graphql.prep' if PREP_KEYS.include?(key)
108
+ return 'graphql.execute' if EXEC_KEYS.include?(key)
109
+
110
+ key[/^graphql\./] ? key : "graphql.#{key}"
111
+ end
112
+
113
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
114
+ def metadata(data, layer)
115
+ data.keys.map do |key|
116
+ case key
117
+ when :context
118
+ graphql_context(data[key], layer)
119
+ when :query
120
+ graphql_query(data[key])
121
+ when :query_string
122
+ graphql_query_string(data[key])
123
+ when :multiplex
124
+ graphql_multiplex(data[key])
125
+ else
126
+ [key, data[key]]
127
+ end
128
+ end.flatten.each_slice(2).to_h.merge(Spec: 'graphql')
129
+ end
130
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
131
+
132
+ def graphql_context(context, layer)
133
+ context.errors && context.errors.each do |err|
134
+ AppOpticsAPM::API.log_exception(layer, err)
135
+ end
136
+
137
+ [[:Path, context.path.join('.')]]
138
+ end
139
+
140
+ def graphql_query(query)
141
+ return [] unless query
142
+
143
+ query_string = query.query_string
144
+ query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
145
+ query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
146
+
147
+ [[:InboundQuery, query_string],
148
+ [:Operation, query.selected_operation_name]]
149
+ end
150
+
151
+ def graphql_query_string(query_string)
152
+ query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
153
+ query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
154
+
155
+ [:InboundQuery, query_string]
156
+ end
157
+
158
+ def graphql_multiplex(data)
159
+ names = data.queries.map(&:operations).map(&:keys).flatten.compact
160
+ multiplex_transaction_name(names) if names.size > 1
161
+
162
+ [:Operations, names.join(', ')]
163
+ end
164
+
165
+ def sanitize(query)
166
+ return unless query
167
+
168
+ # remove arguments
169
+ query.gsub(/"[^"]*"/, '"?"') # strings
170
+ .gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats
171
+ .gsub(/\[[^\]]*\]/, '[?]') # arrays
172
+ end
173
+
174
+ def remove_comments(query)
175
+ return unless query
176
+
177
+ query.gsub(/#[^\n\r]*/, '')
178
+ end
179
+ end
180
+ #-----------------------------------------------------------------------------#
181
+ end
182
+ end
183
+ end
184
+
185
+ module AppOpticsAPM
186
+ module GraphQLSchemaPrepend
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 }
190
+
191
+ self.plugins
192
+ end
193
+
194
+ def inherited(subclass)
195
+ subclass.use(GraphQL::Tracing::AppOpticsTracing)
196
+ super
197
+ end
198
+ end
199
+
200
+ # rubocop:disable Style/RedundantSelf
201
+ module GraphQLErrorPrepend
202
+ def initialize(*args)
203
+ super
204
+ bt = AppOpticsAPM::API.backtrace(1)
205
+ set_backtrace(bt) unless self.backtrace
206
+ end
207
+ end
208
+ # rubocop:enable Style/RedundantSelf
209
+
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
221
+ end
222
+
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
239
+ end
240
+ end