instana 1.197.0.pre1 → 1.199.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|