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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ecd5d44386711b77f9063712fe05c1831771a9c6740c429d75244ed31594730
4
- data.tar.gz: a9babf1b336a2502f0d0b9c1bacd5a8e1283519b02f5d32aea8e58523d50d766
3
+ metadata.gz: a25aa837fcbb248cb39db1f57b7bdc8904810d5e63d87be7e12d912b7b9aea67
4
+ data.tar.gz: 836dc1f8d7ffc4c8c19e3fa90867a306fe7d407fb88a14481d44bde1dc482285
5
5
  SHA512:
6
- metadata.gz: 84916aa5ce872b6d309a5b6b97b50e469b98f7a0cae67c29d2be4904e7f120aa464b85b88b4fac4329165d8f77ea7783066a766d69f5809fd85faa1ad989640f
7
- data.tar.gz: c94e2ef1d5d72362c64355be70cef8a9835bbe23acd14da29a57c11918c1df0fffafa3ea494a4d87d9349318588d3bc0ee1c6ae5dc9a1b21c3a07271cf487b6f
6
+ metadata.gz: b8f3ba0fbf4a015ff1522a3c24e6b219594d2c266bc46427a93979e6ee159272d2c23a8fc53e4720021c5c531ad64ffd1d1f2644b8eb41497aaf8950c99ed8b6
7
+ data.tar.gz: 44e5b93aff42f542eaee494128e973a9447210fbffde21c4b361f1b00729c8b3378634241d64039453ea3dda0dc2a0d207473a0c5631f1f09e77726b2a827532
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ inherit_from: .rubocop_todo.yml
2
2
 
3
3
  require:
4
4
  - ./extras/license_header.rb
5
-
5
+
6
6
  # Remove when we remove .rubocop_todo.yml
7
7
  AllCops:
8
8
  NewCops: disable
@@ -18,4 +18,8 @@ Style/NilComparison:
18
18
  Enabled: false
19
19
 
20
20
  Style/SoleNestedConditional:
21
- Enabled: false
21
+ Enabled: false
22
+
23
+ # Ruby 2.1 compatibility
24
+ Style/SafeNavigation:
25
+ Enabled: false
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ Rake::TestTask.new(:test) do |t|
22
22
  else
23
23
  t.test_files = Dir[
24
24
  'test/*_test.rb',
25
- 'test/{agent,tracing}/*_test.rb'
25
+ 'test/{agent,tracing,backend,snapshot}/*_test.rb'
26
26
  ]
27
27
  end
28
28
  end
data/instana.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  }
26
26
 
27
27
  spec.licenses = ['MIT']
28
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test)/}) }
29
29
  spec.bindir = "exe"
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
@@ -38,13 +38,9 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency "rake", "~> 10.0"
39
39
  spec.add_development_dependency "minitest", "~> 5.0"
40
40
  spec.add_development_dependency "appraisal"
41
+ spec.add_development_dependency "fakefs"
41
42
 
43
+ spec.add_runtime_dependency('concurrent-ruby', '>= 1.1')
42
44
  spec.add_runtime_dependency('sys-proctable', '>= 1.2.2')
43
- spec.add_runtime_dependency('get_process_mem', '>= 0.2.1')
44
- spec.add_runtime_dependency('timers', '>= 4.0.4')
45
45
  spec.add_runtime_dependency('oj', '>=3.0.11') unless RUBY_PLATFORM =~ /java/i
46
-
47
- # Indirect dependency
48
- # https://github.com/instana/ruby-sensor/issues/10
49
- spec.add_runtime_dependency('ffi', '>=1.0.11')
50
46
  end
data/lib/instana.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # (c) Copyright IBM Corp. 2021
2
2
  # (c) Copyright Instana Inc. 2016
3
3
 
4
+ require 'concurrent'
5
+ require 'sys-proctable'
6
+
4
7
  require "instana/setup"
5
8
 
6
9
  # Boot the instana agent background thread. If you wish to have greater
@@ -7,12 +7,14 @@ module Instana
7
7
  attr_reader :trace_point, :activators
8
8
 
9
9
  def start
10
+ # :nocov:
10
11
  @trace_point = TracePoint.new(:end) do
11
12
  activated = ::Instana::Activator.call
12
13
  ::Instana.logger.debug { "Activated #{activated.join(', ')}" } unless activated.empty?
13
14
  end
14
15
 
15
16
  @trace_point.enable if enabled?
17
+ # :nocov:
16
18
  end
17
19
 
18
20
  def call
@@ -0,0 +1,60 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Backend
6
+ # Wrapper class around the various transport backends
7
+ # @since 1.197.0
8
+ class Agent
9
+ attr_reader :delegate
10
+
11
+ def initialize(fargate_metadata_uri: ENV['ECS_CONTAINER_METADATA_URI'], logger: ::Instana.logger)
12
+ @delegate = nil
13
+ @logger = logger
14
+ @fargate_metadata_uri = fargate_metadata_uri
15
+ end
16
+
17
+ def setup
18
+ @delegate = if @fargate_metadata_uri && ENV.key?('INSTANA_ENDPOINT_URL')
19
+ ServerlessAgent.new(fargate_snapshots)
20
+ else
21
+ HostAgent.new
22
+ end
23
+
24
+ @delegate.setup
25
+ end
26
+
27
+ def method_missing(mth, *args, &block)
28
+ if @delegate.respond_to?(mth)
29
+ @delegate.public_send(mth, *args, &block)
30
+ else
31
+ super(mth, *args, &block)
32
+ end
33
+ end
34
+
35
+ def respond_to_missing?(mth, include_all = false)
36
+ @delegate.respond_to?(mth, include_all)
37
+ end
38
+
39
+ private
40
+
41
+ def fargate_snapshots
42
+ metadata_uri = URI(@fargate_metadata_uri)
43
+ client = Backend::RequestClient.new(metadata_uri.host, metadata_uri.port, use_ssl: metadata_uri.scheme == "https")
44
+ response = client.send_request('GET', "#{metadata_uri.path}/task")
45
+
46
+ if response.ok?
47
+ docker = response
48
+ .json['Containers']
49
+ .map { |c| [Snapshot::DockerContainer.new(c), Snapshot::FargateContainer.new(c)] }
50
+ .flatten
51
+
52
+ docker + [Snapshot::FargateProcess.new, Snapshot::RubyProcess.new, Snapshot::FargateTask.new]
53
+ else
54
+ @logger.warn("Received #{response.code} when requesting containers.")
55
+ []
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,41 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'singleton'
5
+
6
+ module Instana
7
+ module Backend
8
+ # Keeps track of garbage collector related metrics
9
+ # @since 1.197.0
10
+ class GCSnapshot
11
+ include Singleton
12
+
13
+ def initialize
14
+ ::GC::Profiler.enable
15
+
16
+ @last_major_count = 0
17
+ @last_minor_count = 0
18
+ end
19
+
20
+ def report
21
+ stats = ::GC.stat
22
+ total_time = ::GC::Profiler.total_time * 1000
23
+
24
+ ::GC::Profiler.clear
25
+
26
+ payload = {
27
+ totalTime: total_time,
28
+ heap_live: stats[:heap_live_slots] || stats[:heap_live_num],
29
+ heap_free: stats[:heap_free_slots] || stats[:heap_free_num],
30
+ minorGcs: stats[:minor_gc_count] - @last_minor_count,
31
+ majorGcs: stats[:major_gc_count] - @last_major_count
32
+ }
33
+
34
+ @last_major_count = stats[:major_gc_count]
35
+ @last_minor_count = stats[:minor_gc_count]
36
+
37
+ payload
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,74 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Backend
6
+ # @since 1.197.0
7
+ class HostAgent
8
+ attr_reader :future
9
+
10
+ def initialize(discovery: Concurrent::Atom.new(nil), logger: ::Instana.logger)
11
+ @discovery = discovery
12
+ @logger = logger
13
+ @future = nil
14
+ end
15
+
16
+ def setup; end
17
+
18
+ def spawn_background_thread
19
+ return if ENV.key?('INSTANA_TEST')
20
+
21
+ @future = Concurrent::Promises.future do
22
+ client = until_not_nil { HostAgentLookup.new.call }
23
+ @discovery.delete_observers
24
+ @discovery
25
+ .with_observer(HostAgentActivationObserver.new(client, @discovery))
26
+ .with_observer(HostAgentReportingObserver.new(client, @discovery))
27
+
28
+ @discovery.swap { nil }
29
+ client
30
+ end
31
+ end
32
+
33
+ # @return [Boolean] true if the agent able to send spans to the backend
34
+ def ready?
35
+ ENV.key?('INSTANA_TEST') || !@discovery.value.nil?
36
+ end
37
+
38
+ # @return [Hash, NilClass] the backend friendly description of the current in process collector
39
+ def source
40
+ {
41
+ e: discovery_value['pid'],
42
+ h: discovery_value['agentUuid']
43
+ }.compact
44
+ end
45
+
46
+ # @return [Array] extra headers to include in the trace
47
+ def extra_headers
48
+ discovery_value['extraHeaders']
49
+ end
50
+
51
+ # @return [Hash] values which are removed from urls sent to the backend
52
+ def secret_values
53
+ discovery_value['secrets']
54
+ end
55
+
56
+ private
57
+
58
+ def until_not_nil
59
+ loop do
60
+ result = yield
61
+ return result unless result.nil?
62
+
63
+ @logger.debug("Waiting on a connection to the agent.")
64
+ sleep(1)
65
+ end
66
+ end
67
+
68
+ def discovery_value
69
+ v = @discovery.value
70
+ v || {}
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,97 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Backend
6
+ # Process which is responsible for initiating monitoring of a Ruby program with a local agent.
7
+ # @since 1.197.0
8
+ class HostAgentActivationObserver
9
+ DISCOVERY_URL = '/com.instana.plugin.ruby.discovery'.freeze
10
+ ENTITY_DATA_URL = '/com.instana.plugin.ruby.%i'.freeze
11
+ class DiscoveryError < StandardError; end
12
+
13
+ # @param [RequestClient] client used to make requests to the backend
14
+ # @param [Concurrent::Atom] discovery object used to store discovery response in
15
+ def initialize(client, discovery, wait_time: 1, logger: ::Instana.logger, max_wait_tries: 60, proc_table: Sys::ProcTable, socket_proc: default_socket_proc) # rubocop:disable Metrics/ParameterLists
16
+ @client = client
17
+ @discovery = discovery
18
+ @wait_time = wait_time
19
+ @logger = logger
20
+ @max_wait_tries = max_wait_tries
21
+ @proc_table = proc_table
22
+ @socket_proc = socket_proc
23
+ end
24
+
25
+ def update(_time, _old_version, new_version)
26
+ return unless new_version.nil?
27
+
28
+ socket = @socket_proc.call(@client)
29
+
30
+ try_forever_with_backoff do
31
+ payload = discovery_payload(socket)
32
+ discovery_response = @client.send_request('PUT', DISCOVERY_URL, payload)
33
+
34
+ raise DiscoveryError, "Discovery response was #{discovery_response.code} with `#{payload}`." unless discovery_response.ok?
35
+
36
+ discovery = discovery_response.json
37
+ @logger.debug("Discovery complete (`#{discovery}`). Waiting for agent.")
38
+ wait_for_backend(discovery['pid'])
39
+ @logger.debug("Agent ready.")
40
+ @discovery.swap { discovery }
41
+ end
42
+
43
+ socket.close
44
+ end
45
+
46
+ private
47
+
48
+ def discovery_payload(socket)
49
+ proc_table = @proc_table.ps(pid: Process.pid)
50
+ process = ProcessInfo.new(proc_table)
51
+
52
+ payload = {
53
+ name: process.name,
54
+ args: process.arguments,
55
+ pid: process.parent_pid,
56
+ pidFromParentNS: process.from_parent_namespace,
57
+ cpuSetFileContent: process.cpuset
58
+ }
59
+
60
+ inode_path = "/proc/self/fd/#{socket.fileno}"
61
+ if socket.fileno && File.exist?(inode_path)
62
+ payload[:fd] = socket.fileno
63
+ payload[:inode] = File.readlink(inode_path)
64
+ end
65
+
66
+ payload.compact
67
+ end
68
+
69
+ def wait_for_backend(pid)
70
+ response = @max_wait_tries.times do
71
+ path = format(ENTITY_DATA_URL, pid)
72
+ wait_response = @client.send_request('HEAD', path)
73
+
74
+ break(wait_response) if wait_response.ok?
75
+
76
+ sleep(1)
77
+ end
78
+
79
+ raise DiscoveryError, "The backend didn't respond in time." unless response.is_a?(RequestClient::Response) && response.ok?
80
+ end
81
+
82
+ def try_forever_with_backoff
83
+ yield
84
+ rescue DiscoveryError, Net::OpenTimeout => e
85
+ @logger.warn(e)
86
+ sleep(@wait_time)
87
+ retry
88
+ rescue StandardError => e
89
+ @logger.error(%(#{e}\n#{e.backtrace.join("\n")}))
90
+ end
91
+
92
+ def default_socket_proc
93
+ ->(c) { TCPSocket.new(c.host, c.port) }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,57 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ require 'csv'
5
+
6
+ module Instana
7
+ module Backend
8
+ # Utility class to discover the agent that a given instance of the collector
9
+ # needs to communicate with.
10
+ # @since 1.197.0
11
+ class HostAgentLookup
12
+ def initialize(host = ::Instana.config[:agent_host], port = ::Instana.config[:agent_port], destination: '00000000')
13
+ @host = host
14
+ @port = port
15
+ @destination = destination
16
+ end
17
+
18
+ # @return [RequestClient, NilClass] the request client to use to communicate with the agent or nil if no agent could be found
19
+ def call
20
+ host_listening?(@host, @port) || host_listening?(default_gateway, @port)
21
+ end
22
+
23
+ private
24
+
25
+ # @return [RequestClient, nil] the request client if it responds to '/' with a success
26
+ def host_listening?(host, port)
27
+ client = RequestClient.new(host, port)
28
+ client.send_request('GET', '/').ok? ? client : nil
29
+ rescue Net::OpenTimeout, Errno::ECONNREFUSED => _e
30
+ nil
31
+ end
32
+
33
+ # @return [String] the default gateway to attempt to connect to or the @host if a default gateway can not be identified
34
+ def default_gateway
35
+ return @host unless File.exist?('/proc/self/net/route')
36
+
37
+ routes = CSV.read(
38
+ '/proc/self/net/route',
39
+ headers: :first_row,
40
+ col_sep: "\t",
41
+ header_converters: [->(v) { v.strip }],
42
+ converters: [->(v) { v.strip }]
43
+ )
44
+
45
+ route = routes.detect { |r| r['Destination'] == @destination }
46
+ return @host unless route
47
+
48
+ route['Gateway']
49
+ .split(/([0-9A-Z]{2})/)
50
+ .reject(&:empty?)
51
+ .reverse
52
+ .map { |s| s.to_i(16) }
53
+ .join('.')
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,106 @@
1
+ # (c) Copyright IBM Corp. 2021
2
+ # (c) Copyright Instana Inc. 2021
3
+
4
+ module Instana
5
+ module Backend
6
+ # Process which is responsible for reporting metrics and tracing to the local agent
7
+ # @since 1.197.0
8
+ class HostAgentReportingObserver
9
+ ENTITY_DATA_URL = '/com.instana.plugin.ruby.%i'.freeze
10
+ RESPONSE_DATA_URL = '/com.instana.plugin.ruby/response.%i?messageId=%s'.freeze
11
+ TRACES_DATA_URL = "/com.instana.plugin.ruby/traces.%i".freeze
12
+
13
+ attr_reader :report_timer
14
+
15
+ # @param [RequestClient] client used to make requests to the backend
16
+ # @param [Concurrent::Atom] discovery object used to store discovery response in
17
+ def initialize(client, discovery, logger: ::Instana.logger, timer_class: Concurrent::TimerTask, processor: ::Instana.processor)
18
+ @client = client
19
+ @discovery = discovery
20
+ @logger = logger
21
+ @report_timer = timer_class.new(execution_interval: 1, run_now: true) { report_to_backend }
22
+ @nonce = Time.now
23
+ @processor = processor
24
+ end
25
+
26
+ def update(time, _old_version, new_version)
27
+ return unless time > @nonce
28
+
29
+ @nonce = time
30
+ new_version.nil? ? @report_timer.shutdown : @report_timer.execute
31
+ end
32
+
33
+ private
34
+
35
+ def report_to_backend
36
+ report_metrics if ::Instana.config[:metrics][:enabled]
37
+ report_traces if ::Instana.config[:tracing][:enabled]
38
+ rescue StandardError => e
39
+ @logger.error(%(#{e}\n#{e.backtrace.join("\n")}))
40
+ end
41
+
42
+ def report_traces
43
+ discovery = @discovery.value
44
+ return unless discovery
45
+
46
+ path = format(TRACES_DATA_URL, discovery['pid'])
47
+
48
+ @processor.send do |spans|
49
+ response = @client.send_request('POST', path, spans)
50
+
51
+ unless response.ok?
52
+ @discovery.swap { nil }
53
+ break
54
+ end
55
+
56
+ @logger.debug("Sent `#{spans.count}` spans to `#{path}` and got `#{response.code}`.")
57
+ end
58
+ end
59
+
60
+ def report_metrics
61
+ discovery = @discovery.value
62
+ return unless discovery
63
+
64
+ path = format(ENTITY_DATA_URL, discovery['pid'])
65
+ payload = metrics_payload(discovery).merge(Util.take_snapshot)
66
+ response = @client.send_request('POST', path, payload)
67
+
68
+ if response.ok?
69
+ handle_agent_tasks(response, discovery) unless response.body.empty?
70
+ else
71
+ @discovery.swap { nil }
72
+ end
73
+
74
+ @logger.debug("Sent `#{payload}` to `#{path}` and got `#{response.code}`.")
75
+ end
76
+
77
+ def handle_agent_tasks(response, discovery)
78
+ payload = response.json
79
+ payload = [payload] if payload.is_a?(Hash)
80
+ payload
81
+ .select { |t| t['action'] == 'ruby.source' }
82
+ .each do |action|
83
+ payload = ::Instana::Util.get_rb_source(action['args']['file'])
84
+ path = format(RESPONSE_DATA_URL, discovery['pid'], action['messageId'])
85
+ @client.send_request('POST', path, payload)
86
+ end
87
+ rescue StandardError => e
88
+ @logger.debug("Error processing agent task #{e.inspect}")
89
+ end
90
+
91
+ def metrics_payload(discovery)
92
+ proc_table = Sys::ProcTable.ps(pid: Process.pid)
93
+ process = ProcessInfo.new(proc_table)
94
+
95
+ {
96
+ pid: discovery['pid'],
97
+ name: Util.get_app_name,
98
+ exec_args: process.arguments,
99
+ gc: GCSnapshot.instance.report,
100
+ thread: {count: ::Thread.list.count},
101
+ memory: {rss_size: proc_table.rss / 1024} # Bytes to Kilobytes
102
+ }
103
+ end
104
+ end
105
+ end
106
+ end