instana 1.193.6 → 1.195.0

Sign up to get free protection for your applications and to get access to all the features.
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