instana 1.195.4 → 1.197.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) 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 +57 -0
  10. data/lib/instana/backend/host_agent_activation_observer.rb +87 -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 +84 -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 +7 -21
  18. data/lib/instana/logger_delegator.rb +31 -0
  19. data/lib/instana/{opentracing → open_tracing}/carrier.rb +0 -0
  20. data/lib/instana/open_tracing/instana_tracer.rb +99 -0
  21. data/lib/instana/secrets.rb +6 -2
  22. data/lib/instana/setup.rb +20 -11
  23. data/lib/instana/snapshot/deltable.rb +25 -0
  24. data/lib/instana/snapshot/docker_container.rb +151 -0
  25. data/lib/instana/snapshot/fargate_container.rb +88 -0
  26. data/lib/instana/snapshot/fargate_process.rb +67 -0
  27. data/lib/instana/snapshot/fargate_task.rb +72 -0
  28. data/lib/instana/snapshot/ruby_process.rb +48 -0
  29. data/lib/instana/tracer.rb +25 -143
  30. data/lib/instana/tracing/processor.rb +4 -22
  31. data/lib/instana/tracing/span.rb +26 -35
  32. data/lib/instana/tracing/span_context.rb +1 -1
  33. data/lib/instana/util.rb +4 -67
  34. data/lib/instana/version.rb +1 -1
  35. data/lib/opentracing.rb +26 -3
  36. data/test/backend/agent_test.rb +54 -0
  37. data/test/backend/gc_snapshot_test.rb +11 -0
  38. data/test/backend/host_agent_activation_observer_test.rb +65 -0
  39. data/test/backend/host_agent_lookup_test.rb +78 -0
  40. data/test/backend/host_agent_reporting_observer_test.rb +192 -0
  41. data/test/backend/host_agent_test.rb +32 -0
  42. data/test/backend/process_info_test.rb +63 -0
  43. data/test/backend/request_client_test.rb +61 -0
  44. data/test/backend/serverless_agent_test.rb +73 -0
  45. data/test/config_test.rb +10 -0
  46. data/test/instana_test.rb +11 -4
  47. data/test/instrumentation/rack_instrumented_request_test.rb +3 -2
  48. data/test/instrumentation/rack_test.rb +2 -14
  49. data/test/secrets_test.rb +41 -22
  50. data/test/snapshot/deltable_test.rb +17 -0
  51. data/test/snapshot/docker_container_test.rb +82 -0
  52. data/test/snapshot/fargate_container_test.rb +82 -0
  53. data/test/snapshot/fargate_process_test.rb +35 -0
  54. data/test/snapshot/fargate_task_test.rb +49 -0
  55. data/test/snapshot/ruby_process_test.rb +14 -0
  56. data/test/support/mock_timer.rb +20 -0
  57. data/test/test_helper.rb +15 -3
  58. data/test/tracing/custom_test.rb +1 -3
  59. data/test/tracing/id_management_test.rb +4 -0
  60. data/test/tracing/opentracing_test.rb +15 -2
  61. data/test/tracing/processor_test.rb +58 -0
  62. data/test/tracing/span_context_test.rb +22 -0
  63. data/test/tracing/span_test.rb +127 -0
  64. data/test/tracing/tracer_async_test.rb +29 -0
  65. data/test/tracing/tracer_test.rb +82 -16
  66. data/test/util_test.rb +10 -0
  67. metadata +72 -45
  68. data/lib/instana/agent.rb +0 -508
  69. data/lib/instana/agent/helpers.rb +0 -87
  70. data/lib/instana/agent/hooks.rb +0 -44
  71. data/lib/instana/agent/tasks.rb +0 -51
  72. data/lib/instana/collector.rb +0 -119
  73. data/lib/instana/collectors/gc.rb +0 -60
  74. data/lib/instana/collectors/memory.rb +0 -37
  75. data/lib/instana/collectors/thread.rb +0 -33
  76. data/lib/instana/eum/eum-test.js.erb +0 -17
  77. data/lib/instana/eum/eum.js.erb +0 -17
  78. data/lib/instana/helpers.rb +0 -47
  79. data/lib/instana/opentracing/tracer.rb +0 -21
  80. data/lib/instana/thread_local.rb +0 -18
  81. data/lib/oj_check.rb +0 -19
  82. data/test/agent/agent_test.rb +0 -151
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0068c5ce2a6bf7c75d517562cec424a612a31d8d938fabb7651151f52b3cc5ea'
4
- data.tar.gz: cffa225ebc37b1a19a6a20d2418af57cd6e559a3635df17b5244bc2d603eaa20
3
+ metadata.gz: 58219b994af0387dcc4c672577b19c9570e487998be17d315a92902d6e1d86e6
4
+ data.tar.gz: 60e586b644f60545ebe6c7690d3ba673354d2133937b3b2bf95db04fbe8f58c4
5
5
  SHA512:
6
- metadata.gz: '0991b7a36fe13c5e4b8ebf93647f3a933087c0f92b8e94e2a3b4a8f7c8bcc24712560f4caf550328bc5390a51f59c334483e48b8be4b78236164b2d557608625'
7
- data.tar.gz: bd5ab3d0ba9f0d80a5b7c86068afb04f3889321fa5a154e43e97f89f577e244e481e0b2208a3fb76ae04452b26bcd32657ca2ea9cd6133230a7e53a8904fc52f
6
+ metadata.gz: 4d4ca8c71a1613c05e5f1fec02e5857c544013f0043d4b95e31fc900b81903ba5f3c08abd075afc4643e7761cd54a7595939ad9149575589b44b8e43722ea89a
7
+ data.tar.gz: 737447ad9f8ed3c6aef72bead3e66128b83ac1d346bc35f00ad1bd3e0b485f4cd8080faabf8817da23a788f229d32db1739e98484637fd8e70b852b415447d97
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,57 @@
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
+ def initialize(discovery: Concurrent::Atom.new(nil))
9
+ @discovery = discovery
10
+ @client = nil
11
+ end
12
+
13
+ def setup
14
+ return if ENV.key?('INSTANA_TEST')
15
+
16
+ @client = HostAgentLookup.new.call
17
+ @discovery
18
+ .with_observer(HostAgentActivationObserver.new(@client, @discovery))
19
+ .with_observer(HostAgentReportingObserver.new(@client, @discovery))
20
+ end
21
+
22
+ def spawn_background_thread
23
+ @discovery.swap { nil }
24
+ end
25
+
26
+ # @return [Boolean] true if the agent able to send spans to the backend
27
+ def ready?
28
+ ENV.key?('INSTANA_TEST') || !@discovery.value.nil?
29
+ end
30
+
31
+ # @return [Hash, NilClass] the backend friendly description of the current in process collector
32
+ def source
33
+ {
34
+ e: discovery_value['pid'],
35
+ h: discovery_value['agentUuid']
36
+ }.compact
37
+ end
38
+
39
+ # @return [Array] extra headers to include in the trace
40
+ def extra_headers
41
+ discovery_value['extraHeaders']
42
+ end
43
+
44
+ # @return [Hash] values which are removed from urls sent to the backend
45
+ def secret_values
46
+ discovery_value['secrets']
47
+ end
48
+
49
+ private
50
+
51
+ def discovery_value
52
+ v = @discovery.value
53
+ v || {}
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,87 @@
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: 60, logger: ::Instana.logger, max_wait_tries: 60, proc_table: Sys::ProcTable) # 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
+ end
23
+
24
+ def update(_time, _old_version, new_version)
25
+ return unless new_version.nil?
26
+
27
+ try_forever_with_backoff do
28
+ payload = discovery_payload
29
+ discovery_response = @client.send_request('PUT', DISCOVERY_URL, payload)
30
+
31
+ raise DiscoveryError, "Discovery response was #{discovery_response.code} with `#{payload}`." unless discovery_response.ok?
32
+
33
+ discovery = discovery_response.json
34
+ @logger.debug("Discovery complete (`#{discovery}`). Waiting for agent.")
35
+ wait_for_backend(discovery['pid'])
36
+ @logger.debug("Agent ready.")
37
+ @discovery.swap { discovery }
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def discovery_payload
44
+ proc_table = @proc_table.ps(pid: Process.pid)
45
+ process = ProcessInfo.new(proc_table)
46
+
47
+ payload = {
48
+ name: process.name,
49
+ args: process.arguments,
50
+ pid: process.parent_pid,
51
+ pidFromParentNS: process.from_parent_namespace,
52
+ cpuSetFileContent: process.cpuset
53
+ }
54
+
55
+ if @client.fileno && @client.inode
56
+ payload[:fd] = @client.fileno
57
+ payload[:inode] = @client.inode
58
+ end
59
+
60
+ payload.compact
61
+ end
62
+
63
+ def wait_for_backend(pid)
64
+ response = @max_wait_tries.times do
65
+ path = format(ENTITY_DATA_URL, pid)
66
+ wait_response = @client.send_request('HEAD', path)
67
+
68
+ break(wait_response) if wait_response.ok?
69
+
70
+ sleep(1)
71
+ end
72
+
73
+ raise DiscoveryError, "The backend didn't respond in time." unless response.is_a?(RequestClient::Response) && response.ok?
74
+ end
75
+
76
+ def try_forever_with_backoff
77
+ yield
78
+ rescue DiscoveryError, Net::OpenTimeout => e
79
+ @logger.error(e)
80
+ sleep(@wait_time)
81
+ retry
82
+ rescue StandardError => e
83
+ @logger.error(%(#{e}\n#{e.backtrace.join("\n")}))
84
+ end
85
+ end
86
+ end
87
+ 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