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.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -2
- data/Rakefile +1 -1
- data/instana.gemspec +3 -7
- data/lib/instana.rb +3 -0
- data/lib/instana/activator.rb +2 -0
- data/lib/instana/backend/agent.rb +60 -0
- data/lib/instana/backend/gc_snapshot.rb +41 -0
- data/lib/instana/backend/host_agent.rb +74 -0
- data/lib/instana/backend/host_agent_activation_observer.rb +97 -0
- data/lib/instana/backend/host_agent_lookup.rb +57 -0
- data/lib/instana/backend/host_agent_reporting_observer.rb +106 -0
- data/lib/instana/backend/process_info.rb +64 -0
- data/lib/instana/backend/request_client.rb +73 -0
- data/lib/instana/backend/serverless_agent.rb +118 -0
- data/lib/instana/base.rb +8 -27
- data/lib/instana/config.rb +8 -22
- data/lib/instana/instrumentation/excon.rb +17 -8
- data/lib/instana/instrumentation/instrumented_request.rb +62 -7
- data/lib/instana/instrumentation/net-http.rb +7 -5
- data/lib/instana/instrumentation/rack.rb +12 -7
- data/lib/instana/logger_delegator.rb +31 -0
- data/lib/instana/{opentracing → open_tracing}/carrier.rb +0 -0
- data/lib/instana/open_tracing/instana_tracer.rb +99 -0
- data/lib/instana/secrets.rb +6 -2
- data/lib/instana/setup.rb +20 -11
- data/lib/instana/snapshot/deltable.rb +25 -0
- data/lib/instana/snapshot/docker_container.rb +151 -0
- data/lib/instana/snapshot/fargate_container.rb +88 -0
- data/lib/instana/snapshot/fargate_process.rb +67 -0
- data/lib/instana/snapshot/fargate_task.rb +72 -0
- data/lib/instana/snapshot/lambda_function.rb +36 -0
- data/lib/instana/snapshot/ruby_process.rb +48 -0
- data/lib/instana/tracer.rb +25 -143
- data/lib/instana/tracing/processor.rb +14 -22
- data/lib/instana/tracing/span.rb +31 -34
- data/lib/instana/tracing/span_context.rb +15 -10
- data/lib/instana/util.rb +8 -69
- data/lib/instana/version.rb +1 -1
- data/lib/opentracing.rb +26 -3
- data/test/backend/agent_test.rb +54 -0
- data/test/backend/gc_snapshot_test.rb +11 -0
- data/test/backend/host_agent_activation_observer_test.rb +72 -0
- data/test/backend/host_agent_lookup_test.rb +78 -0
- data/test/backend/host_agent_reporting_observer_test.rb +192 -0
- data/test/backend/host_agent_test.rb +47 -0
- data/test/backend/process_info_test.rb +63 -0
- data/test/backend/request_client_test.rb +39 -0
- data/test/backend/serverless_agent_test.rb +73 -0
- data/test/config_test.rb +10 -0
- data/test/instana_test.rb +11 -4
- data/test/instrumentation/excon_test.rb +15 -1
- data/test/instrumentation/rack_instrumented_request_test.rb +5 -2
- data/test/instrumentation/rack_test.rb +2 -14
- data/test/secrets_test.rb +41 -22
- data/test/snapshot/deltable_test.rb +17 -0
- data/test/snapshot/docker_container_test.rb +82 -0
- data/test/snapshot/fargate_container_test.rb +82 -0
- data/test/snapshot/fargate_process_test.rb +35 -0
- data/test/snapshot/fargate_task_test.rb +49 -0
- data/test/snapshot/ruby_process_test.rb +14 -0
- data/test/support/mock_timer.rb +20 -0
- data/test/test_helper.rb +16 -4
- data/test/tracing/custom_test.rb +1 -3
- data/test/tracing/id_management_test.rb +4 -0
- data/test/tracing/opentracing_test.rb +15 -2
- data/test/tracing/processor_test.rb +58 -0
- data/test/tracing/span_context_test.rb +21 -0
- data/test/tracing/span_test.rb +136 -0
- data/test/tracing/tracer_async_test.rb +29 -0
- data/test/tracing/tracer_test.rb +82 -16
- data/test/util_test.rb +10 -0
- metadata +71 -43
- data/lib/instana/agent.rb +0 -508
- data/lib/instana/agent/helpers.rb +0 -87
- data/lib/instana/agent/hooks.rb +0 -44
- data/lib/instana/agent/tasks.rb +0 -51
- data/lib/instana/collector.rb +0 -119
- data/lib/instana/collectors/gc.rb +0 -60
- data/lib/instana/collectors/memory.rb +0 -37
- data/lib/instana/collectors/thread.rb +0 -33
- data/lib/instana/eum/eum-test.js.erb +0 -17
- data/lib/instana/eum/eum.js.erb +0 -17
- data/lib/instana/helpers.rb +0 -47
- data/lib/instana/opentracing/tracer.rb +0 -21
- data/lib/instana/thread_local.rb +0 -18
- data/lib/oj_check.rb +0 -19
- data/test/agent/agent_test.rb +0 -151
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a25aa837fcbb248cb39db1f57b7bdc8904810d5e63d87be7e12d912b7b9aea67
|
4
|
+
data.tar.gz: 836dc1f8d7ffc4c8c19e3fa90867a306fe7d407fb88a14481d44bde1dc482285
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
|
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
data/lib/instana/activator.rb
CHANGED
@@ -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
|