instana 1.197.0.pre1 → 1.199.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/lib/instana/backend/agent.rb +9 -1
- data/lib/instana/backend/host_agent.rb +27 -10
- data/lib/instana/backend/host_agent_activation_observer.rb +17 -7
- data/lib/instana/backend/request_client.rb +6 -16
- data/lib/instana/backend/serverless_agent.rb +13 -18
- data/lib/instana/base.rb +2 -0
- data/lib/instana/config.rb +1 -1
- data/lib/instana/instrumentation/excon.rb +7 -5
- 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/serverless.rb +139 -0
- data/lib/instana/setup.rb +5 -0
- data/lib/instana/snapshot/google_cloud_run_instance.rb +69 -0
- data/lib/instana/snapshot/google_cloud_run_process.rb +58 -0
- data/lib/instana/snapshot/lambda_function.rb +39 -0
- data/lib/instana/tracing/processor.rb +11 -1
- data/lib/instana/tracing/span.rb +10 -4
- data/lib/instana/tracing/span_context.rb +14 -9
- data/lib/instana/util.rb +4 -2
- data/lib/instana/version.rb +1 -1
- data/test/backend/agent_test.rb +26 -0
- data/test/backend/host_agent_activation_observer_test.rb +16 -9
- data/test/backend/host_agent_test.rb +17 -2
- data/test/backend/request_client_test.rb +0 -22
- data/test/instrumentation/rack_instrumented_request_test.rb +2 -0
- data/test/serverless_test.rb +323 -0
- data/test/snapshot/google_cloud_run_instance_test.rb +74 -0
- data/test/snapshot/google_cloud_run_process_test.rb +33 -0
- data/test/snapshot/lambda_function_test.rb +37 -0
- data/test/test_helper.rb +1 -1
- data/test/tracing/id_management_test.rb +4 -0
- data/test/tracing/span_context_test.rb +3 -3
- data/test/tracing/span_test.rb +9 -0
- metadata +16 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86a3e023781d52d3b5efd25c9b5d304520dd3c88aecde954c326bd736abae787
|
4
|
+
data.tar.gz: b6c7bf11431925b9a48b827b5adbb52d93cb1f1f44bde1ebef98bd89f2188cb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2045faa632b6edc00e4dba26feb69d5249c755698a0dac2adeaa0e2b1cbe65f7f37682f7b59a1d3d000f8908dbee85c4f0356da333f02e874361b5861cfcc3f
|
7
|
+
data.tar.gz: e1b3983ace1be9653732b033902c7f59a74dce020ebfb585e02cf3dfdd1c2e7eb50df4cff9ff4eb8467df3a57b3208165efe659d97073bd6bc97f9e2bdd62fa0
|
@@ -15,7 +15,15 @@ module Instana
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def setup
|
18
|
-
@delegate = if
|
18
|
+
@delegate = if ENV.key?('_HANDLER')
|
19
|
+
ServerlessAgent.new([Snapshot::LambdaFunction.new])
|
20
|
+
elsif ENV.key?('K_REVISION') && ENV.key?('INSTANA_ENDPOINT_URL')
|
21
|
+
ServerlessAgent.new([
|
22
|
+
Snapshot::GoogleCloudRunProcess.new,
|
23
|
+
Snapshot::GoogleCloudRunInstance.new,
|
24
|
+
Snapshot::RubyProcess.new
|
25
|
+
])
|
26
|
+
elsif @fargate_metadata_uri && ENV.key?('INSTANA_ENDPOINT_URL')
|
19
27
|
ServerlessAgent.new(fargate_snapshots)
|
20
28
|
else
|
21
29
|
HostAgent.new
|
@@ -5,22 +5,29 @@ module Instana
|
|
5
5
|
module Backend
|
6
6
|
# @since 1.197.0
|
7
7
|
class HostAgent
|
8
|
-
|
8
|
+
attr_reader :future
|
9
|
+
|
10
|
+
def initialize(discovery: Concurrent::Atom.new(nil), logger: ::Instana.logger)
|
9
11
|
@discovery = discovery
|
10
|
-
@
|
12
|
+
@logger = logger
|
13
|
+
@future = nil
|
11
14
|
end
|
12
15
|
|
13
|
-
def setup
|
16
|
+
def setup; end
|
17
|
+
|
18
|
+
def spawn_background_thread
|
14
19
|
return if ENV.key?('INSTANA_TEST')
|
15
20
|
|
16
|
-
@
|
17
|
-
|
18
|
-
|
19
|
-
|
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))
|
21
27
|
|
22
|
-
|
23
|
-
|
28
|
+
@discovery.swap { nil }
|
29
|
+
client
|
30
|
+
end
|
24
31
|
end
|
25
32
|
|
26
33
|
# @return [Boolean] true if the agent able to send spans to the backend
|
@@ -48,6 +55,16 @@ module Instana
|
|
48
55
|
|
49
56
|
private
|
50
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
|
+
|
51
68
|
def discovery_value
|
52
69
|
v = @discovery.value
|
53
70
|
v || {}
|
@@ -12,20 +12,23 @@ module Instana
|
|
12
12
|
|
13
13
|
# @param [RequestClient] client used to make requests to the backend
|
14
14
|
# @param [Concurrent::Atom] discovery object used to store discovery response in
|
15
|
-
def initialize(client, discovery, wait_time:
|
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
16
|
@client = client
|
17
17
|
@discovery = discovery
|
18
18
|
@wait_time = wait_time
|
19
19
|
@logger = logger
|
20
20
|
@max_wait_tries = max_wait_tries
|
21
21
|
@proc_table = proc_table
|
22
|
+
@socket_proc = socket_proc
|
22
23
|
end
|
23
24
|
|
24
25
|
def update(_time, _old_version, new_version)
|
25
26
|
return unless new_version.nil?
|
26
27
|
|
28
|
+
socket = @socket_proc.call(@client)
|
29
|
+
|
27
30
|
try_forever_with_backoff do
|
28
|
-
payload = discovery_payload
|
31
|
+
payload = discovery_payload(socket)
|
29
32
|
discovery_response = @client.send_request('PUT', DISCOVERY_URL, payload)
|
30
33
|
|
31
34
|
raise DiscoveryError, "Discovery response was #{discovery_response.code} with `#{payload}`." unless discovery_response.ok?
|
@@ -36,11 +39,13 @@ module Instana
|
|
36
39
|
@logger.debug("Agent ready.")
|
37
40
|
@discovery.swap { discovery }
|
38
41
|
end
|
42
|
+
|
43
|
+
socket.close
|
39
44
|
end
|
40
45
|
|
41
46
|
private
|
42
47
|
|
43
|
-
def discovery_payload
|
48
|
+
def discovery_payload(socket)
|
44
49
|
proc_table = @proc_table.ps(pid: Process.pid)
|
45
50
|
process = ProcessInfo.new(proc_table)
|
46
51
|
|
@@ -52,9 +57,10 @@ module Instana
|
|
52
57
|
cpuSetFileContent: process.cpuset
|
53
58
|
}
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
payload[:
|
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)
|
58
64
|
end
|
59
65
|
|
60
66
|
payload.compact
|
@@ -76,12 +82,16 @@ module Instana
|
|
76
82
|
def try_forever_with_backoff
|
77
83
|
yield
|
78
84
|
rescue DiscoveryError, Net::OpenTimeout => e
|
79
|
-
@logger.
|
85
|
+
@logger.warn(e)
|
80
86
|
sleep(@wait_time)
|
81
87
|
retry
|
82
88
|
rescue StandardError => e
|
83
89
|
@logger.error(%(#{e}\n#{e.backtrace.join("\n")}))
|
84
90
|
end
|
91
|
+
|
92
|
+
def default_socket_proc
|
93
|
+
->(c) { TCPSocket.new(c.host, c.port) }
|
94
|
+
end
|
85
95
|
end
|
86
96
|
end
|
87
97
|
end
|
@@ -18,6 +18,8 @@ module Instana
|
|
18
18
|
# Convince wrapper around {Net::HTTP}.
|
19
19
|
# @since 1.197.0
|
20
20
|
class RequestClient
|
21
|
+
attr_reader :host, :port
|
22
|
+
|
21
23
|
class Response < SimpleDelegator
|
22
24
|
# @return [Hash] the decoded json response
|
23
25
|
def json
|
@@ -31,7 +33,10 @@ module Instana
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def initialize(host, port, use_ssl: false)
|
34
|
-
|
36
|
+
timeout = Integer(ENV.fetch('INSTANA_TIMEOUT', 500))
|
37
|
+
@host = host
|
38
|
+
@port = port
|
39
|
+
@client = Net::HTTP.start(host, port, use_ssl: use_ssl, read_timeout: timeout)
|
35
40
|
end
|
36
41
|
|
37
42
|
# Send a request to the backend. If data is a {Hash},
|
@@ -57,21 +62,6 @@ module Instana
|
|
57
62
|
Response.new(response)
|
58
63
|
end
|
59
64
|
|
60
|
-
# @return [Integer, NilClass] the fileno of the Net::HTTP socket or nil if it can't be identified
|
61
|
-
def fileno
|
62
|
-
socket = @client.instance_variable_get('@socket')
|
63
|
-
io = socket && socket.instance_variable_get('@io')
|
64
|
-
io && io.fileno
|
65
|
-
end
|
66
|
-
|
67
|
-
# @return [String] the inode asscoated with the Net::HTTP socket or nil if it can't be identified
|
68
|
-
def inode
|
69
|
-
path = "/proc/self/fd/#{fileno}"
|
70
|
-
return unless File.exist?(path) && fileno
|
71
|
-
|
72
|
-
File.readlink(path)
|
73
|
-
end
|
74
|
-
|
75
65
|
private
|
76
66
|
|
77
67
|
def encode_body(data)
|
@@ -39,12 +39,10 @@ module Instana
|
|
39
39
|
|
40
40
|
# @return [Hash, NilClass] the backend friendly description of the current in process collector
|
41
41
|
def source
|
42
|
-
return @source if @source
|
43
|
-
|
44
42
|
snapshot = @snapshots.detect { |s| s.respond_to?(:source) }
|
45
43
|
|
46
44
|
if snapshot
|
47
|
-
|
45
|
+
snapshot.source
|
48
46
|
else
|
49
47
|
@logger.warn('Unable to find a snapshot which provides a source.')
|
50
48
|
{}
|
@@ -58,21 +56,10 @@ module Instana
|
|
58
56
|
|
59
57
|
# @return [Hash] values which are removed from urls sent to the backend
|
60
58
|
def secret_values
|
61
|
-
# TODO: Parse from env
|
62
59
|
matcher, *keys = @secrets.split(/[:,]/)
|
63
60
|
{'matcher' => matcher, 'list' => keys}
|
64
61
|
end
|
65
62
|
|
66
|
-
private
|
67
|
-
|
68
|
-
def request_headers
|
69
|
-
{
|
70
|
-
'X-Instana-Host' => host_name,
|
71
|
-
'X-Instana-Key' => ENV['INSTANA_AGENT_KEY'],
|
72
|
-
'X-Instana-Time' => (Time.now.to_i * 1000).to_s
|
73
|
-
}
|
74
|
-
end
|
75
|
-
|
76
63
|
def send_bundle
|
77
64
|
spans = @processor.queued_spans
|
78
65
|
bundle = {
|
@@ -90,9 +77,19 @@ module Instana
|
|
90
77
|
@logger.warn("Recived a `#{response.code}` when sending data.")
|
91
78
|
end
|
92
79
|
|
80
|
+
private
|
81
|
+
|
82
|
+
def request_headers
|
83
|
+
{
|
84
|
+
'X-Instana-Host' => host_name,
|
85
|
+
'X-Instana-Key' => ENV['INSTANA_AGENT_KEY'],
|
86
|
+
'X-Instana-Time' => (Time.now.to_i * 1000).to_s
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
93
90
|
def agent_snapshots
|
94
91
|
@snapshots.map do |snapshot|
|
95
|
-
begin
|
92
|
+
begin # rubocop:disable Style/RedundantBegin, Lint/RedundantCopDisableDirective
|
96
93
|
snapshot.snapshot
|
97
94
|
rescue StandardError => e
|
98
95
|
@logger.error(e.message)
|
@@ -102,12 +99,10 @@ module Instana
|
|
102
99
|
end
|
103
100
|
|
104
101
|
def host_name
|
105
|
-
return @host_name if @host_name
|
106
|
-
|
107
102
|
snapshot = @snapshots.detect { |s| s.respond_to?(:host_name) }
|
108
103
|
|
109
104
|
if snapshot
|
110
|
-
|
105
|
+
snapshot.host_name
|
111
106
|
else
|
112
107
|
@logger.warn('Unable to find a snapshot which provides a host_name.')
|
113
108
|
''
|
data/lib/instana/base.rb
CHANGED
@@ -13,6 +13,7 @@ module Instana
|
|
13
13
|
attr_accessor :config
|
14
14
|
attr_accessor :pid
|
15
15
|
attr_reader :secrets
|
16
|
+
attr_reader :serverless
|
16
17
|
|
17
18
|
##
|
18
19
|
# setup
|
@@ -25,6 +26,7 @@ module Instana
|
|
25
26
|
@tracer = ::Instana::Tracer.new
|
26
27
|
@processor = ::Instana::Processor.new
|
27
28
|
@secrets = ::Instana::Secrets.new
|
29
|
+
@serverless = ::Instana::Serverless.new
|
28
30
|
end
|
29
31
|
|
30
32
|
def logger
|
data/lib/instana/config.rb
CHANGED
@@ -51,7 +51,7 @@ module Instana
|
|
51
51
|
@config[:sanitize_sql] = true
|
52
52
|
|
53
53
|
# W3 Trace Context Support
|
54
|
-
@config[:w3_trace_correlation] = ENV
|
54
|
+
@config[:w3_trace_correlation] = ENV['INSTANA_DISABLE_W3C_TRACE_CORRELATION'].nil?
|
55
55
|
|
56
56
|
@config[:action_controller] = { :enabled => true }
|
57
57
|
@config[:action_view] = { :enabled => true }
|
@@ -24,14 +24,16 @@ module Instana
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# Set request headers; encode IDs as hexadecimal strings
|
27
|
-
datum[:headers]['X-Instana-
|
28
|
-
datum[:headers]['X-Instana-S'] = t_context.span_id_header
|
27
|
+
datum[:headers]['X-Instana-L'] = t_context.level.to_s
|
29
28
|
|
30
|
-
if
|
31
|
-
datum[:headers]['
|
32
|
-
datum[:headers]['
|
29
|
+
if t_context.active?
|
30
|
+
datum[:headers]['X-Instana-T'] = t_context.trace_id_header
|
31
|
+
datum[:headers]['X-Instana-S'] = t_context.span_id_header
|
33
32
|
end
|
34
33
|
|
34
|
+
datum[:headers]['Traceparent'] = t_context.trace_parent_header
|
35
|
+
datum[:headers]['Tracestate'] = t_context.trace_state_header unless t_context.trace_state_header.empty?
|
36
|
+
|
35
37
|
@stack.request_call(datum)
|
36
38
|
end
|
37
39
|
|
@@ -18,16 +18,33 @@ module Instana
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def incoming_context
|
21
|
-
context = if
|
21
|
+
context = if !correlation_data.empty?
|
22
|
+
{}
|
23
|
+
elsif @env['HTTP_X_INSTANA_T']
|
22
24
|
context_from_instana_headers
|
23
|
-
elsif @env['HTTP_TRACEPARENT']
|
25
|
+
elsif @env['HTTP_TRACEPARENT']
|
24
26
|
context_from_trace_parent
|
27
|
+
elsif @env['HTTP_TRACESTATE']
|
28
|
+
context_from_trace_state
|
25
29
|
else
|
26
30
|
{}
|
27
31
|
end
|
28
32
|
|
29
33
|
context[:level] = @env['HTTP_X_INSTANA_L'][0] if @env['HTTP_X_INSTANA_L']
|
30
34
|
|
35
|
+
unless ::Instana.config[:w3_trace_correlation]
|
36
|
+
trace_state = parse_trace_state
|
37
|
+
|
38
|
+
if context[:from_w3] && trace_state.empty?
|
39
|
+
context.delete(:span_id)
|
40
|
+
context[:from_w3] = false
|
41
|
+
elsif context[:from_w3] && !trace_state.empty?
|
42
|
+
context[:trace_id] = trace_state[:t]
|
43
|
+
context[:span_id] = trace_state[:p]
|
44
|
+
context[:from_w3] = false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
31
48
|
context
|
32
49
|
end
|
33
50
|
|
@@ -69,19 +86,43 @@ module Instana
|
|
69
86
|
end
|
70
87
|
|
71
88
|
def continuing_from_trace_parent?
|
72
|
-
incoming_context
|
89
|
+
incoming_context[:from_w3]
|
73
90
|
end
|
74
91
|
|
75
92
|
def synthetic?
|
76
93
|
@env.has_key?('HTTP_X_INSTANA_SYNTHETIC') && @env['HTTP_X_INSTANA_SYNTHETIC'].eql?('1')
|
77
94
|
end
|
78
95
|
|
96
|
+
def long_instana_id?
|
97
|
+
::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_T']).length == 32
|
98
|
+
end
|
99
|
+
|
100
|
+
def external_trace_id?
|
101
|
+
continuing_from_trace_parent? || long_instana_id?
|
102
|
+
end
|
103
|
+
|
104
|
+
def external_trace_id
|
105
|
+
incoming_context[:long_instana_id] || incoming_context[:external_trace_id]
|
106
|
+
end
|
107
|
+
|
79
108
|
private
|
80
109
|
|
81
110
|
def context_from_instana_headers
|
111
|
+
sanitized_t = ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_T'])
|
112
|
+
sanitized_s = ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_S'])
|
113
|
+
external_trace_id = if @env['HTTP_TRACEPARENT']
|
114
|
+
context_from_trace_parent[:external_trace_id]
|
115
|
+
elsif long_instana_id?
|
116
|
+
sanitized_t
|
117
|
+
end
|
118
|
+
|
82
119
|
{
|
83
|
-
|
84
|
-
|
120
|
+
span_id: sanitized_s,
|
121
|
+
trace_id: long_instana_id? ? sanitized_t[16..-1] : sanitized_t, # rubocop:disable Style/SlicingWithRange, Lint/RedundantCopDisableDirective
|
122
|
+
long_instana_id: long_instana_id? ? sanitized_t : nil,
|
123
|
+
external_trace_id: external_trace_id,
|
124
|
+
external_state: @env['HTTP_TRACESTATE'],
|
125
|
+
from_w3: false
|
85
126
|
}.compact
|
86
127
|
end
|
87
128
|
|
@@ -90,14 +131,28 @@ module Instana
|
|
90
131
|
matches = @env['HTTP_TRACEPARENT'].match(W3_TRACE_PARENT_FORMAT)
|
91
132
|
return {} unless matches
|
92
133
|
|
134
|
+
trace_id = ::Instana::Util.header_to_id(matches['trace'][16..-1]) # rubocop:disable Style/SlicingWithRange, Lint/RedundantCopDisableDirective
|
135
|
+
span_id = ::Instana::Util.header_to_id(matches['parent'])
|
136
|
+
|
93
137
|
{
|
94
138
|
external_trace_id: matches['trace'],
|
95
139
|
external_state: @env['HTTP_TRACESTATE'],
|
96
|
-
trace_id:
|
97
|
-
span_id:
|
140
|
+
trace_id: trace_id,
|
141
|
+
span_id: span_id,
|
142
|
+
from_w3: true
|
98
143
|
}
|
99
144
|
end
|
100
145
|
|
146
|
+
def context_from_trace_state
|
147
|
+
state = parse_trace_state
|
148
|
+
|
149
|
+
{
|
150
|
+
trace_id: state[:t],
|
151
|
+
span_id: state[:p],
|
152
|
+
from_w3: false
|
153
|
+
}.compact
|
154
|
+
end
|
155
|
+
|
101
156
|
def parse_trace_state
|
102
157
|
return {} unless @env.has_key?('HTTP_TRACESTATE')
|
103
158
|
token = @env['HTTP_TRACESTATE']
|
@@ -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-
|
23
|
-
request['X-Instana-S'] = t_context.span_id_header
|
22
|
+
request['X-Instana-L'] = t_context.level.to_s
|
24
23
|
|
25
|
-
if
|
26
|
-
request['
|
27
|
-
request['
|
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
|