instana 1.11.6 → 1.193.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of instana might be problematic. Click here for more details.

Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +168 -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/action_controller.rb +11 -0
  10. data/lib/instana/frameworks/instrumentation/mysql2_adapter.rb +7 -1
  11. data/lib/instana/frameworks/instrumentation/mysql_adapter.rb +7 -1
  12. data/lib/instana/frameworks/instrumentation/postgresql_adapter.rb +23 -5
  13. data/lib/instana/frameworks/roda.rb +41 -0
  14. data/lib/instana/frameworks/sinatra.rb +17 -0
  15. data/lib/instana/instrumentation/excon.rb +1 -1
  16. data/lib/instana/instrumentation/graphql.rb +77 -0
  17. data/lib/instana/instrumentation/net-http.rb +2 -0
  18. data/lib/instana/instrumentation/rack.rb +23 -3
  19. data/lib/instana/secrets.rb +42 -0
  20. data/lib/instana/setup.rb +1 -0
  21. data/lib/instana/test.rb +4 -3
  22. data/lib/instana/tracing/span.rb +23 -10
  23. data/lib/instana/version.rb +1 -1
  24. data/test/apps/cuba.rb +4 -0
  25. data/test/apps/roda.rb +3 -0
  26. data/test/apps/sinatra.rb +4 -0
  27. data/test/config_test.rb +1 -17
  28. data/test/frameworks/cuba_test.rb +14 -1
  29. data/test/frameworks/rack_test.rb +121 -69
  30. data/test/frameworks/rails/actioncontroller_test.rb +12 -0
  31. data/test/frameworks/rails/activerecord_test.rb +80 -28
  32. data/test/frameworks/roda_test.rb +14 -0
  33. data/test/frameworks/sinatra_test.rb +37 -15
  34. data/test/instrumentation/excon_test.rb +0 -2
  35. data/test/instrumentation/graphql_test.rb +116 -0
  36. data/test/instrumentation/grpc_test.rb +1 -1
  37. data/test/instrumentation/redis_test.rb +10 -0
  38. data/test/secrets_test.rb +73 -0
  39. data/test/tracing/tracer_test.rb +31 -1
  40. metadata +10 -6
  41. data/.travis.yml +0 -43
  42. data/test/tracing/trace_test.rb +0 -67
@@ -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
@@ -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
@@ -32,6 +32,8 @@ if defined?(::Net::HTTP) && ::Instana.config[:nethttp][:enabled]
32
32
  kv_payload[:http][:url] = "http://#{@address}:#{@port}#{request.path}"
33
33
  end
34
34
  end
35
+
36
+ kv_payload[:http][:url] = ::Instana.secrets.remove_from_query(kv_payload[:http][:url])
35
37
 
36
38
  # The core call
37
39
  response = request_without_instana(*args, &block)
@@ -8,8 +8,9 @@ module Instana
8
8
  @app = app
9
9
  end
10
10
 
11
- def call(env)
12
- kvs = { :http => {} }
11
+ def collect_kvs(env)
12
+ kvs = {}
13
+ kvs[:http] = {}
13
14
  kvs[:http][:method] = env['REQUEST_METHOD']
14
15
  kvs[:http][:url] = ::CGI.unescape(env['PATH_INFO'])
15
16
 
@@ -37,15 +38,27 @@ module Instana
37
38
  end
38
39
  }
39
40
  end
41
+ return kvs
42
+ end
40
43
 
44
+ def call(env)
41
45
  # Check incoming context
42
46
  incoming_context = {}
43
47
  if env.key?('HTTP_X_INSTANA_T')
44
48
  incoming_context[:trace_id] = ::Instana::Util.header_to_id(env['HTTP_X_INSTANA_T'])
45
49
  incoming_context[:span_id] = ::Instana::Util.header_to_id(env['HTTP_X_INSTANA_S']) if env.key?('HTTP_X_INSTANA_S')
46
50
  incoming_context[:level] = env['HTTP_X_INSTANA_L'] if env.key?('HTTP_X_INSTANA_L')
51
+
52
+ # Honor X-Instana-L
53
+ if incoming_context[:level] and incoming_context[:level].length > 0
54
+ if incoming_context[:level][0] == "0"
55
+ return @app.call(env)
56
+ end
57
+ end
47
58
  end
48
59
 
60
+ kvs = collect_kvs(env)
61
+
49
62
  ::Instana.tracer.log_start_or_continue(:rack, {}, incoming_context)
50
63
 
51
64
  status, headers, response = @app.call(env)
@@ -63,6 +76,11 @@ module Instana
63
76
  ::Instana.tracer.log_error(nil)
64
77
  end
65
78
 
79
+ # If the framework instrumentation provides a path template,
80
+ # pass it into the span here.
81
+ # See: https://www.instana.com/docs/tracing/custom-best-practices/#path-templates-visual-grouping-of-http-endpoints
82
+ kvs[:http][:path_tpl] = env['INSTANA_HTTP_PATH_TEMPLATE'] if env['INSTANA_HTTP_PATH_TEMPLATE']
83
+
66
84
  # Save the IDs before the trace ends so we can place
67
85
  # them in the response headers in the ensure block
68
86
  trace_id = ::Instana.tracer.current_span.trace_id
@@ -78,8 +96,10 @@ module Instana
78
96
  # Set reponse headers; encode as hex string
79
97
  headers['X-Instana-T'] = ::Instana::Util.id_to_header(trace_id)
80
98
  headers['X-Instana-S'] = ::Instana::Util.id_to_header(span_id)
99
+ headers['X-Instana-L'] = '1'
100
+ headers['Server-Timing'] = "intid;desc=#{::Instana::Util.id_to_header(trace_id)}"
101
+ ::Instana.tracer.log_end(:rack, kvs)
81
102
  end
82
- ::Instana.tracer.log_end(:rack, kvs)
83
103
  end
84
104
  end
85
105
  end
@@ -0,0 +1,42 @@
1
+ require 'uri'
2
+ require 'cgi'
3
+
4
+ module Instana
5
+ class Secrets
6
+ def remove_from_query(str, secret_values = Instana.agent.secret_values)
7
+ return str unless secret_values
8
+
9
+ url = URI(str)
10
+ params = CGI.parse(url.query)
11
+
12
+ redacted = params.map do |k, v|
13
+ needs_redaction = secret_values['list']
14
+ .any? { |t| matcher(secret_values['matcher']).(t,k) }
15
+ [k, needs_redaction ? '<redacted>' : v]
16
+ end
17
+
18
+ url.query = URI.encode_www_form(redacted)
19
+ CGI.unescape(url.to_s)
20
+ end
21
+
22
+ private
23
+
24
+ def matcher(name)
25
+ case name
26
+ when 'equals-ignore-case'
27
+ ->(expected, actual) { expected.casecmp(actual) == 0 }
28
+ when 'equals'
29
+ ->(expected, actual) { (expected <=> actual) == 0 }
30
+ when 'contains-ignore-case'
31
+ ->(expected, actual) { actual.downcase.include?(expected) }
32
+ when 'contains'
33
+ ->(expected, actual) { actual.include?(expected) }
34
+ when 'regex'
35
+ ->(expected, actual) { !Regexp.new(expected).match(actual).nil? }
36
+ else
37
+ ::Instana.logger.warn("Matcher #{name} is not supported.")
38
+ lambda { false }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -4,6 +4,7 @@ require "instana/base"
4
4
  require "instana/config"
5
5
  require "instana/agent"
6
6
  require "instana/collector"
7
+ require "instana/secrets"
7
8
  require "instana/tracer"
8
9
  require "instana/tracing/processor"
9
10
  require "instana/instrumentation"
@@ -7,14 +7,15 @@ module Instana
7
7
  def setup_environment
8
8
  # Set defaults if not set
9
9
  ENV['MEMCACHED_HOST'] ||= '127.0.0.1:11211'
10
- ENV['TRAVIS_PSQL_HOST'] ||= "127.0.0.1"
11
- ENV['TRAVIS_PSQL_USER'] ||= "postgres"
10
+ ENV['POSTGRES_HOST'] ||= "127.0.0.1"
11
+ ENV['POSTGRES_USER'] ||= "stan"
12
+ ENV['POSTGRES_PASSWORD'] ||= "stanlikesdata"
12
13
  ENV['TRAVIS_MYSQL_HOST'] ||= "127.0.0.1"
13
14
  ENV['TRAVIS_MYSQL_USER'] ||= "root"
14
15
 
15
16
  if !ENV.key?('DATABASE_URL')
16
17
  if ENV['DB_FLAVOR'] == 'postgresql'
17
- ENV['DATABASE_URL'] = "postgresql://#{ENV['TRAVIS_PSQL_USER']}:#{ENV['TRAVIS_PSQL_PASS']}@#{ENV['TRAVIS_PSQL_HOST']}:5432/travis_ci_test"
18
+ ENV['DATABASE_URL'] = "postgresql://#{ENV['POSTGRES_USER']}:#{ENV['POSTGRES_PASSWORD']}@#{ENV['POSTGRES_HOST']}:5432/#{ENV['POSTGRES_USER']}"
18
19
  elsif ENV['DB_FLAVOR'] == 'mysql'
19
20
  ENV['DATABASE_URL'] = "mysql://#{ENV['TRAVIS_MYSQL_USER']}:#{ENV['TRAVIS_MYSQL_PASS']}@#{ENV['TRAVIS_MYSQL_HOST']}:3306/travis_ci_test"
20
21
  else
@@ -3,8 +3,8 @@ module Instana
3
3
  REGISTERED_SPANS = [ :actioncontroller, :actionview, :activerecord, :excon,
4
4
  :memcache, :'net-http', :rack, :render, :'rpc-client',
5
5
  :'rpc-server', :'sidekiq-client', :'sidekiq-worker',
6
- :redis, :'resque-client', :'resque-worker' ].freeze
7
- ENTRY_SPANS = [ :rack, :'resque-worker', :'rpc-server', :'sidekiq-worker' ].freeze
6
+ :redis, :'resque-client', :'resque-worker', :'graphql.server' ].freeze
7
+ ENTRY_SPANS = [ :rack, :'resque-worker', :'rpc-server', :'sidekiq-worker', :'graphql.server' ].freeze
8
8
  EXIT_SPANS = [ :activerecord, :excon, :'net-http', :'resque-client',
9
9
  :'rpc-client', :'sidekiq-client', :redis ].freeze
10
10
  HTTP_SPANS = [ :rack, :excon, :'net-http' ].freeze
@@ -57,9 +57,6 @@ module Instana
57
57
  end
58
58
 
59
59
  if ::Instana.config[:collect_backtraces]
60
- # For entry spans, add a backtrace fingerprint
61
- add_stack(limit: 2) if ENTRY_SPANS.include?(name)
62
-
63
60
  # Attach a backtrace to all exit spans
64
61
  add_stack if EXIT_SPANS.include?(name)
65
62
  end
@@ -76,9 +73,11 @@ module Instana
76
73
  #
77
74
  # @param limit [Integer] Limit the backtrace to the top <limit> frames
78
75
  #
79
- def add_stack(limit: nil, stack: Kernel.caller)
76
+ def add_stack(limit: 30, stack: Kernel.caller)
80
77
  frame_count = 0
78
+ sanitized_stack = []
81
79
  @data[:stack] = []
80
+ limit = 40 if limit > 40
82
81
 
83
82
  stack.each do |i|
84
83
  # If the stack has the full instana gem version in it's path
@@ -86,18 +85,23 @@ module Instana
86
85
  if !i.match(/instana\/instrumentation\/rack.rb/).nil? ||
87
86
  (i.match(::Instana::VERSION_FULL).nil? && i.match('lib/instana/').nil?)
88
87
 
89
- break if limit && frame_count >= limit
90
-
91
88
  x = i.split(':')
92
89
 
93
- @data[:stack] << {
90
+ sanitized_stack << {
94
91
  :c => x[0],
95
92
  :n => x[1],
96
93
  :m => x[2]
97
94
  }
98
- frame_count = frame_count + 1 if limit
99
95
  end
100
96
  end
97
+
98
+ if sanitized_stack.length > limit
99
+ # (limit * -1) gives us negative form of <limit> used for
100
+ # slicing from the end of the list. e.g. stack[-30, 30]
101
+ @data[:stack] = sanitized_stack[limit*-1, limit]
102
+ else
103
+ @data[:stack] = sanitized_stack
104
+ end
101
105
  end
102
106
 
103
107
  # Log an error into the span
@@ -303,6 +307,15 @@ module Instana
303
307
  # a String, Numeric, or Boolean it will be encoded with to_s
304
308
  #
305
309
  def set_tag(key, value)
310
+ if ![Symbol, String].include?(key.class)
311
+ key = key.to_s
312
+ end
313
+
314
+ # If <value> is not a Symbol, String, Array, Hash or Numeric - convert to string
315
+ if ![Symbol, String, Array, TrueClass, FalseClass, Hash].include?(value.class) && !value.is_a?(Numeric)
316
+ value = value.to_s
317
+ end
318
+
306
319
  if custom?
307
320
  @data[:data][:sdk][:custom] ||= {}
308
321
  @data[:data][:sdk][:custom][:tags] ||= {}
@@ -1,4 +1,4 @@
1
1
  module Instana
2
- VERSION = "1.11.6"
2
+ VERSION = "1.193.1"
3
3
  VERSION_FULL = "instana-#{VERSION}"
4
4
  end
@@ -7,6 +7,10 @@ Cuba.define do
7
7
  on "hello" do
8
8
  res.write "Hello Instana!"
9
9
  end
10
+
11
+ on "greet/:name" do |name|
12
+ res.write "Hello, #{name}"
13
+ end
10
14
 
11
15
  on root do
12
16
  res.redirect '/hello'
@@ -6,5 +6,8 @@ class InstanaRodaApp < Roda
6
6
  r.get "hello" do
7
7
  "Hello Roda + Instana"
8
8
  end
9
+ r.get "greet", String do |name|
10
+ "Hello, #{name}!"
11
+ end
9
12
  end
10
13
  end
@@ -2,4 +2,8 @@ class InstanaSinatraApp < ::Sinatra::Base
2
2
  get '/' do
3
3
  "Hello Sinatra!"
4
4
  end
5
+
6
+ get '/greet/:name' do
7
+ "Hello, #{params[:name]}!"
8
+ end
5
9
  end
@@ -10,28 +10,12 @@ class ConfigTest < Minitest::Test
10
10
  assert_equal '127.0.0.1', ::Instana.config[:agent_host]
11
11
  assert_equal 42699, ::Instana.config[:agent_port]
12
12
 
13
- assert ::Instana.config[:enabled]
14
13
  assert ::Instana.config[:tracing][:enabled]
15
14
  assert ::Instana.config[:metrics][:enabled]
16
15
 
17
16
  ::Instana.config[:metrics].each do |k, v|
17
+ next unless v.is_a? Hash
18
18
  assert_equal true, ::Instana.config[:metrics][k].key?(:enabled)
19
19
  end
20
20
  end
21
-
22
- def test_that_global_affects_children
23
- # Disabling the gem should explicitly disable
24
- # metrics and tracing flags
25
- ::Instana.config[:enabled] = false
26
-
27
- assert_equal false, ::Instana.config[:tracing][:enabled]
28
- assert_equal false, ::Instana.config[:metrics][:enabled]
29
-
30
- # Enabling the gem should explicitly enable
31
- # metrics and tracing flags
32
- ::Instana.config[:enabled] = true
33
-
34
- assert_equal ::Instana.config[:tracing][:enabled]
35
- assert_equal ::Instana.config[:metrics][:enabled]
36
- end
37
21
  end