instana 1.195.4 → 1.198.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -2
  3. data/Rakefile +1 -1
  4. data/instana.gemspec +3 -7
  5. data/lib/instana.rb +3 -0
  6. data/lib/instana/activator.rb +2 -0
  7. data/lib/instana/backend/agent.rb +62 -0
  8. data/lib/instana/backend/gc_snapshot.rb +41 -0
  9. data/lib/instana/backend/host_agent.rb +74 -0
  10. data/lib/instana/backend/host_agent_activation_observer.rb +97 -0
  11. data/lib/instana/backend/host_agent_lookup.rb +57 -0
  12. data/lib/instana/backend/host_agent_reporting_observer.rb +106 -0
  13. data/lib/instana/backend/process_info.rb +64 -0
  14. data/lib/instana/backend/request_client.rb +74 -0
  15. data/lib/instana/backend/serverless_agent.rb +113 -0
  16. data/lib/instana/base.rb +10 -27
  17. data/lib/instana/config.rb +8 -22
  18. data/lib/instana/instrumentation/excon.rb +7 -5
  19. data/lib/instana/instrumentation/instrumented_request.rb +62 -7
  20. data/lib/instana/instrumentation/net-http.rb +7 -5
  21. data/lib/instana/instrumentation/rack.rb +12 -7
  22. data/lib/instana/logger_delegator.rb +31 -0
  23. data/lib/instana/{opentracing → open_tracing}/carrier.rb +0 -0
  24. data/lib/instana/open_tracing/instana_tracer.rb +99 -0
  25. data/lib/instana/secrets.rb +6 -2
  26. data/lib/instana/serverless.rb +139 -0
  27. data/lib/instana/setup.rb +23 -11
  28. data/lib/instana/snapshot/deltable.rb +25 -0
  29. data/lib/instana/snapshot/docker_container.rb +151 -0
  30. data/lib/instana/snapshot/fargate_container.rb +88 -0
  31. data/lib/instana/snapshot/fargate_process.rb +67 -0
  32. data/lib/instana/snapshot/fargate_task.rb +72 -0
  33. data/lib/instana/snapshot/lambda_function.rb +39 -0
  34. data/lib/instana/snapshot/ruby_process.rb +48 -0
  35. data/lib/instana/tracer.rb +25 -143
  36. data/lib/instana/tracing/processor.rb +14 -22
  37. data/lib/instana/tracing/span.rb +33 -36
  38. data/lib/instana/tracing/span_context.rb +15 -10
  39. data/lib/instana/util.rb +8 -69
  40. data/lib/instana/version.rb +1 -1
  41. data/lib/opentracing.rb +26 -3
  42. data/test/backend/agent_test.rb +67 -0
  43. data/test/backend/gc_snapshot_test.rb +11 -0
  44. data/test/backend/host_agent_activation_observer_test.rb +72 -0
  45. data/test/backend/host_agent_lookup_test.rb +78 -0
  46. data/test/backend/host_agent_reporting_observer_test.rb +192 -0
  47. data/test/backend/host_agent_test.rb +47 -0
  48. data/test/backend/process_info_test.rb +63 -0
  49. data/test/backend/request_client_test.rb +39 -0
  50. data/test/backend/serverless_agent_test.rb +73 -0
  51. data/test/config_test.rb +10 -0
  52. data/test/instana_test.rb +11 -4
  53. data/test/instrumentation/rack_instrumented_request_test.rb +5 -2
  54. data/test/instrumentation/rack_test.rb +2 -14
  55. data/test/secrets_test.rb +41 -22
  56. data/test/serverless_test.rb +323 -0
  57. data/test/snapshot/deltable_test.rb +17 -0
  58. data/test/snapshot/docker_container_test.rb +82 -0
  59. data/test/snapshot/fargate_container_test.rb +82 -0
  60. data/test/snapshot/fargate_process_test.rb +35 -0
  61. data/test/snapshot/fargate_task_test.rb +49 -0
  62. data/test/snapshot/lambda_function_test.rb +37 -0
  63. data/test/snapshot/ruby_process_test.rb +14 -0
  64. data/test/support/mock_timer.rb +20 -0
  65. data/test/test_helper.rb +16 -4
  66. data/test/tracing/custom_test.rb +1 -3
  67. data/test/tracing/id_management_test.rb +8 -0
  68. data/test/tracing/opentracing_test.rb +15 -2
  69. data/test/tracing/processor_test.rb +58 -0
  70. data/test/tracing/span_context_test.rb +22 -0
  71. data/test/tracing/span_test.rb +136 -0
  72. data/test/tracing/tracer_async_test.rb +29 -0
  73. data/test/tracing/tracer_test.rb +82 -16
  74. data/test/util_test.rb +10 -0
  75. metadata +76 -43
  76. data/lib/instana/agent.rb +0 -508
  77. data/lib/instana/agent/helpers.rb +0 -87
  78. data/lib/instana/agent/hooks.rb +0 -44
  79. data/lib/instana/agent/tasks.rb +0 -51
  80. data/lib/instana/collector.rb +0 -119
  81. data/lib/instana/collectors/gc.rb +0 -60
  82. data/lib/instana/collectors/memory.rb +0 -37
  83. data/lib/instana/collectors/thread.rb +0 -33
  84. data/lib/instana/eum/eum-test.js.erb +0 -17
  85. data/lib/instana/eum/eum.js.erb +0 -17
  86. data/lib/instana/helpers.rb +0 -47
  87. data/lib/instana/opentracing/tracer.rb +0 -21
  88. data/lib/instana/thread_local.rb +0 -18
  89. data/lib/oj_check.rb +0 -19
  90. data/test/agent/agent_test.rb +0 -151
@@ -19,14 +19,16 @@ module Instana
19
19
 
20
20
  # Set request headers; encode IDs as hexadecimal strings
21
21
  t_context = ::Instana.tracer.context
22
- request['X-Instana-T'] = t_context.trace_id_header
23
- request['X-Instana-S'] = t_context.span_id_header
22
+ request['X-Instana-L'] = t_context.level.to_s
24
23
 
25
- if ::Instana.config[:w3_trace_correlation]
26
- request['Traceparent'] = t_context.trace_parent_header
27
- request['Tracestate'] = t_context.trace_state_header
24
+ if t_context.active?
25
+ request['X-Instana-T'] = t_context.trace_id_header
26
+ request['X-Instana-S'] = t_context.span_id_header
28
27
  end
29
28
 
29
+ request['Traceparent'] = t_context.trace_parent_header
30
+ request['Tracestate'] = t_context.trace_state_header unless t_context.trace_state_header.empty?
31
+
30
32
  # Collect up KV info now in case any exception is raised
31
33
  kv_payload = { :http => {} }
32
34
  kv_payload[:http][:method] = request.method
@@ -12,7 +12,6 @@ module Instana
12
12
 
13
13
  def call(env)
14
14
  req = InstrumentedRequest.new(env)
15
- return @app.call(env) if req.skip_trace?
16
15
  kvs = {
17
16
  http: req.request_tags,
18
17
  service: ENV['INSTANA_SERVICE_NAME']
@@ -34,12 +33,16 @@ module Instana
34
33
 
35
34
  if req.continuing_from_trace_parent?
36
35
  current_span[:tp] = true
37
- current_span[:lt] = req.incoming_context[:external_trace_id]
36
+ end
37
+
38
+ if req.external_trace_id?
39
+ current_span[:lt] = req.external_trace_id
38
40
  end
39
41
 
40
42
  if req.synthetic?
41
43
  current_span[:sy] = true
42
44
  end
45
+
43
46
  # In case some previous middleware returned a string status, make sure that we're dealing with
44
47
  # an integer. In Ruby nil.to_i, "asdfasdf".to_i will always return 0 from Ruby versions 1.8.7 and newer.
45
48
  # So if an 0 status is reported here, it indicates some other issue (e.g. no status from previous middleware)
@@ -70,15 +73,17 @@ module Instana
70
73
  if ::Instana.tracer.tracing?
71
74
  if headers
72
75
  # 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
+ if trace_context.active?
77
+ headers['X-Instana-T'] = trace_context.trace_id_header
78
+ headers['X-Instana-S'] = trace_context.span_id_header
79
+ headers['X-Instana-L'] = '1'
76
80
 
77
- if ::Instana.config[:w3_trace_correlation]
78
- headers['Traceparent'] = trace_context.trace_parent_header
79
81
  headers['Tracestate'] = trace_context.trace_state_header
82
+ else
83
+ headers['X-Instana-L'] = '0'
80
84
  end
81
85
 
86
+ headers['Traceparent'] = trace_context.trace_parent_header
82
87
  headers['Server-Timing'] = "intid;desc=#{trace_context.trace_id_header}"
83
88
  end
84
89
 
@@ -0,0 +1,31 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ class LoggerDelegator < SimpleDelegator
6
+ def initialize(obj)
7
+ obj.level = level_from_environment
8
+ super(obj)
9
+ end
10
+
11
+ private
12
+
13
+ def level_from_environment
14
+ # :nocov:
15
+ return Logger::FATAL if ENV.key?('INSTANA_TEST') || ENV.key?('RACK_TEST')
16
+ return Logger::DEBUG if ENV.key?('INSTANA_DEBUG')
17
+
18
+ case ENV['INSTANA_LOG_LEVEL']
19
+ when 'debug'
20
+ Logger::DEBUG
21
+ when 'warn'
22
+ Logger::WARN
23
+ when 'error'
24
+ Logger::ERROR
25
+ else
26
+ Logger::INFO
27
+ end
28
+ # :nocov:
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,99 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'delegate'
5
+ # :nocov:
6
+ module OpenTracing
7
+ class InstanaTracer < SimpleDelegator
8
+ Span = ::Instana::Span
9
+
10
+ # Start a new span
11
+ #
12
+ # @param operation_name [String] The name of the operation represented by the span
13
+ # @param child_of [Span] A span to be used as the ChildOf reference
14
+ # @param start_time [Time] the start time of the span
15
+ # @param tags [Hash] Starting tags for the span
16
+ #
17
+ # @return [Span]
18
+ #
19
+ def start_span(operation_name, child_of: nil, start_time: ::Instana::Util.now_in_ms, tags: nil)
20
+ new_span = if child_of && (child_of.is_a?(::Instana::Span) || child_of.is_a?(::Instana::SpanContext))
21
+ Span.new(operation_name, parent_ctx: child_of, start_time: start_time)
22
+ else
23
+ Span.new(operation_name, start_time: start_time)
24
+ end
25
+ new_span.set_tags(tags) if tags
26
+ new_span
27
+ end
28
+
29
+ # Start a new span which is the child of the current span
30
+ #
31
+ # @param operation_name [String] The name of the operation represented by the span
32
+ # @param child_of [Span] A span to be used as the ChildOf reference
33
+ # @param start_time [Time] the start time of the span
34
+ # @param tags [Hash] Starting tags for the span
35
+ #
36
+ # @return [Span]
37
+ #
38
+ def start_active_span(operation_name, child_of: active_span, start_time: ::Instana::Util.now_in_ms, tags: nil)
39
+ ::Instana.tracer.current_span = start_span(operation_name, child_of: child_of, start_time: start_time, tags: tags)
40
+ block_given? ? yield(::Instana.tracer.current_span) : ::Instana.tracer.current_span
41
+ end
42
+
43
+ # Returns the currently active span
44
+ #
45
+ # @return [Span]
46
+ #
47
+ def active_span
48
+ ::Instana.tracer.current_span
49
+ end
50
+
51
+ # Inject a span into the given carrier
52
+ #
53
+ # @param span_context [SpanContext]
54
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
55
+ # @param carrier [Carrier]
56
+ #
57
+ def inject(span_context, format, carrier)
58
+ case format
59
+ when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY
60
+ ::Instana.logger.debug 'Unsupported inject format'
61
+ when OpenTracing::FORMAT_RACK
62
+ carrier['X-Instana-T'] = ::Instana::Util.id_to_header(span_context.trace_id)
63
+ carrier['X-Instana-S'] = ::Instana::Util.id_to_header(span_context.span_id)
64
+ else
65
+ ::Instana.logger.debug 'Unknown inject format'
66
+ end
67
+ end
68
+
69
+ # Extract a span from a carrier
70
+ #
71
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
72
+ # @param carrier [Carrier]
73
+ #
74
+ # @return [SpanContext]
75
+ #
76
+ def extract(format, carrier)
77
+ case format
78
+ when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY
79
+ ::Instana.logger.debug 'Unsupported extract format'
80
+ when OpenTracing::FORMAT_RACK
81
+ ::Instana::SpanContext.new(::Instana::Util.header_to_id(carrier['HTTP_X_INSTANA_T']),
82
+ ::Instana::Util.header_to_id(carrier['HTTP_X_INSTANA_S']))
83
+ else
84
+ ::Instana.logger.debug 'Unknown inject format'
85
+ nil
86
+ end
87
+ end
88
+
89
+ def method_missing(method, *args, &block)
90
+ ::Instana.logger.warn { "You are invoking `#{m}` on Instana::Tracer via OpenTracing." }
91
+ super(method, *args, &block)
92
+ end
93
+
94
+ def respond_to_missing?(*)
95
+ super(method)
96
+ end
97
+ end
98
+ end
99
+ # :nocov:
@@ -6,6 +6,10 @@ require 'cgi'
6
6
 
7
7
  module Instana
8
8
  class Secrets
9
+ def initialize(logger: ::Instana.logger)
10
+ @logger = logger
11
+ end
12
+
9
13
  def remove_from_query(str, secret_values = Instana.agent.secret_values)
10
14
  return str unless secret_values
11
15
 
@@ -37,8 +41,8 @@ module Instana
37
41
  when 'regex'
38
42
  ->(expected, actual) { !Regexp.new(expected).match(actual).nil? }
39
43
  else
40
- ::Instana.logger.warn("Matcher #{name} is not supported.")
41
- lambda { false }
44
+ @logger.warn("Matcher #{name} is not supported.")
45
+ ->(_e, _a) { false }
42
46
  end
43
47
  end
44
48
  end
@@ -0,0 +1,139 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'base64'
5
+ require 'zlib'
6
+
7
+ # :nocov:
8
+ begin
9
+ require 'instana/instrumentation/instrumented_request'
10
+ rescue LoadError => _e
11
+ Instana.logger.warn("Unable to Instana::InstrumentedRequest. HTTP based triggers won't generate spans.")
12
+ end
13
+ # :nocov:
14
+
15
+ module Instana
16
+ # @since 1.198.0
17
+ class Serverless
18
+ def initialize(agent: ::Instana.agent, tracer: ::Instana.tracer, logger: ::Instana.logger)
19
+ @agent = agent
20
+ @tracer = tracer
21
+ @logger = logger
22
+ end
23
+
24
+ def wrap_aws(event, context, &block)
25
+ Thread.current[:instana_function_arn] = [context.invoked_function_arn, context.function_version].join(':')
26
+ trigger, event_tags, span_context = trigger_from_event(event)
27
+
28
+ tags = {
29
+ lambda: {
30
+ arn: context.invoked_function_arn,
31
+ functionName: context.function_name,
32
+ functionVersion: context.function_version,
33
+ runtime: 'ruby',
34
+ trigger: trigger
35
+ }
36
+ }
37
+
38
+ if event_tags.key?(:http)
39
+ tags = tags.merge(event_tags)
40
+ else
41
+ tags[:lambda] = tags[:lambda].merge(event_tags)
42
+ end
43
+
44
+ @tracer.start_or_continue_trace(:'aws.lambda.entry', tags, span_context, &block)
45
+ ensure
46
+ begin
47
+ @agent.send_bundle
48
+ rescue StandardError => e
49
+ @logger.error(e.message)
50
+ end
51
+ Thread.current[:instana_function_arn] = nil
52
+ end
53
+
54
+ private
55
+
56
+ def trigger_from_event(event) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
57
+ case event
58
+ when ->(e) { defined?(::Instana::InstrumentedRequest) && e.is_a?(Hash) && e.key?('requestContext') && e['requestContext'].key?('elb') }
59
+ request = InstrumentedRequest.new(event_to_rack(event))
60
+ ['aws:application.load.balancer', {http: request.request_tags}, request.incoming_context]
61
+ when ->(e) { defined?(::Instana::InstrumentedRequest) && e.is_a?(Hash) && e.key?('httpMethod') && e.key?('path') && e.key?('headers') }
62
+ request = InstrumentedRequest.new(event_to_rack(event))
63
+ ['aws:api.gateway', {http: request.request_tags}, request.incoming_context]
64
+ when ->(e) { e.is_a?(Hash) && e['source'] == 'aws.events' && e['detail-type'] == 'Scheduled Event' }
65
+ tags = decode_cloudwatch_events(event)
66
+ ['aws:cloudwatch.events', {cw: tags}, {}]
67
+ when ->(e) { e.is_a?(Hash) && e.key?('awslogs') }
68
+ tags = decode_cloudwatch_logs(event)
69
+ ['aws:cloudwatch.logs', {cw: tags}, {}]
70
+ when ->(e) { e.is_a?(Hash) && e.key?('Records') && e['Records'].is_a?(Array) && e['Records'].first && e['Records'].first['source'] == 'aws:s3' }
71
+ tags = decode_s3(event)
72
+ ['aws:s3', {s3: tags}, {}]
73
+ when ->(e) { e.is_a?(Hash) && e.key?('Records') && e['Records'].is_a?(Array) && e['Records'].first && e['Records'].first['source'] == 'aws:sqs' }
74
+ tags = decode_sqs(event)
75
+ ['aws:sqs', {sqs: tags}, {}]
76
+ else
77
+ ['aws:api.gateway.noproxy', {}, {}]
78
+ end
79
+ end
80
+
81
+ def event_to_rack(event)
82
+ event['headers']
83
+ .transform_keys { |k| "HTTP_#{k.gsub('-', '_').upcase}" }
84
+ .merge(
85
+ 'QUERY_STRING' => URI.encode_www_form(event['queryStringParameters'] || {}),
86
+ 'PATH_INFO' => event['path'],
87
+ 'REQUEST_METHOD' => event['httpMethod']
88
+ )
89
+ end
90
+
91
+ def decode_cloudwatch_events(event)
92
+ {
93
+ events: {
94
+ id: event['id'],
95
+ resources: event['resources']
96
+ }
97
+ }
98
+ end
99
+
100
+ def decode_cloudwatch_logs(event)
101
+ logs = begin
102
+ payload = JSON.parse(Zlib::Inflate.inflate(Base64.decode64(event['awslogs']['data'])))
103
+
104
+ {
105
+ group: payload['logGroup'],
106
+ stream: payload['logStream']
107
+ }
108
+ rescue StandardError => e
109
+ {
110
+ decodingError: e.message
111
+ }
112
+ end
113
+
114
+ {logs: logs}
115
+ end
116
+
117
+ def decode_s3(event)
118
+ span_events = event['Records'].map do |record|
119
+ {
120
+ name: record['eventName'],
121
+ bucket: record['s3'] && record['s3']['bucket'] ? record['s3']['bucket']['name'] : nil,
122
+ object: record['s3'] && record['s3']['object'] ? record['s3']['object']['key'] : nil
123
+ }
124
+ end
125
+
126
+ {events: span_events}
127
+ end
128
+
129
+ def decode_sqs(event)
130
+ span_events = event['Records'].map do |record|
131
+ {
132
+ queue: record['eventSourceARN']
133
+ }
134
+ end
135
+
136
+ {messages: span_events}
137
+ end
138
+ end
139
+ end
data/lib/instana/setup.rb CHANGED
@@ -1,30 +1,42 @@
1
1
  # (c) Copyright IBM Corp. 2021
2
2
  # (c) Copyright Instana Inc. 2016
3
3
 
4
- require 'oj_check'
4
+ require 'instana/logger_delegator'
5
5
 
6
6
  require "instana/base"
7
7
  require "instana/config"
8
- require "instana/agent"
9
- require "instana/collector"
10
8
  require "instana/secrets"
11
9
  require "instana/tracer"
12
10
  require "instana/tracing/processor"
13
11
 
12
+ require 'instana/serverless'
13
+
14
14
  require 'instana/activator'
15
15
 
16
+ require 'instana/backend/request_client'
17
+ require 'instana/backend/gc_snapshot'
18
+ require 'instana/backend/process_info'
19
+
20
+ require 'instana/snapshot/deltable'
21
+ require 'instana/snapshot/ruby_process'
22
+ require 'instana/snapshot/fargate_process'
23
+ require 'instana/snapshot/fargate_task'
24
+ require 'instana/snapshot/fargate_container'
25
+ require 'instana/snapshot/docker_container'
26
+ require 'instana/snapshot/lambda_function'
27
+
28
+ require 'instana/backend/host_agent_lookup'
29
+ require 'instana/backend/host_agent_activation_observer'
30
+ require 'instana/backend/host_agent_reporting_observer'
31
+
32
+ require 'instana/backend/host_agent'
33
+ require 'instana/backend/serverless_agent'
34
+ require 'instana/backend/agent'
35
+
16
36
  ::Instana.setup
17
37
  ::Instana.agent.setup
18
38
  ::Instana::Activator.start
19
39
 
20
- # Register the metric collectors
21
- unless RUBY_PLATFORM == 'java'.freeze
22
- require 'instana/collectors/gc'
23
- end
24
-
25
- require 'instana/collectors/memory'
26
- require 'instana/collectors/thread'
27
-
28
40
  # Require supported OpenTracing interfaces
29
41
  require "opentracing"
30
42
 
@@ -0,0 +1,25 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Snapshot
6
+ # @since 1.197.0
7
+ module Deltable
8
+ def delta(key, *rest, compute:, obj:, path: [key, *rest])
9
+ val = obj[key]
10
+ return val if val == nil
11
+
12
+ if rest.empty?
13
+ @__delta ||= Hash.new(0)
14
+ cache_key = path.join('.')
15
+ old = @__delta[cache_key]
16
+ @__delta[cache_key] = val
17
+
18
+ return compute.call(old, val)
19
+ end
20
+
21
+ delta(*rest, compute: compute, obj: val, path: path)
22
+ end
23
+ end
24
+ end
25
+ end