instana 1.11.8 → 1.193.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +10 -0
  3. data/Rakefile +26 -37
  4. data/gemfiles/libraries.gemfile +2 -0
  5. data/lib/instana/agent.rb +6 -0
  6. data/lib/instana/base.rb +2 -0
  7. data/lib/instana/config.rb +11 -0
  8. data/lib/instana/frameworks/cuba.rb +33 -0
  9. data/lib/instana/frameworks/instrumentation/abstract_mysql_adapter.rb +5 -7
  10. data/lib/instana/frameworks/instrumentation/action_controller.rb +11 -0
  11. data/lib/instana/frameworks/instrumentation/action_view.rb +6 -10
  12. data/lib/instana/frameworks/instrumentation/active_record.rb +4 -4
  13. data/lib/instana/frameworks/instrumentation/mysql2_adapter.rb +17 -15
  14. data/lib/instana/frameworks/instrumentation/mysql_adapter.rb +11 -7
  15. data/lib/instana/frameworks/instrumentation/postgresql_adapter.rb +33 -19
  16. data/lib/instana/frameworks/roda.rb +41 -0
  17. data/lib/instana/frameworks/sinatra.rb +17 -0
  18. data/lib/instana/instrumentation/dalli.rb +9 -14
  19. data/lib/instana/instrumentation/excon.rb +1 -1
  20. data/lib/instana/instrumentation/graphql.rb +77 -0
  21. data/lib/instana/instrumentation/grpc.rb +72 -62
  22. data/lib/instana/instrumentation/instrumented_request.rb +68 -0
  23. data/lib/instana/instrumentation/net-http.rb +44 -43
  24. data/lib/instana/instrumentation/rack.rb +17 -52
  25. data/lib/instana/instrumentation/redis.rb +15 -18
  26. data/lib/instana/instrumentation/resque.rb +17 -28
  27. data/lib/instana/instrumentation/rest-client.rb +3 -13
  28. data/lib/instana/secrets.rb +42 -0
  29. data/lib/instana/setup.rb +1 -0
  30. data/lib/instana/tracer.rb +6 -0
  31. data/lib/instana/tracing/span.rb +14 -10
  32. data/lib/instana/util.rb +15 -69
  33. data/lib/instana/version.rb +1 -1
  34. data/test/apps/cuba.rb +4 -0
  35. data/test/apps/roda.rb +3 -0
  36. data/test/apps/sinatra.rb +4 -0
  37. data/test/config_test.rb +1 -17
  38. data/test/frameworks/cuba_test.rb +14 -1
  39. data/test/frameworks/rack_test.rb +52 -19
  40. data/test/frameworks/rails/actioncontroller_test.rb +12 -0
  41. data/test/frameworks/rails/activerecord_test.rb +80 -28
  42. data/test/frameworks/roda_test.rb +14 -0
  43. data/test/frameworks/sinatra_test.rb +14 -0
  44. data/test/instrumentation/excon_test.rb +0 -2
  45. data/test/instrumentation/graphql_test.rb +116 -0
  46. data/test/instrumentation/instrumented_request_test.rb +84 -0
  47. data/test/instrumentation/redis_test.rb +10 -0
  48. data/test/secrets_test.rb +73 -0
  49. data/test/test_helper.rb +3 -9
  50. data/test/tracing/id_management_test.rb +4 -66
  51. metadata +16 -6
@@ -8,16 +8,11 @@ module Instana
8
8
  # This module supports instrumenting ActiveRecord with the postgresql adapter. Only
9
9
  # versions >= 3.1 are supported.
10
10
  #
11
- def self.included(klass)
11
+ def self.prepended(klass)
12
12
  if (::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR > 0) ||
13
13
  ::ActiveRecord::VERSION::MAJOR >= 4
14
14
 
15
15
  # ActiveRecord 3.1 and up
16
- Instana::Util.method_alias(klass, :exec_query)
17
- Instana::Util.method_alias(klass, :exec_delete)
18
- Instana::Util.method_alias(klass, :execute)
19
-
20
-
21
16
  @@sanitize_regexp = Regexp.new('(\'[\s\S][^\']*\'|\d*\.\d+|\d+|NULL)', Regexp::IGNORECASE)
22
17
  end
23
18
  end
@@ -27,14 +22,33 @@ module Instana
27
22
  # @param sql [String]
28
23
  # @return [Hash] Hash of collected KVs
29
24
  #
30
- def collect(sql)
25
+ def collect(sql, binds = nil)
31
26
  payload = { :activerecord => {} }
32
- payload[:activerecord][:sql] = sql.gsub(@@sanitize_regexp, '?')
27
+
33
28
  payload[:activerecord][:adapter] = @config[:adapter]
34
29
  payload[:activerecord][:host] = @config[:host]
35
30
  payload[:activerecord][:db] = @config[:database]
36
31
  payload[:activerecord][:username] = @config[:username]
32
+
33
+ if ::Instana.config[:sanitize_sql]
34
+ payload[:activerecord][:sql] = sql.gsub(@@sanitize_regexp, '?')
35
+ else
36
+ # No sanitization so raw SQL and collect up binds
37
+ payload[:activerecord][:sql] = sql
38
+
39
+ # FIXME: Only works on Rails 5 as the bind format varied in previous versions of Rails
40
+ if binds.is_a?(Array)
41
+ raw_binds = []
42
+ binds.each { |x| raw_binds << x.value_before_type_cast }
43
+ payload[:activerecord][:binds] = raw_binds
44
+ end
45
+ end
46
+
37
47
  payload
48
+ rescue Exception => e
49
+ ::Instana.logger.debug { "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" }
50
+ ensure
51
+ return payload
38
52
  end
39
53
 
40
54
  # In the spirit of ::ActiveRecord::ExplainSubscriber.ignore_payload? There are
@@ -48,36 +62,36 @@ module Instana
48
62
  IGNORED_PAYLOADS.include?(name) || IGNORED_SQL.include?(sql)
49
63
  end
50
64
 
51
- def exec_query_with_instana(sql, name = 'SQL', binds = [], *args)
65
+ def exec_query(sql, name = 'SQL', binds = [], *args)
52
66
  if !::Instana.tracer.tracing? || ignore_payload?(name, sql)
53
- return exec_query_without_instana(sql, name, binds, *args)
67
+ return super(sql, name, binds, *args)
54
68
  end
55
69
 
56
- kv_payload = collect(sql)
70
+ kv_payload = collect(sql, binds)
57
71
  ::Instana.tracer.trace(:activerecord, kv_payload) do
58
- exec_query_without_instana(sql, name, binds, *args)
72
+ super(sql, name, binds, *args)
59
73
  end
60
74
  end
61
75
 
62
- def exec_delete_with_instana(sql, name = nil, binds = [])
76
+ def exec_delete(sql, name = nil, binds = [])
63
77
  if !::Instana.tracer.tracing? || ignore_payload?(name, sql)
64
- return exec_delete_without_instana(sql, name, binds)
78
+ return super(sql, name, binds)
65
79
  end
66
80
 
67
- kv_payload = collect(sql)
81
+ kv_payload = collect(sql, binds)
68
82
  ::Instana.tracer.trace(:activerecord, kv_payload) do
69
- exec_delete_without_instana(sql, name, binds)
83
+ super(sql, name, binds)
70
84
  end
71
85
  end
72
86
 
73
- def execute_with_instana(sql, name = nil)
87
+ def execute(sql, name = nil)
74
88
  if !::Instana.tracer.tracing? || ignore_payload?(name, sql)
75
- return execute_without_instana(sql, name)
89
+ return super(sql, name)
76
90
  end
77
91
 
78
92
  kv_payload = collect(sql)
79
93
  ::Instana.tracer.trace(:activerecord, kv_payload) do
80
- execute_without_instana(sql, name)
94
+ super(sql, name)
81
95
  end
82
96
  end
83
97
  end
@@ -1,6 +1,47 @@
1
1
  require "instana/rack"
2
2
 
3
+ module Instana
4
+ module RodaPathTemplateExtractor
5
+ module RequestMethods
6
+ TERM = defined?(::Roda) ? ::Roda::RodaPlugins::Base::RequestMethods::TERM : Object
7
+
8
+ def if_match(args, &blk)
9
+ path = @remaining_path
10
+ captures = @captures.clear
11
+
12
+ if match_all(args)
13
+ (env['INSTANA_PATH_TEMPLATE_FRAGMENTS'] ||= []).concat(named_args(args, blk))
14
+ block_result(blk.(*captures))
15
+ env['INSTANA_HTTP_PATH_TEMPLATE'] = env['INSTANA_PATH_TEMPLATE_FRAGMENTS']
16
+ .join('/')
17
+ .prepend('/')
18
+ throw :halt, response.finish
19
+ else
20
+ @remaining_path = path
21
+ false
22
+ end
23
+ end
24
+
25
+ def named_args(args, blk)
26
+ parameters = blk.parameters
27
+ args.map do |a|
28
+ case a
29
+ when String
30
+ a
31
+ when TERM
32
+ nil
33
+ else
34
+ _, name = parameters.pop
35
+ "{#{name}}"
36
+ end
37
+ end.compact
38
+ end
39
+ end
40
+ end
41
+ end
42
+
3
43
  if defined?(::Roda)
4
44
  ::Instana.logger.debug "Instrumenting Roda"
5
45
  Roda.use ::Instana::Rack
46
+ Roda.plugin ::Instana::RodaPathTemplateExtractor
6
47
  end
@@ -3,7 +3,24 @@ require "instana/rack"
3
3
  # This instrumentation will insert Rack into Sinatra _and_ Padrino since
4
4
  # the latter is based on Sinatra
5
5
 
6
+ module Instana
7
+ module SinatraPathTemplateExtractor
8
+ def self.extended(base)
9
+ ::Instana.logger.debug "#{base} extended #{self}"
10
+ base.store_path_template
11
+ end
12
+
13
+ def store_path_template
14
+ after do
15
+ @env["INSTANA_HTTP_PATH_TEMPLATE"] = @env["sinatra.route"]
16
+ .sub("#{@request.request_method} ", '')
17
+ end
18
+ end
19
+ end
20
+ end
21
+
6
22
  if defined?(::Sinatra)
7
23
  ::Instana.logger.debug "Instrumenting Sinatra"
8
24
  ::Sinatra::Base.use ::Instana::Rack
25
+ ::Sinatra::Base.register ::Instana::SinatraPathTemplateExtractor
9
26
  end
@@ -1,15 +1,10 @@
1
1
  module Instana
2
2
  module Instrumentation
3
3
  module Dalli
4
- def self.included(klass)
5
- ::Instana::Util.method_alias(klass, :perform)
6
- ::Instana::Util.method_alias(klass, :get_multi)
7
- end
8
-
9
- def perform_with_instana(*args, &blk)
4
+ def perform(*args, &blk)
10
5
  if !::Instana.tracer.tracing? || ::Instana.tracer.tracing_span?(:memcache)
11
6
  do_skip = true
12
- return perform_without_instana(*args, &blk)
7
+ return super(*args, &blk)
13
8
  end
14
9
 
15
10
  op, key, *_opts = args
@@ -22,7 +17,7 @@ module Instana
22
17
  ::Instana.tracer.log_entry(:memcache, entry_payload)
23
18
  exit_payload = { :memcache => {} }
24
19
 
25
- result = perform_without_instana(*args, &blk)
20
+ result = super(*args, &blk)
26
21
 
27
22
  if op == :get
28
23
  exit_payload[:memcache][:hit] = result ? 1 : 0
@@ -36,7 +31,7 @@ module Instana
36
31
  ::Instana.tracer.log_exit(:memcache, exit_payload) unless do_skip
37
32
  end
38
33
 
39
- def get_multi_with_instana(*keys)
34
+ def get_multi(*keys)
40
35
  entry_payload = { :memcache => {} }
41
36
  entry_payload[:memcache][:namespace] = @options[:namespace] if @options.key?(:namespace)
42
37
  entry_payload[:memcache][:command] = :get_multi
@@ -45,7 +40,7 @@ module Instana
45
40
  ::Instana.tracer.log_entry(:memcache, entry_payload)
46
41
  exit_payload = { :memcache => {} }
47
42
 
48
- result = get_multi_without_instana(*keys)
43
+ result = super(*keys)
49
44
 
50
45
  exit_payload[:memcache][:hits] = result.length
51
46
  result
@@ -63,13 +58,13 @@ module Instana
63
58
  ::Instana::Util.method_alias(klass, :request)
64
59
  end
65
60
 
66
- def request_with_instana(op, *args)
61
+ def request(op, *args)
67
62
  if ::Instana.tracer.tracing? || ::Instana.tracer.tracing_span?(:memcache)
68
63
  info_payload = { :memcache => {} }
69
64
  info_payload[:memcache][:server] = "#{@hostname}:#{@port}"
70
65
  ::Instana.tracer.log_info(info_payload)
71
66
  end
72
- request_without_instana(op, *args)
67
+ super(op, *args)
73
68
  end
74
69
  end
75
70
  end
@@ -77,6 +72,6 @@ end
77
72
 
78
73
  if defined?(::Dalli) && ::Instana.config[:dalli][:enabled]
79
74
  ::Instana.logger.debug "Instrumenting Dalli"
80
- ::Dalli::Client.send(:include, ::Instana::Instrumentation::Dalli)
81
- ::Dalli::Server.send(:include, ::Instana::Instrumentation::DalliServer)
75
+ ::Dalli::Client.send(:prepend, ::Instana::Instrumentation::Dalli)
76
+ ::Dalli::Server.send(:prepend, ::Instana::Instrumentation::DalliServer)
82
77
  end
@@ -7,7 +7,7 @@ if defined?(::Excon) && ::Instana.config[:excon][:enabled]
7
7
 
8
8
  payload = { :http => {} }
9
9
  path = datum[:path].split('?').first
10
- payload[:http][:url] = "#{datum[:connection].instance_variable_get(:@socket_key)}#{path}"
10
+ payload[:http][:url] = ::Instana.secrets.remove_from_query("#{datum[:connection].instance_variable_get(:@socket_key)}#{path}")
11
11
  payload[:http][:method] = datum[:method] if datum.key?(:method)
12
12
 
13
13
  if datum[:pipeline] == true
@@ -0,0 +1,77 @@
1
+ if defined?(GraphQL::Schema) && defined?(GraphQL::Tracing::PlatformTracing) && ::Instana.config[:graphql][:enabled]
2
+ module Instana
3
+ class GraphqlTracing < GraphQL::Tracing::PlatformTracing
4
+ self.platform_keys = {
5
+ 'lex' => 'lex.graphql',
6
+ 'parse' => 'parse.graphql',
7
+ 'validate' => 'validate.graphql',
8
+ 'analyze_query' => 'analyze.graphql',
9
+ 'analyze_multiplex' => 'analyze.graphql',
10
+ 'execute_multiplex' => 'execute.graphql',
11
+ 'execute_query' => 'execute.graphql',
12
+ 'execute_query_lazy' => 'execute.graphql',
13
+ }
14
+
15
+ def platform_trace(platform_key, key, data)
16
+ return yield unless key == 'execute_query'
17
+ operation = data[:query].selected_operation
18
+
19
+ arguments = []
20
+ fields = []
21
+
22
+ operation.selections.each do |field|
23
+ arguments.concat(walk_fields(field, :arguments))
24
+ fields.concat(walk_fields(field, :selections))
25
+ end
26
+
27
+ payload = {
28
+ operationName: data[:query].operation_name || 'anonymous',
29
+ operationType: operation.operation_type,
30
+ arguments: grouped_fields(arguments),
31
+ fields: grouped_fields(fields),
32
+ }
33
+
34
+ begin
35
+ ::Instana.tracer.log_entry(:'graphql.server')
36
+ yield
37
+ rescue Exception => e
38
+ ::Instana.tracer.log_error(e)
39
+ raise e
40
+ ensure
41
+ ::Instana.tracer.log_exit(:'graphql.server', {graphql: payload})
42
+ end
43
+ end
44
+
45
+ def platform_field_key(type, field)
46
+ "#{type.graphql_name}.#{field.graphql_name}"
47
+ end
48
+
49
+ def platform_authorized_key(type)
50
+ "#{type.graphql_name}.authorized.graphql"
51
+ end
52
+
53
+ def platform_resolve_type_key(type)
54
+ "#{type.graphql_name}.resolve_type.graphql"
55
+ end
56
+
57
+ private
58
+
59
+ def walk_fields(parent, method)
60
+ return [] unless parent.respond_to?(method)
61
+
62
+ parent.send(method).map do |field|
63
+ [{object: parent.name, field: field.name}] + walk_fields(field, method)
64
+ end.flatten
65
+ end
66
+
67
+ def grouped_fields(fields)
68
+ fields
69
+ .group_by { |p| p[:object] }
70
+ .map { |name, p| [name, p.map { |f| f[:field] }] }
71
+ .to_h
72
+ end
73
+ end
74
+ end
75
+
76
+ ::GraphQL::Schema.use(::Instana::GraphqlTracing)
77
+ end
@@ -1,84 +1,94 @@
1
1
  call_types = [:request_response, :client_streamer, :server_streamer, :bidi_streamer]
2
2
 
3
3
  if defined?(GRPC::ActiveCall) && ::Instana.config[:grpc][:enabled]
4
- call_types.each do |call_type|
5
- GRPC::ClientStub.class_eval <<-RUBY, __FILE__, __LINE__ + 1
6
- def #{call_type}_with_instana(method, *others, **options)
7
- kvs = { rpc: {} }
4
+ module Instana
5
+ module GRPCCientInstrumentation
6
+ CALL_TYPES = [:request_response, :client_streamer, :server_streamer, :bidi_streamer]
8
7
 
9
- unless ::Instana.tracer.tracing?
10
- return #{call_type}_without_instana(method, *others, **options)
11
- end
8
+ CALL_TYPES.each do |call_type|
9
+ define_method(call_type) do |method, *others, **options|
10
+ begin
11
+ kvs = { rpc: {} }
12
12
 
13
- kvs[:rpc][:flavor] = :grpc
14
- kvs[:rpc][:host] = @host
15
- kvs[:rpc][:call] = method
16
- kvs[:rpc][:call_type] = :#{call_type}
13
+ unless ::Instana.tracer.tracing?
14
+ return super(method, *others, **options)
15
+ end
17
16
 
18
- ::Instana.tracer.log_entry(:'rpc-client', kvs)
17
+ kvs[:rpc][:flavor] = :grpc
18
+ kvs[:rpc][:host] = @host
19
+ kvs[:rpc][:call] = method
20
+ kvs[:rpc][:call_type] = call_type
19
21
 
20
- context = ::Instana.tracer.context
21
- if context
22
- options[:metadata] = (options[:metadata] || {}).merge(
23
- 'x-instana-t' => context.trace_id_header,
24
- 'x-instana-s' => context.span_id_header
25
- )
26
- end
22
+ ::Instana.tracer.log_entry(:'rpc-client', kvs)
27
23
 
28
- #{call_type}_without_instana(method, *others, **options)
29
- rescue => e
30
- kvs[:rpc][:error] = true
31
- ::Instana.tracer.log_info(kvs)
32
- ::Instana.tracer.log_error(e)
33
- raise
34
- ensure
35
- ::Instana.tracer.log_exit(:'rpc-client', {})
36
- end
24
+ context = ::Instana.tracer.context
25
+ if context
26
+ options[:metadata] = (options[:metadata] || {}).merge(
27
+ 'x-instana-t' => context.trace_id_header,
28
+ 'x-instana-s' => context.span_id_header
29
+ )
30
+ end
37
31
 
38
- alias #{call_type}_without_instana #{call_type}
39
- alias #{call_type} #{call_type}_with_instana
40
- RUBY
32
+ super(method, *others, **options)
33
+ rescue => e
34
+ kvs[:rpc][:error] = true
35
+ ::Instana.tracer.log_info(kvs)
36
+ ::Instana.tracer.log_error(e)
37
+ raise
38
+ ensure
39
+ ::Instana.tracer.log_exit(:'rpc-client', {})
40
+ end
41
+ end
42
+ end
43
+ end
41
44
  end
45
+
42
46
  ::Instana.logger.debug 'Instrumenting GRPC client'
47
+ ::GRPC::ClientStub.prepend(::Instana::GRPCCientInstrumentation)
43
48
  end
44
49
 
45
50
  if defined?(GRPC::RpcDesc) && ::Instana.config[:grpc][:enabled]
46
- call_types.each do |call_type|
47
- GRPC::RpcDesc.class_eval <<-RUBY, __FILE__, __LINE__ + 1
48
- def handle_#{call_type}_with_instana(active_call, mth, *others)
49
- kvs = { rpc: {} }
50
- metadata = active_call.metadata
51
+ module Instana
52
+ module GRPCServerInstrumentation
53
+ CALL_TYPES = [:request_response, :client_streamer, :server_streamer, :bidi_streamer]
51
54
 
52
- incoming_context = {}
53
- if metadata.key?('x-instana-t')
54
- incoming_context[:trace_id] = ::Instana::Util.header_to_id(metadata['x-instana-t'])
55
- incoming_context[:span_id] = ::Instana::Util.header_to_id(metadata['x-instana-s']) if metadata.key?('x-instana-s')
56
- incoming_context[:level] = metadata['x-instana-l'] if metadata.key?('x-instana-l')
57
- end
55
+ CALL_TYPES.each do |call_type|
56
+ define_method(:"handle_#{call_type}") do |active_call, mth, *others|
57
+ begin
58
+ kvs = { rpc: {} }
59
+ metadata = active_call.metadata
58
60
 
59
- kvs[:rpc][:flavor] = :grpc
60
- kvs[:rpc][:host] = Socket.gethostname
61
- kvs[:rpc][:call] = "/\#{mth.owner.service_name}/\#{name}"
62
- kvs[:rpc][:call_type] = :#{call_type}
63
- kvs[:rpc][:peer] = { address: active_call.peer }
61
+ incoming_context = {}
62
+ if metadata.key?('x-instana-t')
63
+ incoming_context[:trace_id] = ::Instana::Util.header_to_id(metadata['x-instana-t'])
64
+ incoming_context[:span_id] = ::Instana::Util.header_to_id(metadata['x-instana-s']) if metadata.key?('x-instana-s')
65
+ incoming_context[:level] = metadata['x-instana-l'] if metadata.key?('x-instana-l')
66
+ end
64
67
 
65
- ::Instana.tracer.log_start_or_continue(
66
- :'rpc-server', kvs, incoming_context
67
- )
68
+ kvs[:rpc][:flavor] = :grpc
69
+ kvs[:rpc][:host] = Socket.gethostname
70
+ kvs[:rpc][:call] = "/#{mth.owner.service_name}/#{name}"
71
+ kvs[:rpc][:call_type] = call_type
72
+ kvs[:rpc][:peer] = { address: active_call.peer }
68
73
 
69
- handle_#{call_type}_without_instana(active_call, mth, *others)
70
- rescue => e
71
- kvs[:rpc][:error] = true
72
- ::Instana.tracer.log_info(kvs)
73
- ::Instana.tracer.log_error(e)
74
- raise
75
- ensure
76
- ::Instana.tracer.log_end(:'rpc-server', {}) if ::Instana.tracer.tracing?
77
- end
74
+ ::Instana.tracer.log_start_or_continue(
75
+ :'rpc-server', kvs, incoming_context
76
+ )
78
77
 
79
- alias handle_#{call_type}_without_instana handle_#{call_type}
80
- alias handle_#{call_type} handle_#{call_type}_with_instana
81
- RUBY
78
+ super(active_call, mth, *others)
79
+ rescue => e
80
+ kvs[:rpc][:error] = true
81
+ ::Instana.tracer.log_info(kvs)
82
+ ::Instana.tracer.log_error(e)
83
+ raise
84
+ ensure
85
+ ::Instana.tracer.log_end(:'rpc-server', {}) if ::Instana.tracer.tracing?
86
+ end
87
+ end
88
+ end
89
+ end
82
90
  end
91
+
83
92
  ::Instana.logger.debug 'Instrumenting GRPC server'
93
+ ::GRPC::RpcDesc.prepend(::Instana::GRPCServerInstrumentation)
84
94
  end