instana 2.9.0 → 2.11.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/README.md +173 -0
- data/lib/instana/backend/host_agent_reporting_observer.rb +26 -14
- data/lib/instana/base.rb +5 -0
- data/lib/instana/config.rb +66 -2
- data/lib/instana/exporter/otlp/aws_converter.rb +14 -3
- data/lib/instana/exporter/otlp/background_job_converter.rb +12 -1
- data/lib/instana/exporter/otlp/base_converter.rb +14 -2
- data/lib/instana/exporter/otlp/database_converter.rb +24 -4
- data/lib/instana/exporter/otlp/http_converter.rb +3 -2
- data/lib/instana/exporter/otlp/messaging_converter.rb +67 -21
- data/lib/instana/exporter/otlp/rpc_converter.rb +64 -4
- data/lib/instana/instrumentation/active_job.rb +34 -19
- data/lib/instana/instrumentation/aws_sdk_lambda.rb +16 -7
- data/lib/instana/instrumentation/aws_sdk_sns.rb +31 -1
- data/lib/instana/instrumentation/aws_sdk_sqs.rb +10 -5
- data/lib/instana/instrumentation/bunny.rb +23 -6
- data/lib/instana/instrumentation/grpc.rb +39 -11
- data/lib/instana/instrumentation/instrumented_request.rb +21 -11
- data/lib/instana/instrumentation/rack.rb +2 -0
- data/lib/instana/instrumentation/resque.rb +2 -1
- data/lib/instana/instrumentation/shoryuken.rb +33 -12
- data/lib/instana/instrumentation/sidekiq-client.rb +1 -0
- data/lib/instana/instrumentation/sidekiq-worker.rb +4 -2
- data/lib/instana/trace/span_context.rb +1 -1
- data/lib/instana/util.rb +68 -1
- data/lib/instana/version.rb +1 -1
- metadata +2 -2
|
@@ -11,15 +11,36 @@ module Instana
|
|
|
11
11
|
module Otlp
|
|
12
12
|
# Converter for messaging spans to OTLP format
|
|
13
13
|
class MessagingConverter < BaseConverter
|
|
14
|
-
# Build OTel-compliant span name for messaging
|
|
14
|
+
# Build OTel-compliant span name for messaging spans
|
|
15
15
|
#
|
|
16
|
-
# Formula per SPAN_NAME_PATTERNS.txt Section 3 (bunny/AMQP):
|
|
17
|
-
# publish → "{exchange} publish" (or "{queue} publish" when no exchange)
|
|
18
|
-
# receive → "{queue} receive"
|
|
16
|
+
# Formula per SPAN_NAME_PATTERNS.txt Section 3 (bunny/AMQP / Kafka):
|
|
17
|
+
# RabbitMQ publish → "{exchange} publish" (or "{queue} publish" when no exchange)
|
|
18
|
+
# RabbitMQ receive → "{queue} receive"
|
|
19
|
+
# Kafka → "{service} {access}" (e.g., "orders send")
|
|
19
20
|
#
|
|
20
21
|
# @return [String] The span name
|
|
21
22
|
def span_name
|
|
22
|
-
rabbitmq_data = span[:data]&.[](:rabbitmq)
|
|
23
|
+
if (rabbitmq_data = span[:data]&.[](:rabbitmq))
|
|
24
|
+
rabbitmq_span_name(rabbitmq_data)
|
|
25
|
+
elsif (kafka_data = span[:data]&.[](:kafka))
|
|
26
|
+
kafka_span_name(kafka_data)
|
|
27
|
+
else
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def convert_attributes
|
|
33
|
+
attributes = {}
|
|
34
|
+
|
|
35
|
+
convert_rabbitmq_attributes(attributes, span[:data]&.[](:rabbitmq))
|
|
36
|
+
convert_kafka_attributes(attributes, span[:data]&.[](:kafka))
|
|
37
|
+
|
|
38
|
+
attributes
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def rabbitmq_span_name(rabbitmq_data)
|
|
23
44
|
sort = rabbitmq_data[:sort].to_s
|
|
24
45
|
|
|
25
46
|
if sort == 'publish'
|
|
@@ -32,26 +53,51 @@ module Instana
|
|
|
32
53
|
end
|
|
33
54
|
end
|
|
34
55
|
|
|
35
|
-
def
|
|
36
|
-
|
|
56
|
+
def kafka_span_name(kafka_data)
|
|
57
|
+
service = kafka_data[:service].to_s.strip
|
|
58
|
+
access = kafka_data[:access].to_s.strip
|
|
59
|
+
parts = [service, access].reject(&:empty?)
|
|
60
|
+
parts.empty? ? 'kafka' : parts.join(' ')
|
|
61
|
+
end
|
|
37
62
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_SYSTEM, 'rabbitmq')
|
|
41
|
-
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_DESTINATION_NAME,
|
|
42
|
-
rabbitmq_destination_name(rabbitmq_data))
|
|
43
|
-
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY, rabbitmq_data[:key])
|
|
44
|
-
add_attribute(attributes, 'messaging.rabbitmq.queue', rabbitmq_data[:queue])
|
|
45
|
-
add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, rabbitmq_data[:address])
|
|
46
|
-
|
|
47
|
-
operation = rabbitmq_data[:sort] == 'publish' ? 'send' : 'receive'
|
|
48
|
-
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION_TYPE, operation)
|
|
49
|
-
end
|
|
63
|
+
def convert_rabbitmq_attributes(attributes, rabbitmq_data)
|
|
64
|
+
return unless rabbitmq_data
|
|
50
65
|
|
|
51
|
-
attributes
|
|
66
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_SYSTEM, 'rabbitmq')
|
|
67
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_DESTINATION_NAME,
|
|
68
|
+
rabbitmq_destination_name(rabbitmq_data))
|
|
69
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY, rabbitmq_data[:key])
|
|
70
|
+
add_attribute(attributes, 'messaging.rabbitmq.queue', rabbitmq_data[:queue])
|
|
71
|
+
add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, extract_host(rabbitmq_data[:address]))
|
|
72
|
+
add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_PORT, extract_port(rabbitmq_data[:address]))
|
|
73
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_MESSAGE_BODY_SIZE, rabbitmq_data[:size])
|
|
74
|
+
|
|
75
|
+
operation = rabbitmq_data[:sort] == 'publish' ? 'send' : 'receive'
|
|
76
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION_TYPE, operation)
|
|
77
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION_NAME, rabbitmq_data[:sort])
|
|
52
78
|
end
|
|
53
79
|
|
|
54
|
-
|
|
80
|
+
def convert_kafka_attributes(attributes, kafka_data)
|
|
81
|
+
return unless kafka_data
|
|
82
|
+
|
|
83
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_SYSTEM, 'kafka')
|
|
84
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_DESTINATION_NAME, kafka_data[:service])
|
|
85
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION_NAME, kafka_data[:access])
|
|
86
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION_TYPE, kafka_data[:access])
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def extract_host(address)
|
|
90
|
+
return nil unless address
|
|
91
|
+
|
|
92
|
+
address.to_s.split(':').first
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def extract_port(address)
|
|
96
|
+
return nil unless address
|
|
97
|
+
|
|
98
|
+
port = address.to_s.split(':').last
|
|
99
|
+
port.to_i if port =~ /^\d+$/
|
|
100
|
+
end
|
|
55
101
|
|
|
56
102
|
# Build the composite destination name per spec:
|
|
57
103
|
# Producer (publish): "{exchange}:{key}" — omit absent parts
|
|
@@ -6,6 +6,7 @@ require_relative 'base_converter'
|
|
|
6
6
|
require 'opentelemetry/semconv/incubating/rpc'
|
|
7
7
|
require 'opentelemetry/semconv/incubating/code'
|
|
8
8
|
require 'opentelemetry/semconv/server'
|
|
9
|
+
require 'opentelemetry/semconv/network'
|
|
9
10
|
|
|
10
11
|
module Instana
|
|
11
12
|
module Exporter
|
|
@@ -48,10 +49,13 @@ module Instana
|
|
|
48
49
|
|
|
49
50
|
private
|
|
50
51
|
|
|
52
|
+
# gRPC status code mapping: 0 = OK for success, otherwise derive from error
|
|
53
|
+
GRPC_STATUS_CODE_OK = 0
|
|
54
|
+
|
|
51
55
|
# Convert gRPC span attributes
|
|
52
56
|
def convert_grpc_attributes(attributes, rpc_data)
|
|
53
57
|
# RPC system
|
|
54
|
-
add_attribute(attributes,
|
|
58
|
+
add_attribute(attributes, Instana::Exporter::Otlp::RPC_SYSTEM_ATTR, 'grpc')
|
|
55
59
|
|
|
56
60
|
# RPC service and method
|
|
57
61
|
if rpc_data[:call]
|
|
@@ -60,9 +64,20 @@ module Instana
|
|
|
60
64
|
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::RPC::RPC_METHOD, method)
|
|
61
65
|
end
|
|
62
66
|
|
|
63
|
-
#
|
|
67
|
+
# Server host and port
|
|
64
68
|
add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, rpc_data[:host])
|
|
65
|
-
add_attribute(attributes, OpenTelemetry::SemConv::SERVER::
|
|
69
|
+
add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_PORT, parse_port(rpc_data[:port]))
|
|
70
|
+
|
|
71
|
+
# Network peer address (peer.address → network.peer.address)
|
|
72
|
+
peer = rpc_data[:peer]
|
|
73
|
+
if peer
|
|
74
|
+
add_attribute(attributes, OpenTelemetry::SemConv::NETWORK::NETWORK_PEER_ADDRESS, peer[:address])
|
|
75
|
+
add_attribute(attributes, OpenTelemetry::SemConv::NETWORK::NETWORK_PEER_PORT, parse_port(peer[:port]))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# gRPC status code (integer): 0 = OK for success spans
|
|
79
|
+
grpc_status = grpc_status_code(rpc_data)
|
|
80
|
+
add_attribute(attributes, OpenTelemetry::SemConv::Incubating::RPC::RPC_GRPC_STATUS_CODE, grpc_status)
|
|
66
81
|
|
|
67
82
|
# gRPC-specific attributes
|
|
68
83
|
add_attribute(attributes, 'rpc.grpc.call_type', rpc_data[:call_type])
|
|
@@ -71,7 +86,7 @@ module Instana
|
|
|
71
86
|
# Convert ActionCable span attributes
|
|
72
87
|
def convert_action_cable_attributes(attributes, rpc_data)
|
|
73
88
|
# RPC system
|
|
74
|
-
add_attribute(attributes,
|
|
89
|
+
add_attribute(attributes, Instana::Exporter::Otlp::RPC_SYSTEM_ATTR, 'actioncable')
|
|
75
90
|
|
|
76
91
|
# ActionCable-specific attributes
|
|
77
92
|
add_attribute(attributes, 'rails.actioncable.channel', rpc_data[:call])
|
|
@@ -96,6 +111,51 @@ module Instana
|
|
|
96
111
|
|
|
97
112
|
[parts[1], parts[2]]
|
|
98
113
|
end
|
|
114
|
+
|
|
115
|
+
# Parse port value to integer, returns nil if invalid or out of range
|
|
116
|
+
def parse_port(port)
|
|
117
|
+
return nil unless port
|
|
118
|
+
|
|
119
|
+
int_port = port.to_i
|
|
120
|
+
int_port if int_port >= 0 && int_port <= 65_535
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Map to gRPC status code integer.
|
|
124
|
+
# For successful spans (ec = 0) → 0 (OK).
|
|
125
|
+
# For error spans with a recognized gRPC status string in the error → map it.
|
|
126
|
+
# Otherwise → nil (omit the attribute).
|
|
127
|
+
GRPC_STATUS_CODES = {
|
|
128
|
+
'OK' => 0,
|
|
129
|
+
'CANCELLED' => 1,
|
|
130
|
+
'UNKNOWN' => 2,
|
|
131
|
+
'INVALID_ARGUMENT' => 3,
|
|
132
|
+
'DEADLINE_EXCEEDED' => 4,
|
|
133
|
+
'NOT_FOUND' => 5,
|
|
134
|
+
'ALREADY_EXISTS' => 6,
|
|
135
|
+
'PERMISSION_DENIED' => 7,
|
|
136
|
+
'RESOURCE_EXHAUSTED' => 8,
|
|
137
|
+
'FAILED_PRECONDITION' => 9,
|
|
138
|
+
'ABORTED' => 10,
|
|
139
|
+
'OUT_OF_RANGE' => 11,
|
|
140
|
+
'UNIMPLEMENTED' => 12,
|
|
141
|
+
'INTERNAL' => 13,
|
|
142
|
+
'UNAVAILABLE' => 14,
|
|
143
|
+
'DATA_LOSS' => 15,
|
|
144
|
+
'UNAUTHENTICATED' => 16
|
|
145
|
+
}.freeze
|
|
146
|
+
|
|
147
|
+
def grpc_status_code(rpc_data)
|
|
148
|
+
error_count = span[:ec].to_i
|
|
149
|
+
return GRPC_STATUS_CODE_OK if error_count.zero?
|
|
150
|
+
|
|
151
|
+
# Attempt to extract gRPC status string from error message
|
|
152
|
+
error_msg = rpc_data[:error].to_s
|
|
153
|
+
GRPC_STATUS_CODES.each do |name, code|
|
|
154
|
+
return code if error_msg.include?(name)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
GRPC_STATUS_CODES['UNKNOWN']
|
|
158
|
+
end
|
|
99
159
|
end
|
|
100
160
|
end
|
|
101
161
|
end
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
# (c) Copyright IBM Corp. 2021
|
|
2
2
|
# (c) Copyright Instana Inc. 2021
|
|
3
|
-
module OpenTelemetry
|
|
4
|
-
module Trace
|
|
5
|
-
module Propagation
|
|
6
|
-
module TraceContext
|
|
7
|
-
# A TraceParent is an implementation of the W3C trace context specification
|
|
8
|
-
# https://www.w3.org/TR/trace-context/
|
|
9
|
-
# {Trace::SpanContext}
|
|
10
|
-
class TraceParent
|
|
11
|
-
REGEXP = /^(?<version>[A-Fa-f0-9]{2})-(?<trace_id>[A-Fa-f0-9]{32})-(?<span_id>[A-Fa-f0-9]{32})-(?<flags>[A-Fa-f0-9]{2})(?<ignored>-.*)?$/
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
3
|
|
|
18
4
|
module Instana
|
|
19
5
|
module Instrumentation
|
|
@@ -48,11 +34,8 @@ module Instana
|
|
|
48
34
|
job_id: job.job_id
|
|
49
35
|
}
|
|
50
36
|
}
|
|
51
|
-
incoming_context =
|
|
52
|
-
|
|
53
|
-
job.arguments.pop
|
|
54
|
-
instana_context ? ::Instana::SpanContext.new(trace_id: instana_context[:trace_id], span_id: instana_context[:span_id]) : nil
|
|
55
|
-
end
|
|
37
|
+
incoming_context = ::Instana::Instrumentation::ActiveJob.extract_context(job)
|
|
38
|
+
|
|
56
39
|
Trace.with_span(OpenTelemetry::Trace.non_recording_span(incoming_context)) do
|
|
57
40
|
::Instana.tracer.in_span(:activejob, attributes: tags) do
|
|
58
41
|
block.call
|
|
@@ -60,6 +43,38 @@ module Instana
|
|
|
60
43
|
end
|
|
61
44
|
end
|
|
62
45
|
end
|
|
46
|
+
|
|
47
|
+
def self.extract_context(job)
|
|
48
|
+
# 1. From job arguments (Instana payload)
|
|
49
|
+
if job.arguments.is_a?(Array) && job.arguments.last.is_a?(Hash) && job.arguments.last.key?(:instana_context)
|
|
50
|
+
instana_context = job.arguments.last[:instana_context]
|
|
51
|
+
job.arguments.pop
|
|
52
|
+
return instana_context ? ::Instana::SpanContext.new(
|
|
53
|
+
trace_id: instana_context[:trace_id],
|
|
54
|
+
span_id: instana_context[:span_id],
|
|
55
|
+
level: instana_context[:level] || 1
|
|
56
|
+
) : nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# 2. From job.__otel_headers (if opentelemetry-instrumentation-active_job is active)
|
|
60
|
+
if job.respond_to?(:__otel_headers) && job.__otel_headers.is_a?(Hash)
|
|
61
|
+
headers = job.__otel_headers
|
|
62
|
+
tp = headers['traceparent'] || headers[:traceparent]
|
|
63
|
+
if tp
|
|
64
|
+
ctx = parse_w3c_traceparent(tp, headers['tracestate'] || headers[:tracestate])
|
|
65
|
+
return ctx if ctx
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.parse_w3c_traceparent(traceparent, tracestate)
|
|
73
|
+
ctx = ::Instana::Util.parse_w3c_trace_context(traceparent, tracestate)
|
|
74
|
+
return nil unless ctx
|
|
75
|
+
|
|
76
|
+
::Instana::SpanContext.new(trace_id: ctx[:trace_id], span_id: ctx[:span_id], level: ctx[:level])
|
|
77
|
+
end
|
|
63
78
|
end
|
|
64
79
|
end
|
|
65
80
|
end
|
|
@@ -9,15 +9,24 @@ module Instana
|
|
|
9
9
|
def call(context)
|
|
10
10
|
return @handler.call(context) unless [:invoke_async, :invoke].include?(context.operation_name)
|
|
11
11
|
|
|
12
|
-
if
|
|
12
|
+
if ::Instana.tracer.tracing? && context.operation_name == :invoke
|
|
13
13
|
span_context = ::Instana.tracer.context
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
if span_context
|
|
15
|
+
trace_payload = {
|
|
16
|
+
'X-INSTANA-T' => span_context.trace_id,
|
|
17
|
+
'X-INSTANA-S' => span_context.span_id,
|
|
18
|
+
'X-INSTANA-L' => span_context.level.to_s,
|
|
19
|
+
'traceparent' => span_context.trace_parent_header,
|
|
20
|
+
'tracestate' => span_context.trace_state_header
|
|
21
|
+
}.reject { |_, v| v.nil? || v.empty? }
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
merged = if context.params[:client_context]
|
|
24
|
+
JSON.parse(Base64.strict_decode64(context.params[:client_context])).merge(trace_payload)
|
|
25
|
+
else
|
|
26
|
+
trace_payload
|
|
27
|
+
end
|
|
28
|
+
context.params[:client_context] = Base64.strict_encode64(JSON.dump(merged))
|
|
29
|
+
end
|
|
21
30
|
end
|
|
22
31
|
|
|
23
32
|
tags = {
|
|
@@ -14,11 +14,41 @@ module Instana
|
|
|
14
14
|
}.reject { |_, v| v.nil? }
|
|
15
15
|
|
|
16
16
|
if context.operation_name == :publish
|
|
17
|
-
::Instana.tracer.in_span(:sns, attributes: {sns: sns_tags})
|
|
17
|
+
::Instana.tracer.in_span(:sns, attributes: { sns: sns_tags }) do |_span|
|
|
18
|
+
if ::Instana.tracer.tracing?
|
|
19
|
+
inject_trace_attributes(context.params)
|
|
20
|
+
end
|
|
21
|
+
@handler.call(context)
|
|
22
|
+
end
|
|
23
|
+
elsif context.operation_name == :publish_batch
|
|
24
|
+
::Instana.tracer.in_span(:sns, attributes: { sns: sns_tags }) do |_span|
|
|
25
|
+
if ::Instana.tracer.tracing?
|
|
26
|
+
context.params[:publish_batch_request_entries]&.each do |entry|
|
|
27
|
+
inject_trace_attributes(entry)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
@handler.call(context)
|
|
31
|
+
end
|
|
18
32
|
else
|
|
19
33
|
@handler.call(context)
|
|
20
34
|
end
|
|
21
35
|
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def inject_trace_attributes(params)
|
|
40
|
+
span_context = ::Instana.tracer.context
|
|
41
|
+
return unless span_context
|
|
42
|
+
|
|
43
|
+
params[:message_attributes] ||= {}
|
|
44
|
+
available_slots = 10 - params[:message_attributes].size
|
|
45
|
+
|
|
46
|
+
extra = ::Instana::Util.aws_message_attributes_w3c(span_context)
|
|
47
|
+
extra.merge!(::Instana::Util.aws_message_attributes_instana(span_context)) if available_slots >= 5
|
|
48
|
+
|
|
49
|
+
extra.reject! { |_, v| v[:string_value].nil? || v[:string_value].empty? }
|
|
50
|
+
params[:message_attributes].merge!(extra)
|
|
51
|
+
end
|
|
22
52
|
end
|
|
23
53
|
|
|
24
54
|
def add_handlers(handlers, _config)
|
|
@@ -36,11 +36,16 @@ module Instana
|
|
|
36
36
|
|
|
37
37
|
def inject_instana_headers(span, params)
|
|
38
38
|
params[:message_attributes] ||= {}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
span_context = span.context
|
|
40
|
+
return unless span_context
|
|
41
|
+
|
|
42
|
+
available_slots = 10 - params[:message_attributes].size
|
|
43
|
+
|
|
44
|
+
extra = ::Instana::Util.aws_message_attributes_w3c(span_context)
|
|
45
|
+
extra.merge!(::Instana::Util.aws_message_attributes_instana(span_context)) if available_slots >= 5
|
|
46
|
+
|
|
47
|
+
extra.reject! { |_, v| v[:string_value].nil? || v[:string_value].empty? }
|
|
48
|
+
params[:message_attributes].merge!(extra)
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
def tags_for(operation_name, params)
|
|
@@ -20,9 +20,11 @@ module Instana
|
|
|
20
20
|
::Instana.tracer.in_span(:rabbitmq, attributes: kvs) do |span|
|
|
21
21
|
# Inject trace context into message headers
|
|
22
22
|
options[:headers] ||= {}
|
|
23
|
-
options[:headers][
|
|
24
|
-
options[:headers][
|
|
25
|
-
options[:headers][
|
|
23
|
+
options[:headers][INSTANA_TRACE_ID_HEADER] = span.context.trace_id
|
|
24
|
+
options[:headers][INSTANA_SPAN_ID_HEADER] = span.context.span_id
|
|
25
|
+
options[:headers][INSTANA_LEVEL_HEADER] = span.context.level.to_s
|
|
26
|
+
options[:headers]['traceparent'] = span.context.trace_parent_header
|
|
27
|
+
options[:headers]['tracestate'] = span.context.trace_state_header
|
|
26
28
|
|
|
27
29
|
super(payload, options)
|
|
28
30
|
end
|
|
@@ -113,13 +115,28 @@ module Instana
|
|
|
113
115
|
return {} unless properties && properties.headers
|
|
114
116
|
|
|
115
117
|
headers = properties.headers
|
|
118
|
+
|
|
119
|
+
if headers[INSTANA_TRACE_ID_HEADER]
|
|
120
|
+
extract_instana_context(headers)
|
|
121
|
+
elsif headers['traceparent']
|
|
122
|
+
extract_w3c_context(headers)
|
|
123
|
+
else
|
|
124
|
+
{}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def extract_instana_context(headers)
|
|
116
129
|
{
|
|
117
|
-
trace_id: headers[
|
|
118
|
-
span_id: headers[
|
|
119
|
-
level: headers[
|
|
130
|
+
trace_id: headers[INSTANA_TRACE_ID_HEADER],
|
|
131
|
+
span_id: headers[INSTANA_SPAN_ID_HEADER],
|
|
132
|
+
level: headers[INSTANA_LEVEL_HEADER]&.to_i || 1
|
|
120
133
|
}.reject { |_, v| v.nil? }
|
|
121
134
|
end
|
|
122
135
|
|
|
136
|
+
def extract_w3c_context(headers)
|
|
137
|
+
::Instana::Util.parse_w3c_trace_context(headers['traceparent'], headers['tracestate']) || {}
|
|
138
|
+
end
|
|
139
|
+
|
|
123
140
|
def log_error(error)
|
|
124
141
|
# Log errors on to console if INSTANA_DEBUG is enabled
|
|
125
142
|
::Instana.logger.debug { "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{error.message}" }
|
|
@@ -8,8 +8,8 @@ module Instana
|
|
|
8
8
|
module GRPCCientInstrumentation
|
|
9
9
|
CALL_TYPES = [:request_response, :client_streamer, :server_streamer, :bidi_streamer]
|
|
10
10
|
|
|
11
|
-
CALL_TYPES.each do |call_type|
|
|
12
|
-
define_method(call_type) do |method, *others, **options|
|
|
11
|
+
CALL_TYPES.each do |call_type| # rubocop:disable Metrics/BlockLength
|
|
12
|
+
define_method(call_type) do |method, *others, **options| # rubocop:disable Metrics/BlockLength
|
|
13
13
|
begin
|
|
14
14
|
kvs = { rpc: {} }
|
|
15
15
|
|
|
@@ -26,10 +26,15 @@ module Instana
|
|
|
26
26
|
|
|
27
27
|
context = ::Instana.tracer.context
|
|
28
28
|
if context
|
|
29
|
-
|
|
29
|
+
extra = {
|
|
30
30
|
'x-instana-t' => context.trace_id_header,
|
|
31
|
-
'x-instana-s' => context.span_id_header
|
|
32
|
-
|
|
31
|
+
'x-instana-s' => context.span_id_header,
|
|
32
|
+
'x-instana-l' => context.level.to_s,
|
|
33
|
+
'traceparent' => context.trace_parent_header,
|
|
34
|
+
'tracestate' => context.trace_state_header
|
|
35
|
+
}
|
|
36
|
+
extra.reject! { |_, v| v.nil? || v.empty? }
|
|
37
|
+
options[:metadata] = (options[:metadata] || {}).merge(extra)
|
|
33
38
|
end
|
|
34
39
|
|
|
35
40
|
super(method, *others, **options)
|
|
@@ -58,12 +63,7 @@ module Instana
|
|
|
58
63
|
kvs = { rpc: {} }
|
|
59
64
|
metadata = active_call.metadata
|
|
60
65
|
|
|
61
|
-
incoming_context =
|
|
62
|
-
if metadata.key?('x-instana-t')
|
|
63
|
-
incoming_context = SpanContext.new(trace_id: ::Instana::Util.header_to_id(metadata['x-instana-t']),
|
|
64
|
-
span_id: metadata.key?('x-instana-s') ? ::Instana::Util.header_to_id(metadata['x-instana-s']) : nil,
|
|
65
|
-
level: metadata.key?('x-instana-l') ? metadata['x-instana-l'] : nil)
|
|
66
|
-
end
|
|
66
|
+
incoming_context = extract_grpc_context(metadata)
|
|
67
67
|
|
|
68
68
|
kvs[:rpc][:flavor] = :grpc
|
|
69
69
|
kvs[:rpc][:host] = Socket.gethostname
|
|
@@ -87,6 +87,34 @@ module Instana
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def extract_grpc_context(metadata)
|
|
94
|
+
if metadata.key?('x-instana-t')
|
|
95
|
+
extract_instana_grpc_context(metadata)
|
|
96
|
+
elsif metadata.key?('traceparent')
|
|
97
|
+
extract_w3c_grpc_context(metadata)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def extract_instana_grpc_context(metadata)
|
|
102
|
+
SpanContext.new(
|
|
103
|
+
trace_id: ::Instana::Util.header_to_id(metadata['x-instana-t']),
|
|
104
|
+
span_id: metadata.key?('x-instana-s') ? ::Instana::Util.header_to_id(metadata['x-instana-s']) : nil,
|
|
105
|
+
level: metadata['x-instana-l'] || 1
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def extract_w3c_grpc_context(metadata)
|
|
110
|
+
ctx = ::Instana::Util.parse_w3c_trace_context(metadata['traceparent'], metadata['tracestate'])
|
|
111
|
+
return nil unless ctx
|
|
112
|
+
|
|
113
|
+
SpanContext.new(trace_id: ctx[:trace_id],
|
|
114
|
+
span_id: ctx[:span_id],
|
|
115
|
+
level: ctx[:level],
|
|
116
|
+
baggage: ctx.slice(:external_trace_id, :external_trace_flags, :external_state))
|
|
117
|
+
end
|
|
90
118
|
end
|
|
91
119
|
end
|
|
92
120
|
end
|
|
@@ -32,19 +32,14 @@ module Instana
|
|
|
32
32
|
|
|
33
33
|
context[:level] = @env['HTTP_X_INSTANA_L'][0] if @env['HTTP_X_INSTANA_L']
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
context.delete(:span_id)
|
|
40
|
-
context[:from_w3c] = false
|
|
41
|
-
elsif context[:from_w3c] && !trace_state.empty?
|
|
42
|
-
context[:trace_id] = trace_state[:t]
|
|
43
|
-
context[:span_id] = trace_state[:p]
|
|
44
|
-
context[:from_w3c] = false
|
|
45
|
-
end
|
|
35
|
+
# Honour W3C sampled bit when no X-Instana-L is present.
|
|
36
|
+
if context[:level].nil? && context[:external_trace_flags]
|
|
37
|
+
w3c_sampled = (context[:external_trace_flags].to_i(16) & 0x01) == 1
|
|
38
|
+
context[:level] = w3c_sampled ? '1' : '0'
|
|
46
39
|
end
|
|
47
40
|
|
|
41
|
+
apply_legacy_w3c_override(context) unless ::Instana.config[:w3c_trace_correlation]
|
|
42
|
+
|
|
48
43
|
context
|
|
49
44
|
end
|
|
50
45
|
|
|
@@ -107,6 +102,21 @@ module Instana
|
|
|
107
102
|
|
|
108
103
|
private
|
|
109
104
|
|
|
105
|
+
def apply_legacy_w3c_override(context)
|
|
106
|
+
return unless context[:from_w3c]
|
|
107
|
+
|
|
108
|
+
trace_state = parse_trace_state
|
|
109
|
+
|
|
110
|
+
if trace_state.empty?
|
|
111
|
+
context.delete(:span_id)
|
|
112
|
+
else
|
|
113
|
+
context[:trace_id] = trace_state[:t]
|
|
114
|
+
context[:span_id] = trace_state[:p]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context[:from_w3c] = false
|
|
118
|
+
end
|
|
119
|
+
|
|
110
120
|
def context_from_instana_headers
|
|
111
121
|
sanitized_t = ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_T'])
|
|
112
122
|
sanitized_s = ::Instana::Util.header_to_id(@env['HTTP_X_INSTANA_S'])
|
|
@@ -79,7 +79,8 @@ module Instana
|
|
|
79
79
|
context_from_wire = job.payload['args'].pop
|
|
80
80
|
::Instana::SpanContext.new(
|
|
81
81
|
trace_id: context_from_wire['trace_id'],
|
|
82
|
-
span_id: context_from_wire['span_id']
|
|
82
|
+
span_id: context_from_wire['span_id'],
|
|
83
|
+
level: context_from_wire['level'] || 1
|
|
83
84
|
)
|
|
84
85
|
end
|
|
85
86
|
span = OpenTelemetry::Trace.non_recording_span(trace_context) if trace_context
|
|
@@ -15,24 +15,45 @@ module Instana
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
context = incomming_context_from(sqs_message.message_attributes)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if context[:trace_id]
|
|
19
|
+
instana_context = Instana::SpanContext.new(
|
|
20
|
+
trace_id: context[:trace_id],
|
|
21
|
+
span_id: context[:span_id],
|
|
22
|
+
level: context[:level] || 1
|
|
23
|
+
)
|
|
24
|
+
Trace.with_span(OpenTelemetry::Trace.non_recording_span(instana_context)) do
|
|
25
|
+
::Instana.tracer.in_span(:sqs, attributes: { sqs: sqs_tags }, &block)
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
::Instana.tracer.in_span(:sqs, attributes: { sqs: sqs_tags }, &block)
|
|
21
29
|
end
|
|
22
30
|
end
|
|
23
31
|
|
|
24
32
|
private
|
|
25
33
|
|
|
26
34
|
def incomming_context_from(attributes)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
return {} unless attributes
|
|
36
|
+
|
|
37
|
+
if read_message_header(attributes, 'X_INSTANA_T')
|
|
38
|
+
trace_id = read_message_header(attributes, 'X_INSTANA_T')
|
|
39
|
+
span_id = read_message_header(attributes, 'X_INSTANA_S')
|
|
40
|
+
level = read_message_header(attributes, 'X_INSTANA_L')
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
trace_id: trace_id,
|
|
44
|
+
span_id: span_id,
|
|
45
|
+
level: level ? level.to_i : 1
|
|
46
|
+
}.reject { |_, v| v.nil? }
|
|
47
|
+
else
|
|
48
|
+
w3c_context_from(attributes)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def w3c_context_from(attributes)
|
|
53
|
+
::Instana::Util.parse_w3c_trace_context(
|
|
54
|
+
read_message_header(attributes, 'traceparent'),
|
|
55
|
+
read_message_header(attributes, 'tracestate')
|
|
56
|
+
) || {}
|
|
36
57
|
end
|
|
37
58
|
|
|
38
59
|
def read_message_header(attributes, key)
|
|
@@ -30,10 +30,12 @@ module Instana
|
|
|
30
30
|
trace_context = nil
|
|
31
31
|
if msg.key?('X-Instana-T')
|
|
32
32
|
trace_id = msg.delete('X-Instana-T')
|
|
33
|
-
span_id
|
|
33
|
+
span_id = msg.delete('X-Instana-S')
|
|
34
|
+
level = msg.delete('X-Instana-L')
|
|
34
35
|
trace_context = ::Instana::SpanContext.new(
|
|
35
36
|
trace_id: ::Instana::Util.header_to_id(trace_id),
|
|
36
|
-
span_id: span_id ? ::Instana::Util.header_to_id(span_id) : nil
|
|
37
|
+
span_id: span_id ? ::Instana::Util.header_to_id(span_id) : nil,
|
|
38
|
+
level: level || 1
|
|
37
39
|
)
|
|
38
40
|
end
|
|
39
41
|
|