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
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