instana 1.195.2 → 1.197.0

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