instana 1.193.6 → 1.195.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db77f1675976effa012f80a5d583f271b99e82db24e288a5af82eae4fa423cf1
4
- data.tar.gz: 9fe2b02c6f58355876c91d8954d3039a14b948f67b7a5730319579fb5d405a9f
3
+ metadata.gz: c2d6ab5606ce437d1b56cac7e9db7170085b5ba5603c4c0056b5c9529264c24e
4
+ data.tar.gz: dd0c1163a6f6c8ccb66025d83a18b0f89a02208b423ee17ab771c0e30db45f6d
5
5
  SHA512:
6
- metadata.gz: e00009c32acb5e684ee6c2dea9c7a7d4a40133f14ff8ec5929e39e97622b6daf8c5a027ad6cfdf9c3ace7e27f7accb9f1fff25f954e51eab11854ffb7f0f5e9b
7
- data.tar.gz: 7b8f0c486ab5deaa118debcedc1bf43b6164832cde533d52cb99fe8ac44c76ffe9c555101331ed7cff4b7e421fa321898ff3cf195af80d3a4cda4ed2b5c19db9
6
+ metadata.gz: '0792a8df1fb233c29f99f0004cba3551ec08720eab3128b8f956fb3c161a2469b970b2ec9272799a8fb2f65114be4fae252cf81fa48ef3eb1f5e31c596f9a73c'
7
+ data.tar.gz: 2856142ade217e7ccd90301d2f90d1127f005e290ca4fa6924b4604c91d11983353089e4862935e1a0be107ee45ecb240608cbc7abc33c08bf6df1a4f9f72db9
@@ -55,6 +55,9 @@ module Instana
55
55
  # ::Instana.config[:sanitize_sql] = false
56
56
  @config[:sanitize_sql] = true
57
57
 
58
+ # W3 Trace Context Support
59
+ @config[:w3_trace_correlation] = ENV.fetch('INSTANA_W3C_TRACE_CORRELATION', 'true').eql?('true')
60
+
58
61
  @config[:action_controller] = { :enabled => true }
59
62
  @config[:action_view] = { :enabled => true }
60
63
  @config[:active_record] = { :enabled => true }
@@ -26,6 +26,11 @@ module Instana
26
26
  datum[:headers]['X-Instana-T'] = t_context.trace_id_header
27
27
  datum[:headers]['X-Instana-S'] = t_context.span_id_header
28
28
 
29
+ if ::Instana.config[:w3_trace_correlation]
30
+ datum[:headers]['Traceparent'] = t_context.trace_parent_header
31
+ datum[:headers]['Tracestate'] = t_context.trace_state_header
32
+ end
33
+
29
34
  @stack.request_call(datum)
30
35
  end
31
36
 
@@ -9,19 +9,24 @@ require 'rack/request'
9
9
 
10
10
  module Instana
11
11
  class InstrumentedRequest < Rack::Request
12
+ W3_TRACE_PARENT_FORMAT = /00-(?<trace>[0-9a-f]+)-(?<parent>[0-9a-f]+)-(?<flags>[0-9a-f]+)/.freeze
13
+ INSTANA_TRACE_STATE = /in=(?<trace>[0-9a-f]+);(?<span>[0-9a-f]+)/.freeze
14
+
12
15
  def skip_trace?
13
16
  # Honor X-Instana-L
14
17
  @env.has_key?('HTTP_X_INSTANA_L') && @env['HTTP_X_INSTANA_L'].start_with?('0')
15
18
  end
16
19
 
17
20
  def incoming_context
18
- context = {}
21
+ context = if @env['HTTP_X_INSTANA_T']
22
+ context_from_instana_headers
23
+ elsif @env['HTTP_TRACEPARENT'] && ::Instana.config[:w3_trace_correlation]
24
+ context_from_trace_parent
25
+ else
26
+ {}
27
+ end
19
28
 
20
- if @env['HTTP_X_INSTANA_T']
21
- context[:trace_id] = ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_T'])
22
- context[:span_id] = ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_S']) if @env['HTTP_X_INSTANA_S']
23
- context[:level] = @env['HTTP_X_INSTANA_L'][0] if @env['HTTP_X_INSTANA_L']
24
- end
29
+ context[:level] = @env['HTTP_X_INSTANA_L'][0] if @env['HTTP_X_INSTANA_L']
25
30
 
26
31
  context
27
32
  end
@@ -54,8 +59,55 @@ module Instana
54
59
  @correlation_data ||= parse_correlation_data
55
60
  end
56
61
 
62
+ def instana_ancestor
63
+ @instana_ancestor ||= parse_trace_state
64
+ end
65
+
66
+ def continuing_from_trace_parent?
67
+ incoming_context.include?(:external_trace_id)
68
+ end
69
+
70
+ def synthetic?
71
+ @env.has_key?('HTTP_X_INSTANA_SYNTHETIC') && @env['HTTP_X_INSTANA_SYNTHETIC'].eql?('1')
72
+ end
73
+
57
74
  private
58
75
 
76
+ def context_from_instana_headers
77
+ {
78
+ trace_id: ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_T']),
79
+ span_id: ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_S'])
80
+ }.compact
81
+ end
82
+
83
+ def context_from_trace_parent
84
+ return {} unless @env.has_key?('HTTP_TRACEPARENT')
85
+ matches = @env['HTTP_TRACEPARENT'].match(W3_TRACE_PARENT_FORMAT)
86
+ return {} unless matches
87
+
88
+ {
89
+ external_trace_id: matches['trace'],
90
+ external_state: @env['HTTP_TRACESTATE'],
91
+ trace_id: ::Instana::Util.header_to_id(matches['trace'][16..-1]),
92
+ span_id: ::Instana::Util.header_to_id(matches['parent'])
93
+ }
94
+ end
95
+
96
+ def parse_trace_state
97
+ return {} unless @env.has_key?('HTTP_TRACESTATE')
98
+ token = @env['HTTP_TRACESTATE']
99
+ .split(/,/)
100
+ .map { |t| t.match(INSTANA_TRACE_STATE) }
101
+ .reject { |t| t.nil? }
102
+ .first
103
+ return {} unless token
104
+
105
+ {
106
+ t: token['trace'],
107
+ p: token['span']
108
+ }
109
+ end
110
+
59
111
  def parse_correlation_data
60
112
  return {} unless @env.has_key?('HTTP_X_INSTANA_L')
61
113
  _level, *tokens = @env['HTTP_X_INSTANA_L'].split(/[,=;]/)
@@ -22,6 +22,11 @@ module Instana
22
22
  request['X-Instana-T'] = t_context.trace_id_header
23
23
  request['X-Instana-S'] = t_context.span_id_header
24
24
 
25
+ if ::Instana.config[:w3_trace_correlation]
26
+ request['Traceparent'] = t_context.trace_parent_header
27
+ request['Tracestate'] = t_context.trace_state_header
28
+ end
29
+
25
30
  # Collect up KV info now in case any exception is raised
26
31
  kv_payload = { :http => {} }
27
32
  kv_payload[:http][:method] = request.method
@@ -20,14 +20,26 @@ module Instana
20
20
 
21
21
  current_span = ::Instana.tracer.log_start_or_continue(:rack, {}, req.incoming_context)
22
22
 
23
- unless req.correlation_data.empty?
24
- current_span[:crid] = req.correlation_data[:id]
25
- current_span[:crtp] = req.correlation_data[:type]
26
- end
27
-
28
23
  status, headers, response = @app.call(env)
29
24
 
30
25
  if ::Instana.tracer.tracing?
26
+ unless req.correlation_data.empty?
27
+ current_span[:crid] = req.correlation_data[:id]
28
+ current_span[:crtp] = req.correlation_data[:type]
29
+ end
30
+
31
+ if !req.instana_ancestor.empty? && req.continuing_from_trace_parent?
32
+ current_span[:ia] = req.instana_ancestor
33
+ end
34
+
35
+ if req.continuing_from_trace_parent?
36
+ current_span[:tp] = true
37
+ current_span[:lt] = req.incoming_context[:external_trace_id]
38
+ end
39
+
40
+ if req.synthetic?
41
+ current_span[:sy] = true
42
+ end
31
43
  # In case some previous middleware returned a string status, make sure that we're dealing with
32
44
  # an integer. In Ruby nil.to_i, "asdfasdf".to_i will always return 0 from Ruby versions 1.8.7 and newer.
33
45
  # So if an 0 status is reported here, it indicates some other issue (e.g. no status from previous middleware)
@@ -45,23 +57,31 @@ module Instana
45
57
  # See: https://www.instana.com/docs/tracing/custom-best-practices/#path-templates-visual-grouping-of-http-endpoints
46
58
  kvs[:http][:path_tpl] = env['INSTANA_HTTP_PATH_TEMPLATE'] if env['INSTANA_HTTP_PATH_TEMPLATE']
47
59
 
48
- # Save the IDs before the trace ends so we can place
60
+ # Save the span context before the trace ends so we can place
49
61
  # them in the response headers in the ensure block
50
- trace_id = ::Instana.tracer.current_span.trace_id
51
- span_id = ::Instana.tracer.current_span.id
62
+ trace_context = ::Instana.tracer.current_span.context
52
63
  end
53
64
 
54
65
  [status, headers, response]
55
66
  rescue Exception => e
56
- ::Instana.tracer.log_error(e)
67
+ ::Instana.tracer.log_error(e) if ::Instana.tracer.tracing?
57
68
  raise
58
69
  ensure
59
- if headers && ::Instana.tracer.tracing?
60
- # Set reponse headers; encode as hex string
61
- headers['X-Instana-T'] = ::Instana::Util.id_to_header(trace_id)
62
- headers['X-Instana-S'] = ::Instana::Util.id_to_header(span_id)
63
- headers['X-Instana-L'] = '1'
64
- headers['Server-Timing'] = "intid;desc=#{::Instana::Util.id_to_header(trace_id)}"
70
+ if ::Instana.tracer.tracing?
71
+ if headers
72
+ # Set response headers; encode as hex string
73
+ headers['X-Instana-T'] = trace_context.trace_id_header
74
+ headers['X-Instana-S'] = trace_context.span_id_header
75
+ headers['X-Instana-L'] = '1'
76
+
77
+ if ::Instana.config[:w3_trace_correlation]
78
+ headers['Traceparent'] = trace_context.trace_parent_header
79
+ headers['Tracestate'] = trace_context.trace_state_header
80
+ end
81
+
82
+ headers['Server-Timing'] = "intid;desc=#{trace_context.trace_id_header}"
83
+ end
84
+
65
85
  ::Instana.tracer.log_end(:rack, kvs)
66
86
  end
67
87
  end
@@ -91,7 +91,15 @@ module Instana
91
91
  if incoming_context
92
92
  if incoming_context.is_a?(Hash)
93
93
  if !incoming_context.empty?
94
- parent_context = SpanContext.new(incoming_context[:trace_id], incoming_context[:span_id], incoming_context[:level])
94
+ parent_context = SpanContext.new(
95
+ incoming_context[:trace_id],
96
+ incoming_context[:span_id],
97
+ incoming_context[:level],
98
+ {
99
+ external_trace_id: incoming_context[:external_trace_id],
100
+ external_state: incoming_context[:external_state]
101
+ }
102
+ )
95
103
  end
96
104
  else
97
105
  parent_context = incoming_context
@@ -103,12 +111,7 @@ module Instana
103
111
  else
104
112
  self.current_span = Span.new(name)
105
113
  end
106
-
107
- if incoming_context.is_a?(Hash) && incoming_context[:correlation] && !incoming_context[:correlation].empty?
108
- self.current_span[:crid] = incoming_context[:correlation][:id]
109
- self.current_span[:crtp] = incoming_context[:correlation][:type]
110
- end
111
-
114
+
112
115
  self.current_span.set_tags(kvs) unless kvs.empty?
113
116
  self.current_span
114
117
  end
@@ -18,7 +18,7 @@ module Instana
18
18
  @trace_id = tid
19
19
  @span_id = sid
20
20
  @level = level
21
- @baggage = baggage
21
+ @baggage = baggage || {}
22
22
  end
23
23
 
24
24
  def trace_id_header
@@ -29,8 +29,31 @@ module Instana
29
29
  ::Instana::Util.id_to_header(@span_id)
30
30
  end
31
31
 
32
+ def trace_parent_header
33
+ return '' unless valid?
34
+
35
+ trace = (@baggage[:external_trace_id] || @trace_id).rjust(32, '0')
36
+ parent = @span_id.rjust(16, '0')
37
+ flags = @level == 1 ? "01" : "00"
38
+
39
+ "00-#{trace}-#{parent}-#{flags}"
40
+ end
41
+
42
+ def trace_state_header
43
+ return '' unless valid?
44
+
45
+ state = ["in=#{@trace_id};#{@span_id}", @baggage[:external_state]]
46
+ state.compact.join(',')
47
+ end
48
+
32
49
  def to_hash
33
50
  { :trace_id => @trace_id, :span_id => @span_id }
34
51
  end
52
+
53
+ private
54
+
55
+ def valid?
56
+ @baggage && @trace_id && @span_id
57
+ end
35
58
  end
36
59
  end
@@ -2,6 +2,6 @@
2
2
  # (c) Copyright Instana Inc. 2016
3
3
 
4
4
  module Instana
5
- VERSION = "1.193.6"
5
+ VERSION = "1.195.0"
6
6
  VERSION_FULL = "instana-#{VERSION}"
7
7
  end
@@ -18,7 +18,7 @@ class RackInstrumentedRequestTest < Minitest::Test
18
18
  refute req.skip_trace?
19
19
  end
20
20
 
21
- def test_incomming_context
21
+ def test_incoming_context
22
22
  id = Instana::Util.generate_id
23
23
  req = Instana::InstrumentedRequest.new(
24
24
  'HTTP_X_INSTANA_L' => '1',
@@ -33,6 +33,51 @@ class RackInstrumentedRequestTest < Minitest::Test
33
33
  }
34
34
 
35
35
  assert_equal expected, req.incoming_context
36
+ refute req.continuing_from_trace_parent?
37
+ end
38
+
39
+ def test_incoming_w3_content
40
+ req = Instana::InstrumentedRequest.new(
41
+ 'HTTP_X_INSTANA_L' => '1',
42
+ 'HTTP_TRACEPARENT' => '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
43
+ )
44
+
45
+ expected = {
46
+ external_trace_id: '4bf92f3577b34da6a3ce929d0e0e4736',
47
+ external_state: nil,
48
+ trace_id: 'a3ce929d0e0e4736',
49
+ span_id: '00f067aa0ba902b7',
50
+ level: '1'
51
+ }
52
+
53
+ assert_equal expected, req.incoming_context
54
+ assert req.continuing_from_trace_parent?
55
+ end
56
+
57
+ def test_incoming_invalid_w3_content
58
+ req = Instana::InstrumentedRequest.new(
59
+ 'HTTP_X_INSTANA_L' => '1',
60
+ 'HTTP_TRACEPARENT' => '00-XXa3ce929d0e0e4736-00f67aa0ba902b7-01'
61
+ )
62
+
63
+ expected = {
64
+ level: '1'
65
+ }
66
+
67
+ assert_equal expected, req.incoming_context
68
+ end
69
+
70
+ def test_incoming_w3_state
71
+ req = Instana::InstrumentedRequest.new(
72
+ 'HTTP_TRACESTATE' => 'a=12345,in=123;abe,c=[+]'
73
+ )
74
+
75
+ expected = {
76
+ t: '123',
77
+ p: 'abe'
78
+ }
79
+
80
+ assert_equal expected, req.instana_ancestor
36
81
  end
37
82
 
38
83
  def test_request_tags
@@ -15,6 +15,24 @@ class RackTest < Minitest::Test
15
15
  end
16
16
  end
17
17
 
18
+ class ErrorApp
19
+ def call(_env)
20
+ raise 'An Error'
21
+ end
22
+ end
23
+
24
+ class FiveZeroOneApp
25
+ def call(_env)
26
+ [501, {}, ['No']]
27
+ end
28
+ end
29
+
30
+ class NoHeadersApp
31
+ def call(_env)
32
+ [501, nil, ['No']]
33
+ end
34
+ end
35
+
18
36
  def app
19
37
  @app = Rack::Builder.new do
20
38
  use Rack::CommonLogger
@@ -22,6 +40,8 @@ class RackTest < Minitest::Test
22
40
  use Instana::Rack
23
41
  map("/mrlobster") { run Rack::Lobster.new }
24
42
  map("/path_tpl") { run PathTemplateApp.new }
43
+ map("/error") { run ErrorApp.new }
44
+ map("/five_zero_one") { run FiveZeroOneApp.new }
25
45
  end
26
46
  end
27
47
 
@@ -49,6 +69,10 @@ class RackTest < Minitest::Test
49
69
  assert last_response.headers.key?("Server-Timing")
50
70
  assert last_response.headers["Server-Timing"] == "intid;desc=#{::Instana::Util.id_to_header(rack_span[:t])}"
51
71
 
72
+ # W3 Trace Context
73
+ assert_equal "00-#{rack_span[:t].rjust(32, '0')}-#{rack_span[:s]}-01", last_response.headers["Traceparent"]
74
+ assert_equal "in=#{rack_span[:t]};#{rack_span[:s]}", last_response.headers["Tracestate"]
75
+
52
76
  assert rack_span.key?(:data)
53
77
  assert rack_span[:data].key?(:http)
54
78
  assert_equal "GET", rack_span[:data][:http][:method]
@@ -272,4 +296,121 @@ class RackTest < Minitest::Test
272
296
  assert_equal :rack, rack_span[:n]
273
297
  assert_equal 'sample_template', rack_span[:data][:http][:path_tpl]
274
298
  end
299
+
300
+ def test_basic_get_with_x_instana_synthetic
301
+ header 'X-INSTANA-SYNTHETIC', '1'
302
+
303
+ clear_all!
304
+ get '/mrlobster'
305
+ assert last_response.ok?
306
+
307
+ spans = ::Instana.processor.queued_spans
308
+
309
+ # Span validation
310
+ assert_equal 1, spans.count
311
+
312
+ first_span = spans.first
313
+ assert_equal true, first_span[:sy]
314
+ end
315
+
316
+ def test_basic_get_with_w3_trace
317
+ clear_all!
318
+
319
+ header 'TRACEPARENT', '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
320
+
321
+ get '/mrlobster'
322
+ assert last_response.ok?
323
+
324
+ spans = ::Instana.processor.queued_spans
325
+ assert_equal 1, spans.count
326
+
327
+ first_span = spans.first
328
+ assert_equal :rack, first_span[:n]
329
+ assert_equal 'a3ce929d0e0e4736', first_span[:t]
330
+ assert_equal '00f067aa0ba902b7', first_span[:p]
331
+ assert_equal '4bf92f3577b34da6a3ce929d0e0e4736', first_span[:lt]
332
+ assert_nil first_span[:ia]
333
+ assert first_span[:tp]
334
+ end
335
+
336
+ def test_basic_get_with_w3_disabled
337
+ clear_all!
338
+ ::Instana.config[:w3_trace_correlation] = false
339
+
340
+ header 'TRACEPARENT', '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
341
+
342
+ get '/mrlobster'
343
+ assert last_response.ok?
344
+
345
+ spans = ::Instana.processor.queued_spans
346
+ assert_equal 1, spans.count
347
+
348
+ first_span = spans.first
349
+ assert_equal :rack, first_span[:n]
350
+ refute first_span[:tp]
351
+ ::Instana.config[:w3_trace_correlation] = true
352
+ end
353
+
354
+ def test_skip_trace
355
+ clear_all!
356
+ header 'X_INSTANA_L', '0;junk'
357
+
358
+ get '/mrlobster'
359
+ assert last_response.ok?
360
+
361
+ spans = ::Instana.processor.queued_spans
362
+ assert_equal 0, spans.count
363
+ end
364
+
365
+ def test_disable_trace
366
+ clear_all!
367
+ ::Instana.config[:tracing][:enabled] = false
368
+
369
+ get '/mrlobster'
370
+ assert last_response.ok?
371
+
372
+ spans = ::Instana.processor.queued_spans
373
+ assert_equal 0, spans.count
374
+ ::Instana.config[:tracing][:enabled] = true
375
+ end
376
+
377
+ def test_error_trace
378
+ clear_all!
379
+
380
+ get '/error'
381
+ refute last_response.ok?
382
+
383
+ spans = ::Instana.processor.queued_spans
384
+ assert_equal 1, spans.count
385
+
386
+ first_span = spans.first
387
+ assert_equal :rack, first_span[:n]
388
+ assert_equal 1, first_span[:ec]
389
+ end
390
+
391
+ def test_disable_trace_with_error
392
+ clear_all!
393
+ ::Instana.config[:tracing][:enabled] = false
394
+
395
+ get '/error'
396
+ refute last_response.ok?
397
+
398
+ spans = ::Instana.processor.queued_spans
399
+ assert_equal 0, spans.count
400
+ ::Instana.config[:tracing][:enabled] = true
401
+ end
402
+
403
+ def test_five_zero_x_trace
404
+ clear_all!
405
+
406
+ get '/five_zero_one'
407
+ refute last_response.ok?
408
+
409
+ spans = ::Instana.processor.queued_spans
410
+ assert_equal 1, spans.count
411
+
412
+ first_span = spans.first
413
+ assert_equal :rack, first_span[:n]
414
+ assert_equal 1, first_span[:ec]
415
+ end
275
416
  end
@@ -304,7 +304,7 @@ class OpenTracerTest < Minitest::Test
304
304
  assert_equal second_span[:s], third_span[:p]
305
305
 
306
306
  # Every span should have baggage
307
- assert_equal(nil, entry_span.context.baggage)
307
+ assert_equal({}, entry_span.context.baggage)
308
308
  assert_equal({:my_bag=>1}, ac_span.context.baggage)
309
309
  assert_equal({:my_bag=>1}, av_span.context.baggage)
310
310
  end
@@ -331,9 +331,9 @@ class OpenTracerTest < Minitest::Test
331
331
  spans = ::Instana.processor.queued_spans
332
332
  assert_equal 3, spans.length
333
333
 
334
- assert_equal(nil, entry_span.context.baggage)
334
+ assert_equal({}, entry_span.context.baggage)
335
335
  assert_equal({:my_bag=>1}, ac_span.context.baggage)
336
- assert_equal(nil, av_span.context.baggage)
336
+ assert_equal({}, av_span.context.baggage)
337
337
  end
338
338
 
339
339
  def test_start_active_span
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instana
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.193.6
4
+ version: 1.195.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Giacomo Lombardo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-19 00:00:00.000000000 Z
11
+ date: 2021-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler