instana 1.195.2 → 1.197.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.
Files changed (88) 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 +60 -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 +73 -0
  15. data/lib/instana/backend/serverless_agent.rb +118 -0
  16. data/lib/instana/base.rb +8 -27
  17. data/lib/instana/config.rb +8 -22
  18. data/lib/instana/instrumentation/excon.rb +17 -8
  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/setup.rb +20 -11
  27. data/lib/instana/snapshot/deltable.rb +25 -0
  28. data/lib/instana/snapshot/docker_container.rb +151 -0
  29. data/lib/instana/snapshot/fargate_container.rb +88 -0
  30. data/lib/instana/snapshot/fargate_process.rb +67 -0
  31. data/lib/instana/snapshot/fargate_task.rb +72 -0
  32. data/lib/instana/snapshot/lambda_function.rb +36 -0
  33. data/lib/instana/snapshot/ruby_process.rb +48 -0
  34. data/lib/instana/tracer.rb +25 -143
  35. data/lib/instana/tracing/processor.rb +14 -22
  36. data/lib/instana/tracing/span.rb +31 -34
  37. data/lib/instana/tracing/span_context.rb +15 -10
  38. data/lib/instana/util.rb +8 -69
  39. data/lib/instana/version.rb +1 -1
  40. data/lib/opentracing.rb +26 -3
  41. data/test/backend/agent_test.rb +54 -0
  42. data/test/backend/gc_snapshot_test.rb +11 -0
  43. data/test/backend/host_agent_activation_observer_test.rb +72 -0
  44. data/test/backend/host_agent_lookup_test.rb +78 -0
  45. data/test/backend/host_agent_reporting_observer_test.rb +192 -0
  46. data/test/backend/host_agent_test.rb +47 -0
  47. data/test/backend/process_info_test.rb +63 -0
  48. data/test/backend/request_client_test.rb +39 -0
  49. data/test/backend/serverless_agent_test.rb +73 -0
  50. data/test/config_test.rb +10 -0
  51. data/test/instana_test.rb +11 -4
  52. data/test/instrumentation/excon_test.rb +15 -1
  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/snapshot/deltable_test.rb +17 -0
  57. data/test/snapshot/docker_container_test.rb +82 -0
  58. data/test/snapshot/fargate_container_test.rb +82 -0
  59. data/test/snapshot/fargate_process_test.rb +35 -0
  60. data/test/snapshot/fargate_task_test.rb +49 -0
  61. data/test/snapshot/ruby_process_test.rb +14 -0
  62. data/test/support/mock_timer.rb +20 -0
  63. data/test/test_helper.rb +16 -4
  64. data/test/tracing/custom_test.rb +1 -3
  65. data/test/tracing/id_management_test.rb +4 -0
  66. data/test/tracing/opentracing_test.rb +15 -2
  67. data/test/tracing/processor_test.rb +58 -0
  68. data/test/tracing/span_context_test.rb +21 -0
  69. data/test/tracing/span_test.rb +136 -0
  70. data/test/tracing/tracer_async_test.rb +29 -0
  71. data/test/tracing/tracer_test.rb +82 -16
  72. data/test/util_test.rb +10 -0
  73. metadata +71 -43
  74. data/lib/instana/agent.rb +0 -508
  75. data/lib/instana/agent/helpers.rb +0 -87
  76. data/lib/instana/agent/hooks.rb +0 -44
  77. data/lib/instana/agent/tasks.rb +0 -51
  78. data/lib/instana/collector.rb +0 -119
  79. data/lib/instana/collectors/gc.rb +0 -60
  80. data/lib/instana/collectors/memory.rb +0 -37
  81. data/lib/instana/collectors/thread.rb +0 -33
  82. data/lib/instana/eum/eum-test.js.erb +0 -17
  83. data/lib/instana/eum/eum.js.erb +0 -17
  84. data/lib/instana/helpers.rb +0 -47
  85. data/lib/instana/opentracing/tracer.rb +0 -21
  86. data/lib/instana/thread_local.rb +0 -18
  87. data/lib/oj_check.rb +0 -19
  88. 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
data/lib/instana/setup.rb CHANGED
@@ -1,30 +1,39 @@
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
 
14
12
  require 'instana/activator'
15
13
 
14
+ require 'instana/backend/request_client'
15
+ require 'instana/backend/gc_snapshot'
16
+ require 'instana/backend/process_info'
17
+
18
+ require 'instana/snapshot/deltable'
19
+ require 'instana/snapshot/ruby_process'
20
+ require 'instana/snapshot/fargate_process'
21
+ require 'instana/snapshot/fargate_task'
22
+ require 'instana/snapshot/fargate_container'
23
+ require 'instana/snapshot/docker_container'
24
+
25
+ require 'instana/backend/host_agent_lookup'
26
+ require 'instana/backend/host_agent_activation_observer'
27
+ require 'instana/backend/host_agent_reporting_observer'
28
+
29
+ require 'instana/backend/host_agent'
30
+ require 'instana/backend/serverless_agent'
31
+ require 'instana/backend/agent'
32
+
16
33
  ::Instana.setup
17
34
  ::Instana.agent.setup
18
35
  ::Instana::Activator.start
19
36
 
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
37
  # Require supported OpenTracing interfaces
29
38
  require "opentracing"
30
39
 
@@ -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
@@ -0,0 +1,151 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Snapshot
6
+ # Describes a Docker container visible to the current process
7
+ # @since 1.197.0
8
+ class DockerContainer
9
+ include Deltable
10
+ ID = 'com.instana.plugin.docker'.freeze
11
+
12
+ def initialize(container, metadata_uri: ENV['ECS_CONTAINER_METADATA_URI'])
13
+ @container = container
14
+ @metadata_uri = URI(metadata_uri)
15
+ @client = Backend::RequestClient.new(@metadata_uri.host, @metadata_uri.port, use_ssl: @metadata_uri.scheme == "https")
16
+ end
17
+
18
+ def entity_id
19
+ "#{@container['Labels']['com.amazonaws.ecs.task-arn']}::#{@container['Name']}"
20
+ end
21
+
22
+ def data
23
+ metrics = lookup('/task/stats').fetch(@container['DockerId'], {})
24
+
25
+ container_metrics(metrics)
26
+ .merge(container_metadata)
27
+ end
28
+
29
+ def snapshot
30
+ {
31
+ name: ID,
32
+ entityId: entity_id,
33
+ data: data
34
+ }
35
+ end
36
+
37
+ private
38
+
39
+ def container_metadata
40
+ {
41
+ Id: @container['DockerId'],
42
+ Created: @container['CreatedAt'],
43
+ Started: @container['StartedAt'],
44
+ Image: @container['Image'],
45
+ Labels: @container['Labels'],
46
+ Ports: @container['Ports'],
47
+ NetworkMode: @container['Networks'].first['NetworkMode']
48
+ }
49
+ end
50
+
51
+ def container_metrics(metrics)
52
+ return {} if metrics.empty?
53
+
54
+ {
55
+ memory: memory_stats(metrics),
56
+ blkio: blkio_stats(metrics),
57
+ cpu: cpu_stats(metrics),
58
+ network: network_stats(metrics)
59
+ }.compact
60
+ end
61
+
62
+ def memory_stats(metrics)
63
+ identity = ->(_old, new) { new }
64
+
65
+ {
66
+ active_anon: delta('memory_stats', 'stats', 'active_anon', compute: identity, obj: metrics),
67
+ active_file: delta('memory_stats', 'stats', 'active_file', compute: identity, obj: metrics),
68
+ inactive_anon: delta('memory_stats', 'stats', 'inactive_anon', compute: identity, obj: metrics),
69
+ inactive_file: delta('memory_stats', 'stats', 'inactive_file', compute: identity, obj: metrics),
70
+ total_cache: delta('memory_stats', 'stats', 'total_cache', compute: identity, obj: metrics),
71
+ total_rss: delta('memory_stats', 'stats', 'total_rss', compute: identity, obj: metrics),
72
+ usage: delta('memory_stats', 'usage', compute: identity, obj: metrics),
73
+ max_usage: delta('memory_stats', 'max_usage', compute: identity, obj: metrics),
74
+ limit: delta('memory_stats', 'limit', compute: identity, obj: metrics)
75
+ }
76
+ end
77
+
78
+ def blkio_stats(metrics)
79
+ delta = ->(old, new) { new - old }
80
+ bytes = {
81
+ 'block_bytes' => metrics['blkio_stats']['io_service_bytes_recursive'].map { |r| [r['op'], r['value']] }.to_h
82
+ }
83
+
84
+ {
85
+ blk_read: delta('block_bytes', 'Read', compute: delta, obj: bytes),
86
+ blk_write: delta('block_bytes', 'Write', compute: delta, obj: bytes)
87
+ }
88
+ end
89
+
90
+ def cpu_stats(metrics)
91
+ delta = ->(old, new) { new - old }
92
+ identity = ->(_old, new) { new }
93
+
94
+ cpu_system_delta = delta('cpu_stats', 'system_cpu_usage', compute: delta, obj: metrics).to_f
95
+ online_cpus = delta('cpu_stats', 'online_cpus', compute: identity, obj: metrics) || 1
96
+
97
+ {
98
+ total_usage: (delta('cpu_stats', 'cpu_usage', 'total_usage', compute: delta, obj: metrics) / cpu_system_delta) * online_cpus,
99
+ user_usage: (delta('cpu_stats', 'cpu_usage', 'usage_in_usermode', compute: delta, obj: metrics) / cpu_system_delta) * online_cpus,
100
+ system_usage: (delta('cpu_stats', 'cpu_usage', 'usage_in_kernelmode', compute: delta, obj: metrics) / cpu_system_delta) * online_cpus,
101
+ throttling_count: delta('cpu_stats', 'throttling_data', 'periods', compute: delta, obj: metrics),
102
+ throttling_time: delta('cpu_stats', 'throttling_data', 'throttled_time', compute: delta, obj: metrics)
103
+ }
104
+ end
105
+
106
+ def network_stats(metrics)
107
+ delta = ->(old, new) { new - old }
108
+ return nil unless metrics['networks']
109
+
110
+ interfaces = metrics['networks'].keys
111
+ payload = {
112
+ rx: {
113
+ bytes: 0,
114
+ dropped: 0,
115
+ errors: 0,
116
+ packet: 0
117
+ },
118
+ tx: {
119
+ bytes: 0,
120
+ dropped: 0,
121
+ errors: 0,
122
+ packet: 0
123
+ }
124
+ }
125
+
126
+ interfaces.each do |interface|
127
+ payload[:rx][:bytes] += delta('networks', interface, 'rx_bytes', compute: delta, obj: metrics)
128
+ payload[:rx][:dropped] += delta('networks', interface, 'rx_dropped', compute: delta, obj: metrics)
129
+ payload[:rx][:errors] += delta('networks', interface, 'rx_errors', compute: delta, obj: metrics)
130
+ payload[:rx][:packet] += delta('networks', interface, 'rx_packets', compute: delta, obj: metrics)
131
+
132
+ payload[:tx][:bytes] += delta('networks', interface, 'tx_bytes', compute: delta, obj: metrics)
133
+ payload[:tx][:dropped] += delta('networks', interface, 'tx_packets', compute: delta, obj: metrics)
134
+ payload[:tx][:errors] += delta('networks', interface, 'tx_errors', compute: delta, obj: metrics)
135
+ payload[:tx][:packet] += delta('networks', interface, 'tx_dropped', compute: delta, obj: metrics)
136
+ end
137
+
138
+ payload
139
+ end
140
+
141
+ def lookup(resource)
142
+ path = @metadata_uri.path + resource
143
+ response = @client.send_request('GET', path)
144
+
145
+ raise "Unable to get `#{path}`. Got `#{response.code}`." unless response.ok?
146
+
147
+ response.json
148
+ end
149
+ end
150
+ end
151
+ end