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
@@ -1,34 +1,24 @@
1
1
  module Instana
2
2
  module Instrumentation
3
3
  module RestClientRequest
4
- def self.included(klass)
5
- if klass.method_defined?(:execute)
6
- klass.class_eval do
7
- alias execute_without_instana execute
8
- alias execute execute_with_instana
9
- end
10
- end
11
- end
12
-
13
- def execute_with_instana & block
4
+ def execute(&block)
14
5
  # Since RestClient uses net/http under the covers, we just
15
6
  # provide span visibility here. HTTP related KVs are reported
16
7
  # in the Net::HTTP instrumentation
17
8
  ::Instana.tracer.log_entry(:'rest-client')
18
9
 
19
- execute_without_instana(&block)
10
+ super(&block)
20
11
  rescue => e
21
12
  ::Instana.tracer.log_error(e)
22
13
  raise
23
14
  ensure
24
15
  ::Instana.tracer.log_exit(:'rest-client')
25
16
  end
26
-
27
17
  end
28
18
  end
29
19
  end
30
20
 
31
21
  if defined?(::RestClient::Request) && ::Instana.config[:'rest-client'][:enabled]
32
22
  ::Instana.logger.debug "Instrumenting RestClient"
33
- ::RestClient::Request.send(:include, ::Instana::Instrumentation::RestClientRequest)
23
+ ::RestClient::Request.send(:prepend, ::Instana::Instrumentation::RestClientRequest)
34
24
  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"
@@ -100,6 +100,12 @@ module Instana
100
100
  else
101
101
  self.current_span = Span.new(name)
102
102
  end
103
+
104
+ if incoming_context.is_a?(Hash) && incoming_context[:correlation] && !incoming_context[:correlation].empty?
105
+ self.current_span[:crid] = incoming_context[:correlation][:id]
106
+ self.current_span[:crtp] = incoming_context[:correlation][:type]
107
+ end
108
+
103
109
  self.current_span.set_tags(kvs) unless kvs.empty?
104
110
  self.current_span
105
111
  end
@@ -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
@@ -2,46 +2,6 @@ module Instana
2
2
  module Util
3
3
  class << self
4
4
  ID_RANGE = -2**63..2**63-1
5
-
6
- # An agnostic approach to method aliasing.
7
- #
8
- # @param klass [Object] The class or module that holds the method to be alias'd.
9
- # @param method [Symbol] The name of the method to be aliased.
10
- #
11
- def method_alias(klass, method)
12
- if klass.method_defined?(method.to_sym) ||
13
- klass.private_method_defined?(method.to_sym)
14
-
15
- with = "#{method}_with_instana"
16
- without = "#{method}_without_instana"
17
-
18
- klass.class_eval do
19
- alias_method without, method.to_s
20
- alias_method method.to_s, with
21
- end
22
- else
23
- ::Instana.logger.debug "No such method (#{method}) to alias on #{klass}"
24
- end
25
- end
26
-
27
- # Calls on target_class to 'extend' cls
28
- #
29
- # @param target_cls [Object] the class/module to do the 'extending'
30
- # @param cls [Object] the class/module to be 'extended'
31
- #
32
- def send_extend(target_cls, cls)
33
- target_cls.send(:extend, cls) if defined?(target_cls)
34
- end
35
-
36
- # Calls on <target_cls> to include <cls> into itself.
37
- #
38
- # @param target_cls [Object] the class/module to do the 'including'
39
- # @param cls [Object] the class/module to be 'included'
40
- #
41
- def send_include(target_cls, cls)
42
- target_cls.send(:include, cls) if defined?(target_cls)
43
- end
44
-
45
5
  # Debugging helper method
46
6
  #
47
7
  def pry!
@@ -225,53 +185,39 @@ module Instana
225
185
  (time.to_f * 1000).floor
226
186
  end
227
187
 
228
- # Generate a random 64bit ID
188
+ # Generate a random 64bit/128bit ID
189
+ #
190
+ # @param size [Integer] Number of 64 bit integers used to generate the id
229
191
  #
230
- # @return [Integer] a random 64bit integer
192
+ # @return [String] a random 64bit/128bit hex encoded string
231
193
  #
232
- def generate_id
233
- # Max value is 9223372036854775807 (signed long in Java)
234
- rand(ID_RANGE)
194
+ def generate_id(size = 1)
195
+ Array.new(size) { rand(ID_RANGE) }
196
+ .pack('q>*')
197
+ .unpack('H*')
198
+ .first
235
199
  end
236
200
 
237
201
  # Convert an ID to a value appropriate to pass in a header.
238
202
  #
239
- # @param id [Integer] the id to be converted
203
+ # @param id [String] the id to be converted
240
204
  #
241
205
  # @return [String]
242
206
  #
243
207
  def id_to_header(id)
244
- unless id.is_a?(Integer) || id.is_a?(String)
245
- Instana.logger.debug "id_to_header received a #{id.class}: returning empty string"
246
- return String.new
247
- end
248
- [id.to_i].pack('q>').unpack('H*')[0].gsub(/^0+/, '')
249
- rescue => e
250
- Instana.logger.info "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
251
- Instana.logger.debug { e.backtrace.join("\r\n") }
208
+ return '' unless id.is_a?(String)
209
+ # Only send 64bit IDs downstream for now
210
+ id.length == 32 ? id[16..-1] : id
252
211
  end
253
212
 
254
213
  # Convert a received header value into a valid ID
255
214
  #
256
215
  # @param header_id [String] the header value to be converted
257
216
  #
258
- # @return [Integer]
217
+ # @return [String]
259
218
  #
260
219
  def header_to_id(header_id)
261
- if !header_id.is_a?(String)
262
- Instana.logger.debug "header_to_id received a #{header_id.class}: returning 0"
263
- return 0
264
- end
265
- if header_id.length < 16
266
- # The header is less than 16 chars. Prepend
267
- # zeros so we can convert correctly
268
- missing = 16 - header_id.length
269
- header_id = ("0" * missing) + header_id
270
- end
271
- [header_id].pack("H*").unpack("q>")[0]
272
- rescue => e
273
- Instana.logger.info "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
274
- Instana.logger.debug { e.backtrace.join("\r\n") }
220
+ header_id.is_a?(String) && header_id.match(/\A[a-z\d]{16,32}\z/i) ? header_id : ''
275
221
  end
276
222
  end
277
223
  end
@@ -1,4 +1,4 @@
1
1
  module Instana
2
- VERSION = "1.11.8"
2
+ VERSION = "1.193.2"
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
@@ -1,4 +1,3 @@
1
-
2
1
  if defined?(::Cuba)
3
2
  require 'test_helper'
4
3
  require File.expand_path(File.dirname(__FILE__) + '/../apps/cuba')
@@ -40,5 +39,19 @@ if defined?(::Cuba)
40
39
  assert first_span[:data][:http].key?(:host)
41
40
  assert_equal "example.org", first_span[:data][:http][:host]
42
41
  end
42
+
43
+ def test_path_template
44
+ clear_all!
45
+
46
+ r = get '/greet/instana'
47
+ assert last_response.ok?
48
+
49
+ spans = ::Instana.processor.queued_spans
50
+ assert_equal 1, spans.count
51
+
52
+ first_span = spans.first
53
+ assert_equal :rack, first_span[:n]
54
+ assert_equal '/greet/{name}', first_span[:data][:http][:path_tpl]
55
+ end
43
56
  end
44
57
  end
@@ -6,15 +6,21 @@ require "instana/rack"
6
6
  class RackTest < Minitest::Test
7
7
  include Rack::Test::Methods
8
8
 
9
+ class PathTemplateApp
10
+ def call(env)
11
+ env['INSTANA_HTTP_PATH_TEMPLATE'] = 'sample_template'
12
+ [200, {}, ['Ok']]
13
+ end
14
+ end
15
+
9
16
  def app
10
- @app = Rack::Builder.new {
17
+ @app = Rack::Builder.new do
11
18
  use Rack::CommonLogger
12
19
  use Rack::ShowExceptions
13
20
  use Instana::Rack
14
- map "/mrlobster" do
15
- run Rack::Lobster.new
16
- end
17
- }
21
+ map("/mrlobster") { run Rack::Lobster.new }
22
+ map("/path_tpl") { run PathTemplateApp.new }
23
+ end
18
24
  end
19
25
 
20
26
  def test_basic_get
@@ -51,11 +57,7 @@ class RackTest < Minitest::Test
51
57
  assert rack_span[:f].key?(:e)
52
58
  assert rack_span[:f].key?(:h)
53
59
  assert_equal ::Instana.agent.agent_uuid, rack_span[:f][:h]
54
-
55
- # Backtrace fingerprint validation
56
- assert rack_span.key?(:stack)
57
- assert_equal 2, rack_span[:stack].count
58
- refute_nil rack_span[:stack].first[:c].match(/instana\/instrumentation\/rack.rb/)
60
+ assert !rack_span.key?(:stack)
59
61
 
60
62
  # Restore to default
61
63
  ::Instana.config[:collect_backtraces] = false
@@ -145,8 +147,9 @@ class RackTest < Minitest::Test
145
147
 
146
148
  def test_context_continuation
147
149
  clear_all!
148
- header 'X-INSTANA-T', Instana::Util.id_to_header(1234)
149
- header 'X-INSTANA-S', Instana::Util.id_to_header(4321)
150
+ continuation_id = Instana::Util.generate_id
151
+ header 'X-INSTANA-T', continuation_id
152
+ header 'X-INSTANA-S', continuation_id
150
153
 
151
154
  get '/mrlobster'
152
155
  assert last_response.ok?
@@ -180,10 +183,30 @@ class RackTest < Minitest::Test
180
183
  # Context validation
181
184
  # The first span should have the passed in trace ID
182
185
  # and specify the passed in span ID as it's parent.
183
- assert_equal 1234, rack_span[:t]
184
- assert_equal 4321, rack_span[:p]
186
+ assert_equal continuation_id, rack_span[:t]
187
+ assert_equal continuation_id, rack_span[:p]
185
188
  end
186
189
 
190
+ def test_correlation_information
191
+ clear_all!
192
+
193
+ header 'X-INSTANA-L', '1,correlationType=test;correlationId=abcdefh123'
194
+
195
+ get '/mrlobster'
196
+ assert last_response.ok?
197
+
198
+ spans = ::Instana.processor.queued_spans
199
+
200
+ # Span validation
201
+ assert_equal 1, spans.count
202
+ rack_span = spans.first
203
+ assert_equal :rack, rack_span[:n]
204
+
205
+ assert_equal 'abcdefh123', rack_span[:crid]
206
+ assert_equal 'test', rack_span[:crtp]
207
+ end
208
+
209
+
187
210
  def test_instana_response_headers
188
211
  clear_all!
189
212
  get '/mrlobster'
@@ -227,14 +250,24 @@ class RackTest < Minitest::Test
227
250
  assert rack_span[:data][:http][:header].key?(:"X-Capture-This")
228
251
  assert !rack_span[:data][:http][:header].key?(:"X-Capture-That")
229
252
  assert_equal "ThereYouGo", rack_span[:data][:http][:header][:"X-Capture-This"]
230
-
231
- # Backtrace fingerprint validation
232
- assert rack_span.key?(:stack)
233
- assert_equal 2, rack_span[:stack].count
234
- refute_nil rack_span[:stack].first[:c].match(/instana\/instrumentation\/rack.rb/)
253
+ assert !rack_span.key?(:stack)
235
254
 
236
255
  # Restore to default
237
256
  ::Instana.config[:collect_backtraces] = false
238
257
  ::Instana.agent.extra_headers = nil
239
258
  end
259
+
260
+ def test_capture_http_path_template
261
+ clear_all!
262
+
263
+ get '/path_tpl'
264
+ assert last_response.ok?
265
+
266
+ spans = ::Instana.processor.queued_spans
267
+ assert_equal 1, spans.length
268
+
269
+ rack_span = spans.first
270
+ assert_equal :rack, rack_span[:n]
271
+ assert_equal 'sample_template', rack_span[:data][:http][:path_tpl]
272
+ end
240
273
  end