instana 1.11.8 → 1.193.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 (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