solarwinds_apm 6.0.0.preV2 → 6.0.0.preV4
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/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +1 -1
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +1 -1
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +1 -1
- data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +1 -1
- data/ext/oboe_metal/src/VERSION +1 -2
- data/ext/oboe_metal/src/oboe.h +0 -12
- data/ext/oboe_metal/src/oboe_swig_wrap.cc +5 -5
- data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +1 -9
- data/lib/solarwinds_apm/api/current_trace_info.rb +0 -1
- data/lib/solarwinds_apm/api/opentelemetry.rb +39 -0
- data/lib/solarwinds_apm/api/transaction_name.rb +17 -17
- data/lib/solarwinds_apm/api.rb +2 -0
- data/lib/solarwinds_apm/config.rb +3 -19
- data/lib/solarwinds_apm/constants.rb +2 -4
- data/lib/solarwinds_apm/noop/context.rb +1 -1
- data/lib/solarwinds_apm/oboe_init_options.rb +2 -5
- data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +92 -87
- data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +18 -17
- data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +28 -28
- data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +9 -14
- data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +122 -170
- data/lib/solarwinds_apm/otel_config.rb +47 -28
- data/lib/solarwinds_apm/support/lumberjack_formatter.rb +17 -2
- data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +5 -4
- data/lib/solarwinds_apm/support/transaction_cache.rb +27 -2
- data/lib/solarwinds_apm/support/transaction_settings.rb +2 -2
- data/lib/solarwinds_apm/support/txn_name_manager.rb +34 -17
- data/lib/solarwinds_apm/support/utils.rb +24 -0
- data/lib/solarwinds_apm/support.rb +2 -4
- data/lib/solarwinds_apm/version.rb +1 -1
- data/lib/solarwinds_apm.rb +2 -2
- metadata +13 -13
- data/lib/oboe.rb +0 -7
- data/lib/solarwinds_apm/support/transformer.rb +0 -56
@@ -32,11 +32,9 @@ module SolarWindsAPM
|
|
32
32
|
return if parent_span && parent_span.context != ::OpenTelemetry::Trace::SpanContext::INVALID && parent_span.context.remote? == false
|
33
33
|
|
34
34
|
trace_flags = span.context.trace_flags.sampled? ? '01' : '00'
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] current baggage values: #{::OpenTelemetry::Baggage.values}"}
|
35
|
+
@txn_manager.set_root_context_h(span.context.hex_trace_id,"#{span.context.hex_span_id}-#{trace_flags}")
|
36
|
+
rescue StandardError => e
|
37
|
+
SolarWindsAPM.logger.info {"[#{self.class}/#{__method__}] processor on_start error: #{e.message}"}
|
40
38
|
end
|
41
39
|
|
42
40
|
# Called when a {Span} is ended, if the {Span#recording?}
|
@@ -47,7 +45,9 @@ module SolarWindsAPM
|
|
47
45
|
# Only calculate inbound metrics for service root spans
|
48
46
|
#
|
49
47
|
# @param [Span] span the {Span} that just ended.
|
50
|
-
def on_finish(span)
|
48
|
+
def on_finish(span)
|
49
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] processor on_finish span: #{span.inspect}"}
|
50
|
+
|
51
51
|
if span.parent_span_id != ::OpenTelemetry::Trace::INVALID_SPAN_ID
|
52
52
|
@exporter&.export([span.to_span_data]) if span.context.trace_flags.sampled?
|
53
53
|
return
|
@@ -57,12 +57,10 @@ module SolarWindsAPM
|
|
57
57
|
domain = nil
|
58
58
|
has_error = error?(span)
|
59
59
|
trans_name = calculate_transaction_names(span)
|
60
|
-
url_tran = span.attributes[HTTP_URL]
|
61
|
-
|
62
|
-
liboboe_txn_name = nil
|
63
60
|
if span_http?(span)
|
64
|
-
status_code
|
61
|
+
status_code = get_http_status_code(span)
|
65
62
|
request_method = span.attributes[HTTP_METHOD]
|
63
|
+
url_tran = span.attributes[HTTP_URL]
|
66
64
|
|
67
65
|
SolarWindsAPM.logger.debug do
|
68
66
|
"[#{self.class}/#{__method__}] createHttpSpan with\n
|
@@ -92,8 +90,11 @@ module SolarWindsAPM
|
|
92
90
|
|
93
91
|
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] liboboe_txn_name: #{liboboe_txn_name}"}
|
94
92
|
@txn_manager["#{span.context.hex_trace_id}-#{span.context.hex_span_id}"] = liboboe_txn_name if span.context.trace_flags.sampled?
|
95
|
-
|
93
|
+
@txn_manager.delete_root_context_h(span.context.hex_trace_id)
|
96
94
|
@exporter&.export([span.to_span_data]) if span.context.trace_flags.sampled?
|
95
|
+
rescue StandardError => e
|
96
|
+
SolarWindsAPM.logger.info {"[#{self.class}/#{__method__}] can't flush span to exporter; processor on_finish error: #{e.message}"}
|
97
|
+
::OpenTelemetry::SDK::Trace::Export::FAILURE
|
97
98
|
end
|
98
99
|
|
99
100
|
# Export all ended spans to the configured `Exporter` that have not yet
|
@@ -108,7 +109,7 @@ module SolarWindsAPM
|
|
108
109
|
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
|
109
110
|
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
|
110
111
|
def force_flush(timeout: nil)
|
111
|
-
@exporter&.force_flush(timeout: timeout) || ::OpenTelemetry::SDK::
|
112
|
+
@exporter&.force_flush(timeout: timeout) || ::OpenTelemetry::SDK::Trace::Export::SUCCESS
|
112
113
|
end
|
113
114
|
|
114
115
|
# Called when {TracerProvider#shutdown} is called.
|
@@ -117,14 +118,13 @@ module SolarWindsAPM
|
|
117
118
|
# @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
|
118
119
|
# a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
|
119
120
|
def shutdown(timeout: nil)
|
120
|
-
@exporter&.shutdown(timeout: timeout) || ::OpenTelemetry::SDK::
|
121
|
+
@exporter&.shutdown(timeout: timeout) || ::OpenTelemetry::SDK::Trace::Export::SUCCESS
|
121
122
|
end
|
122
123
|
|
123
124
|
private
|
124
125
|
|
125
126
|
# This span from inbound HTTP request if from a SERVER by some http.method
|
126
127
|
def span_http?(span)
|
127
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] span.kind #{span.kind} span.attributes: #{span.attributes[HTTP_METHOD]}"}
|
128
128
|
(span.kind == ::OpenTelemetry::Trace::SpanKind::SERVER && !span.attributes[HTTP_METHOD].nil?)
|
129
129
|
end
|
130
130
|
|
@@ -143,10 +143,11 @@ module SolarWindsAPM
|
|
143
143
|
|
144
144
|
# Get trans_name and url_tran of this span instance.
|
145
145
|
def calculate_transaction_names(span)
|
146
|
-
|
147
146
|
trace_span_id = "#{span.context.hex_trace_id}-#{span.context.hex_span_id}"
|
148
|
-
|
149
|
-
|
147
|
+
trans_name = @txn_manager.get(trace_span_id)
|
148
|
+
if trans_name
|
149
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] found trans name from txn_manager: #{trans_name} by #{trace_span_id}"}
|
150
|
+
@txn_manager.del(trace_span_id)
|
150
151
|
else
|
151
152
|
trans_name = span.attributes[HTTP_ROUTE] || nil
|
152
153
|
trans_name = span.name if span.name && (trans_name.nil? || trans_name.empty?)
|
@@ -2,6 +2,7 @@ module SolarWindsAPM
|
|
2
2
|
module OpenTelemetry
|
3
3
|
module SolarWindsPropagator
|
4
4
|
# TextMapPropagator
|
5
|
+
# propagator error will be rescued by OpenTelemetry::Context::Propagation::TextMapPropagator
|
5
6
|
class TextMapPropagator
|
6
7
|
TRACESTATE_HEADER_NAME = "tracestate".freeze
|
7
8
|
XTRACEOPTIONS_HEADER_NAME = "x-trace-options".freeze
|
@@ -26,19 +27,11 @@ module SolarWindsAPM
|
|
26
27
|
# if extraction fails
|
27
28
|
def extract(carrier, context: ::OpenTelemetry::Context.current, getter: ::OpenTelemetry::Context::Propagation.text_map_getter)
|
28
29
|
|
29
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] context
|
30
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] extract context: #{context.inspect}"}
|
30
31
|
|
31
|
-
context = ::OpenTelemetry::Context.new({})
|
32
|
-
|
33
|
-
|
34
|
-
context = context.set_value(INTL_SWO_X_OPTIONS_KEY, xtraceoptions_header) if xtraceoptions_header
|
35
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] xtraceoptions_header: #{xtraceoptions_header}"}
|
36
|
-
|
37
|
-
signature_header = getter.get(carrier, XTRACEOPTIONS_SIGNATURE_HEADER_NAME)
|
38
|
-
context = context.set_value(INTL_SWO_SIGNATURE_KEY, signature_header) if signature_header
|
39
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] signature_header: #{signature_header}; propagator extract context: #{context.inspect}"}
|
40
|
-
|
41
|
-
context
|
32
|
+
context = context.nil?? ::OpenTelemetry::Context.new({}) : context
|
33
|
+
context = inject_extracted_header(carrier, context, getter, XTRACEOPTIONS_HEADER_NAME, INTL_SWO_X_OPTIONS_KEY)
|
34
|
+
inject_extracted_header(carrier, context, getter, XTRACEOPTIONS_SIGNATURE_HEADER_NAME, INTL_SWO_SIGNATURE_KEY)
|
42
35
|
end
|
43
36
|
|
44
37
|
# Inject trace context into the supplied carrier.
|
@@ -51,32 +44,30 @@ module SolarWindsAPM
|
|
51
44
|
def inject(carrier, context: ::OpenTelemetry::Context.current, setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
|
52
45
|
|
53
46
|
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] inject context: #{context.inspect}"}
|
54
|
-
|
55
|
-
|
56
|
-
span_context
|
57
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] cspan #{cspan.inspect}; span_context #{span_context.inspect}"}
|
47
|
+
|
48
|
+
span_context = ::OpenTelemetry::Trace.current_span(context)&.context
|
49
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] span_context #{span_context.inspect}"}
|
58
50
|
return unless span_context&.valid?
|
59
51
|
|
60
|
-
|
52
|
+
trace_flag = span_context.trace_flags.sampled?? 1 : 0
|
53
|
+
sw_value = "#{span_context.hex_span_id}-0#{trace_flag}"
|
61
54
|
trace_state_header = carrier[TRACESTATE_HEADER_NAME].nil?? nil : carrier[TRACESTATE_HEADER_NAME]
|
62
|
-
|
63
55
|
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] sw_value: #{sw_value}; trace_state_header: #{trace_state_header}"}
|
64
56
|
|
65
|
-
#
|
66
|
-
trace_state = nil
|
57
|
+
# prepare carrier with carrier's or new tracestate
|
67
58
|
if trace_state_header.nil?
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
59
|
+
# only create new trace state if valid span_id
|
60
|
+
unless span_context.span_id == ::OpenTelemetry::Trace::INVALID_SPAN_ID
|
61
|
+
trace_state = ::OpenTelemetry::Trace::Tracestate.create({SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => sw_value})
|
62
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] creating new trace state: #{trace_state.inspect}"}
|
63
|
+
setter.set(carrier, TRACESTATE_HEADER_NAME, Utils.trace_state_header(trace_state))
|
64
|
+
end
|
73
65
|
else
|
74
66
|
trace_state_from_string = ::OpenTelemetry::Trace::Tracestate.from_string(trace_state_header)
|
75
67
|
trace_state = trace_state_from_string.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_value)
|
76
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}]
|
68
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] updating/adding trace state for injection #{trace_state.inspect}"}
|
69
|
+
setter.set(carrier, TRACESTATE_HEADER_NAME, Utils.trace_state_header(trace_state))
|
77
70
|
end
|
78
|
-
|
79
|
-
setter.set(carrier, TRACESTATE_HEADER_NAME, Transformer.trace_state_header(trace_state))
|
80
71
|
end
|
81
72
|
|
82
73
|
# Returns the predefined propagation fields. If your carrier is reused, you
|
@@ -86,6 +77,15 @@ module SolarWindsAPM
|
|
86
77
|
def fields
|
87
78
|
TRACESTATE_HEADER_NAME
|
88
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def inject_extracted_header(carrier, context, getter, header, inject_key)
|
84
|
+
extracted_header = getter.get(carrier, header)
|
85
|
+
context = context.set_value(inject_key, extracted_header) if extracted_header
|
86
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] #{header}: #{inject_key} = #{extracted_header}"}
|
87
|
+
context
|
88
|
+
end
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
@@ -2,6 +2,7 @@ module SolarWindsAPM
|
|
2
2
|
module OpenTelemetry
|
3
3
|
module SolarWindsResponsePropagator
|
4
4
|
# ResponsePropagator
|
5
|
+
# response propagator error will be rescued by OpenTelemetry::Instrumentation::Rack::Middlewares::EventHandler
|
5
6
|
class TextMapPropagator
|
6
7
|
HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers".freeze
|
7
8
|
XTRACE_HEADER_NAME = "x-trace".freeze
|
@@ -25,26 +26,20 @@ module SolarWindsAPM
|
|
25
26
|
# text map setter will be used.
|
26
27
|
def inject(carrier, context: ::OpenTelemetry::Context.current, setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
|
27
28
|
|
28
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] response propagator context: #{context.inspect}"}
|
29
29
|
span_context = ::OpenTelemetry::Trace.current_span(context).context
|
30
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}]
|
31
|
-
return unless span_context
|
30
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] context: #{context.inspect}; span_context: #{span_context.inspect}"}
|
31
|
+
return unless span_context&.valid?
|
32
32
|
|
33
|
-
x_trace
|
34
|
-
|
35
|
-
exposed_headers = [XTRACE_HEADER_NAME]
|
36
|
-
|
37
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] response propagator span_context.tracestate: #{span_context.tracestate.inspect}"}
|
33
|
+
x_trace = Utils.traceparent_from_context(span_context)
|
34
|
+
exposed_headers = [XTRACE_HEADER_NAME]
|
38
35
|
xtraceoptions_response = recover_response_from_tracestate(span_context.tracestate)
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
setter.set(carrier, XTRACEOPTIONS_RESPONSE_HEADER_NAME, xtraceoptions_response)
|
43
|
-
end
|
37
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] x-trace: #{x_trace}; exposed headers: #{exposed_headers.inspect}; x-trace-options-response: #{xtraceoptions_response}"}
|
38
|
+
exposed_headers.append(XTRACEOPTIONS_RESPONSE_HEADER_NAME) unless xtraceoptions_response.empty?
|
44
39
|
|
45
|
-
|
40
|
+
setter.set(carrier, XTRACE_HEADER_NAME, x_trace)
|
41
|
+
setter.set(carrier, XTRACEOPTIONS_RESPONSE_HEADER_NAME, xtraceoptions_response) unless xtraceoptions_response.empty?
|
46
42
|
setter.set(carrier, HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, exposed_headers.join(","))
|
47
|
-
|
48
43
|
end
|
49
44
|
|
50
45
|
# Returns the predefined propagation fields. If your carrier is reused, you
|
@@ -38,7 +38,7 @@ module SolarWindsAPM
|
|
38
38
|
def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
|
39
39
|
|
40
40
|
SolarWindsAPM.logger.debug do
|
41
|
-
"[#{self.class}/#{__method__}] should_sample? parameters \n
|
41
|
+
"[#{self.class}/#{__method__}] should_sample? start parameters \n
|
42
42
|
trace_id: #{trace_id.unpack1('H*')}\n
|
43
43
|
parent_context: #{parent_context}\n
|
44
44
|
parent_context.inspect: #{parent_context.inspect}\n
|
@@ -48,30 +48,25 @@ module SolarWindsAPM
|
|
48
48
|
attributes: #{attributes}"
|
49
49
|
end
|
50
50
|
|
51
|
-
|
51
|
+
parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
|
52
|
+
xtraceoptions = ::SolarWindsAPM::XTraceOptions.new(parent_context)
|
53
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] parent_span_context: #{parent_span_context.inspect}\n xtraceoptions: #{xtraceoptions.inspect}"}
|
52
54
|
|
53
|
-
parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
|
54
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] should_sample? parent_span_context: #{parent_span_context.inspect}"}
|
55
|
-
|
56
|
-
xtraceoptions = SolarWindsAPM::XTraceOptions.new(parent_context)
|
57
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] xtraceoptions: #{xtraceoptions.inspect}"}
|
58
|
-
|
59
55
|
liboboe_decision = calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_trace_state: #{new_trace_state.inspect}"}
|
67
|
-
|
68
|
-
new_attributes = calculate_attributes(attributes,liboboe_decision,new_trace_state,parent_span_context,xtraceoptions)
|
69
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_attributes: #{new_attributes.inspect}"}
|
56
|
+
otel_decision = otel_decision_from_liboboe(liboboe_decision)
|
57
|
+
new_trace_state = calculate_trace_state(liboboe_decision, parent_span_context, xtraceoptions)
|
58
|
+
new_attributes = otel_sampled?(otel_decision)? calculate_attributes(attributes, liboboe_decision, new_trace_state, parent_span_context, xtraceoptions) : nil
|
59
|
+
sampling_result = ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: otel_decision,
|
60
|
+
attributes: new_attributes,
|
61
|
+
tracestate: new_trace_state)
|
70
62
|
|
71
|
-
sampling_result
|
72
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] sampling_result: #{sampling_result.inspect}"}
|
73
|
-
|
63
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] should_sample? end with sampling_result: #{sampling_result.inspect} from otel_decision: #{otel_decision.inspect} and new_attributes: #{new_attributes.inspect}"}
|
74
64
|
sampling_result
|
65
|
+
rescue StandardError => e
|
66
|
+
SolarWindsAPM.logger.info {"[#{self.class}/#{__method__}] sampler error: #{e.message}"}
|
67
|
+
::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP,
|
68
|
+
attributes: attributes,
|
69
|
+
tracestate: ::OpenTelemetry::Trace::Tracestate::DEFAULT)
|
75
70
|
end
|
76
71
|
|
77
72
|
protected
|
@@ -80,51 +75,36 @@ module SolarWindsAPM
|
|
80
75
|
|
81
76
|
private
|
82
77
|
|
83
|
-
|
78
|
+
##
|
79
|
+
# use parent_span_context and xtraceoptions object to feed to liboboe function getDecisions that get liboboe_decision
|
80
|
+
# name, kind and attributes are used for transaction filter caching (to avoid continous calculate_trace_mode calculation)
|
81
|
+
# return decision Hash
|
84
82
|
def calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
|
85
|
-
|
86
|
-
tracestring
|
87
|
-
if parent_span_context.valid? && parent_span_context.remote?
|
88
|
-
tracestring = Transformer.traceparent_from_context(parent_span_context)
|
89
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_liboboe_decision parent_span_context.remote? #{parent_span_context.remote?} with #{tracestring}"}
|
90
|
-
end
|
91
|
-
|
92
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] name: #{name}, kind: #{kind}, attributes: #{attributes.inspect}"}
|
83
|
+
tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
|
84
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] tracestring: #{tracestring}"}
|
93
85
|
|
94
86
|
# otel-ruby contrib use different key to store url info, currently it's using http.target for path
|
95
87
|
url_path = attributes.nil?? '' : attributes['http.target']
|
96
88
|
transaction_naming_key = "#{url_path}-#{name}-#{kind}"
|
97
|
-
|
98
89
|
tracing_mode = SolarWindsAPM::TransactionCache.get(transaction_naming_key)
|
99
|
-
|
100
|
-
|
101
|
-
|
90
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache: #{transaction_naming_key}; tracing_mode: #{tracing_mode}."}
|
91
|
+
|
92
|
+
unless tracing_mode
|
102
93
|
trans_settings = SolarWindsAPM::TransactionSettings.new(url_path: url_path, name: name, kind: kind)
|
103
94
|
tracing_mode = trans_settings.calculate_trace_mode == 1 ? SWO_TRACING_ENABLED : SWO_TRACING_DISABLED
|
104
95
|
SolarWindsAPM::TransactionCache.set(transaction_naming_key, tracing_mode)
|
105
|
-
else
|
106
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache found: #{transaction_naming_key}."}
|
107
96
|
end
|
108
97
|
|
109
98
|
sw_member_value = parent_span_context.tracestate[SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY]
|
110
|
-
|
111
|
-
# need to create the config class
|
112
99
|
trigger_trace_mode = OboeTracingMode.get_oboe_trigger_trace_mode(@config["trigger_trace"])
|
113
100
|
sample_rate = UNSET
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
timestamp = nil
|
119
|
-
if xtraceoptions
|
120
|
-
options = xtraceoptions.options
|
121
|
-
trigger_trace = xtraceoptions.intify_trigger_trace
|
122
|
-
signature = xtraceoptions.signature
|
123
|
-
timestamp = xtraceoptions.timestamp
|
124
|
-
end
|
101
|
+
options = xtraceoptions&.options
|
102
|
+
trigger_trace = xtraceoptions&.intify_trigger_trace || 0
|
103
|
+
signature = xtraceoptions&.signature
|
104
|
+
timestamp = xtraceoptions&.timestamp
|
125
105
|
|
126
106
|
SolarWindsAPM.logger.debug do
|
127
|
-
"[#{self.class}/#{__method__}] decision parameters \n
|
107
|
+
"[#{self.class}/#{__method__}] get liboboe decision parameters: \n
|
128
108
|
tracestring: #{tracestring}\n
|
129
109
|
sw_member_value: #{sw_member_value}\n
|
130
110
|
tracing_mode: #{tracing_mode}\n
|
@@ -136,9 +116,12 @@ module SolarWindsAPM
|
|
136
116
|
timestamp: #{timestamp}"
|
137
117
|
end
|
138
118
|
|
139
|
-
args = [tracestring,sw_member_value,tracing_mode,sample_rate,
|
140
|
-
|
141
|
-
|
119
|
+
args = [tracestring, sw_member_value, tracing_mode, sample_rate,
|
120
|
+
trigger_trace, trigger_trace_mode, options, signature,timestamp]
|
121
|
+
|
122
|
+
do_metrics, do_sample, rate, source, bucket_rate,
|
123
|
+
bucket_cap, decision_type, auth, status_msg, auth_msg,
|
124
|
+
status = SolarWindsAPM::Context.getDecisions(*args)
|
142
125
|
|
143
126
|
decision = {}
|
144
127
|
decision["do_metrics"] = do_metrics > 0
|
@@ -163,136 +146,78 @@ module SolarWindsAPM
|
|
163
146
|
elsif liboboe_decision["do_metrics"]
|
164
147
|
decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_ONLY
|
165
148
|
end
|
166
|
-
SolarWindsAPM.logger.debug {"
|
149
|
+
SolarWindsAPM.logger.debug {"otel decision: #{decision} created from liboboe_decision: #{liboboe_decision}"}
|
167
150
|
decision
|
168
151
|
end
|
169
152
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
w3c_sanitized = SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED
|
175
|
-
response << [XTRACEOPTIONS_RESP_AUTH, decision['auth_msg']].join(w3c_sanitized) if xtraceoptions.signature && decision['auth_msg']
|
176
|
-
if !decision["auth"] || decision["auth"] < 1
|
177
|
-
trigger_msg = ""
|
178
|
-
tracestring = nil
|
179
|
-
if xtraceoptions.trigger_trace
|
180
|
-
# If a traceparent header was provided then oboe does not generate the message
|
181
|
-
tracestring = Transformer.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
|
182
|
-
trigger_msg = tracestring && decision['decision_type'] == 0 ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : decision['status_msg']
|
183
|
-
else
|
184
|
-
trigger_msg = XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED
|
185
|
-
end
|
186
|
-
|
187
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] create_xtraceoptions_response_value parent_span_context: #{parent_span_context}; tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"}
|
188
|
-
|
189
|
-
# e.g. response << trigger-trace####ok
|
190
|
-
response << [XTRACEOPTIONS_RESP_TRIGGER_TRACE, trigger_msg].join(w3c_sanitized)
|
191
|
-
|
192
|
-
end
|
193
|
-
|
194
|
-
# so far the x-trace-options are only used for liboboe calculate decision for x-trace feature
|
195
|
-
# probably not need for remaining services since liboboe decision only calculate once
|
196
|
-
unless xtraceoptions.ignored.empty?
|
197
|
-
ignored_response = [XTRACEOPTIONS_RESP_IGNORED, xtraceoptions.ignored.join(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED)]
|
198
|
-
response << ignored_response.join(w3c_sanitized)
|
199
|
-
# e.g. response << ignored####invalidkeys,invalidkeys,invalidkeys
|
200
|
-
end
|
201
|
-
|
202
|
-
response.join(';') # e.g. trigger-trace####ok;ignored####invalidkeys,invalidkeys,invalidkeys
|
203
|
-
end
|
204
|
-
|
205
|
-
def create_new_trace_state(parent_span_context, decision)
|
206
|
-
decision = sw_from_span_and_decision(parent_span_context, decision)
|
207
|
-
trace_state = ::OpenTelemetry::Trace::Tracestate.from_hash({SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => decision}) # e.g. sw=3e222c863a04123a-01
|
208
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Created new trace_state: #{trace_state.inspect}"}
|
209
|
-
trace_state
|
210
|
-
end
|
211
|
-
|
212
|
-
#
|
213
|
-
# calculate_trace_state
|
214
|
-
# This function merely just add sw=value and xtrace_options_response=value into the old/new tracestate
|
215
|
-
# The return value tracestate will be used in propagating to next services
|
216
|
-
#
|
217
|
-
def calculate_trace_state(decision, parent_span_context, xtraceoptions)
|
218
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_trace_state parent_span_context: #{parent_span_context.inspect}"}
|
153
|
+
##
|
154
|
+
# add sw=value and xtrace_options_response=value into the old/new tracestate
|
155
|
+
# the returned value tracestate will be used in propagating to next services
|
156
|
+
def calculate_trace_state(liboboe_decision, parent_span_context, xtraceoptions)
|
219
157
|
if !parent_span_context.valid?
|
220
|
-
|
221
|
-
|
158
|
+
trace_state = create_new_trace_state(parent_span_context, liboboe_decision)
|
159
|
+
elsif parent_span_context.tracestate.nil?
|
160
|
+
trace_state = create_new_trace_state(parent_span_context, liboboe_decision)
|
222
161
|
else
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
trace_state = create_new_trace_state(parent_span_context, decision)
|
227
|
-
else
|
228
|
-
trace_state = parent_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context, decision))
|
229
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Updated trace_state with span_id and sw trace_flags: #{trace_state.inspect}"}
|
230
|
-
end
|
162
|
+
trace_state = parent_span_context.tracestate.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY,
|
163
|
+
sw_from_span_and_decision(parent_span_context, liboboe_decision))
|
164
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] updated trace_state: #{trace_state.inspect}"}
|
231
165
|
end
|
232
166
|
|
233
167
|
# for setting up the xtrace_options_response
|
234
168
|
if xtraceoptions&.options
|
235
|
-
trace_state = trace_state.set_value(
|
236
|
-
|
237
|
-
create_xtraceoptions_response_value(decision,parent_span_context,xtraceoptions))
|
169
|
+
trace_state = trace_state.set_value(XTraceOptions.sw_xtraceoptions_response_key.to_s,
|
170
|
+
create_xtraceoptions_response_value(liboboe_decision, parent_span_context, xtraceoptions))
|
238
171
|
end
|
172
|
+
trace_state
|
173
|
+
end
|
239
174
|
|
175
|
+
##
|
176
|
+
#
|
177
|
+
def create_new_trace_state(parent_span_context, liboboe_decision)
|
178
|
+
decision = sw_from_span_and_decision(parent_span_context, liboboe_decision)
|
179
|
+
trace_state = ::OpenTelemetry::Trace::Tracestate.from_hash({SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => decision}) # e.g. sw=3e222c863a04123a-01
|
180
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] created new trace_state: #{trace_state.inspect}"}
|
240
181
|
trace_state
|
241
182
|
end
|
242
183
|
|
184
|
+
##
|
243
185
|
#
|
244
|
-
|
245
|
-
# sw_xtraceoptions_response_key = "xtrace_options_response"
|
246
|
-
# trace_state is the new trace_state from existing span information
|
247
|
-
# parent_span_context.trace_state is from its parent
|
248
|
-
#
|
249
|
-
def add_tracestate_capture_to_attributes_dict(attributes_dict, decision, trace_state, parent_span_context)
|
250
|
-
|
251
|
-
tracestate_capture = attributes_dict[SW_TRACESTATE_CAPTURE_KEY]
|
252
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] tracestate_capture #{tracestate_capture.inspect}; attributes_dict #{attributes_dict.inspect}; trace_state #{trace_state.inspect}; parent_span_context: #{parent_span_context.inspect}" }
|
186
|
+
def create_xtraceoptions_response_value(liboboe_decision, parent_span_context, xtraceoptions)
|
253
187
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
188
|
+
response = []
|
189
|
+
w3c_sanitized = SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED
|
190
|
+
w3c_sanitized_comma = SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED
|
191
|
+
response << [XTRACEOPTIONS_RESP_AUTH, liboboe_decision['auth_msg']].join(w3c_sanitized) if xtraceoptions.signature && liboboe_decision['auth_msg']
|
258
192
|
|
259
|
-
|
260
|
-
|
261
|
-
|
193
|
+
if !liboboe_decision["auth"] || liboboe_decision["auth"] < 1
|
194
|
+
if xtraceoptions.trigger_trace
|
195
|
+
# If a traceparent header was provided then oboe does not generate the message
|
196
|
+
tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
|
197
|
+
trigger_msg = tracestring && liboboe_decision['decision_type'] == 0 ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : liboboe_decision['status_msg']
|
198
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"}
|
199
|
+
else
|
200
|
+
trigger_msg = XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED
|
201
|
+
end
|
262
202
|
|
263
|
-
|
264
|
-
new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context,decision))
|
265
|
-
|
266
|
-
trace_state_no_response = new_attr_trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
|
203
|
+
response << [XTRACEOPTIONS_RESP_TRIGGER_TRACE, trigger_msg].join(w3c_sanitized) # e.g. response << trigger-trace####ok
|
267
204
|
end
|
268
205
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
attributes_dict[SW_TRACESTATE_CAPTURE_KEY] = Transformer.trace_state_header(trace_state_no_response) if no_sw_count > 0
|
275
|
-
|
276
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] attributes_dict #{attributes_dict.inspect}"}
|
277
|
-
attributes_dict
|
206
|
+
# appending ignored value from xtraceoptions to response. e.g. response << ignored####invalidkeys,invalidkeys,invalidkeys
|
207
|
+
response << [XTRACEOPTIONS_RESP_IGNORED, xtraceoptions.ignored.join(w3c_sanitized_comma)].join(w3c_sanitized) unless xtraceoptions.ignored.empty?
|
208
|
+
joined_response = response.join(';')
|
209
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] final response_value: #{joined_response}"}
|
210
|
+
joined_response
|
278
211
|
end
|
279
212
|
|
280
213
|
##
|
281
|
-
# calculate_attributes is used for getting the otel Result class in last step of sampler should_sample?
|
214
|
+
# calculate_attributes is used for getting the otel Result class in last step of sampler should_sample?
|
215
|
+
# e.g. result = Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
|
282
216
|
# calculate_attributes use new_trace_state that is derived from current span information and old tracestate from parent_span_context.tracestate
|
283
217
|
# the sw.w3c.tracestate should perserve the old tracestate value for debugging purpose
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
otel_decision = otel_decision_from_liboboe(decision)
|
289
|
-
return nil if Transformer.sampled?(otel_decision) == false
|
290
|
-
|
291
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Trace decision is_sampled - setting attributes #{otel_decision.inspect}"}
|
292
|
-
|
293
|
-
new_attributes = {}
|
294
|
-
# Copy existing MappingProxyType KV into new_attributes for modification.
|
295
|
-
attributes&.each {|k,v| new_attributes[k] = v}
|
218
|
+
def calculate_attributes(attributes, liboboe_decision, trace_state, parent_span_context, xtraceoptions)
|
219
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_trace_state: #{trace_state.inspect}"}
|
220
|
+
new_attributes = attributes.dup || {}
|
296
221
|
|
297
222
|
# Always (root or is_remote) set _INTERNAL_SW_KEYS if injected
|
298
223
|
new_attributes[INTERNAL_SW_KEYS] = xtraceoptions.sw_keys if xtraceoptions.sw_keys
|
@@ -301,16 +226,14 @@ module SolarWindsAPM
|
|
301
226
|
xtraceoptions.custom_kvs&.each {|k,v| new_attributes[k] = v}
|
302
227
|
|
303
228
|
# Always (root or is_remote) set service entry internal KVs
|
304
|
-
new_attributes[INTERNAL_BUCKET_CAPACITY] =
|
305
|
-
new_attributes[INTERNAL_BUCKET_RATE] =
|
306
|
-
new_attributes[INTERNAL_SAMPLE_RATE] =
|
307
|
-
new_attributes[INTERNAL_SAMPLE_SOURCE] =
|
308
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Set attributes with service entry internal KVs: #{new_attributes}"}
|
229
|
+
new_attributes[INTERNAL_BUCKET_CAPACITY] = liboboe_decision["bucket_cap"].to_s
|
230
|
+
new_attributes[INTERNAL_BUCKET_RATE] = liboboe_decision["bucket_rate"].to_s
|
231
|
+
new_attributes[INTERNAL_SAMPLE_RATE] = liboboe_decision["rate"]
|
232
|
+
new_attributes[INTERNAL_SAMPLE_SOURCE] = liboboe_decision["source"]
|
309
233
|
|
310
234
|
# set sw.tracestate_parent_id if its tracestate contains "sw"
|
311
235
|
sw_value = parent_span_context.tracestate.value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY)
|
312
|
-
|
313
|
-
new_attributes[SW_TRACESTATE_ROOT_KEY]= Transformer.span_id_from_sw(sw_value) if sw_value && parent_span_context.remote?
|
236
|
+
new_attributes[SW_TRACESTATE_ROOT_KEY] = sw_value.split("-")[0] if sw_value && parent_span_context.remote?
|
314
237
|
|
315
238
|
# If unsigned or signed TT (root or is_remote), set TriggeredTrace
|
316
239
|
new_attributes[SolarWindsAPM::Constants::INTERNAL_TRIGGERED_TRACE] = true if xtraceoptions.trigger_trace
|
@@ -321,14 +244,43 @@ module SolarWindsAPM
|
|
321
244
|
return new_attributes.freeze || nil
|
322
245
|
end
|
323
246
|
|
324
|
-
new_attributes =
|
325
|
-
# e.g. {"SWKeys"=>"check-id:check-1013,website-id:booking-demo", "BucketCapacity"=>"6.0", "BucketRate"=>"0.1", "SampleRate"=>-1, "SampleSource"=>-1, "http.method"=>"GET", "http.host"=>"0.0.0.0:8002", "http.scheme"=>"http", "http.target"=>"/call_second_rails/", "http.user_agent"=>"curl/7.81.0", "sw.w3c.tracestate"=>"sw=aaaa1111bbbb2222-01"}
|
247
|
+
new_attributes = add_tracestate_capture_to_new_attributes(new_attributes, liboboe_decision, trace_state, parent_span_context)
|
326
248
|
new_attributes.freeze
|
327
249
|
end
|
328
250
|
|
329
|
-
|
330
|
-
|
331
|
-
|
251
|
+
##
|
252
|
+
#
|
253
|
+
def add_tracestate_capture_to_new_attributes(new_attributes, liboboe_decision, trace_state, parent_span_context)
|
254
|
+
|
255
|
+
tracestate_capture = new_attributes[SW_TRACESTATE_CAPTURE_KEY]
|
256
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] tracestate_capture #{tracestate_capture.inspect}; new_attributes #{new_attributes.inspect}"}
|
257
|
+
|
258
|
+
if tracestate_capture.nil?
|
259
|
+
trace_state_no_response = trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
|
260
|
+
else
|
261
|
+
# retain all potential tracestate pairs for attributes and generate new sw=key for tracestate based on root parent_span_id
|
262
|
+
attr_trace_state = ::OpenTelemetry::Trace::Tracestate.from_string(tracestate_capture)
|
263
|
+
new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context,liboboe_decision))
|
264
|
+
trace_state_no_response = new_attr_trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
|
265
|
+
end
|
266
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] trace_state_no_response #{trace_state_no_response.inspect}"}
|
267
|
+
|
268
|
+
trace_state_no_response = parent_span_context.tracestate.delete(XTraceOptions.sw_xtraceoptions_response_key)
|
269
|
+
no_sw_count = trace_state_no_response.to_h.reject { |k, _v| k == "sw" }.count
|
270
|
+
new_attributes[SW_TRACESTATE_CAPTURE_KEY] = Utils.trace_state_header(trace_state_no_response) if no_sw_count > 0
|
271
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_attributes after add_tracestate_capture_to_new_attributes: #{new_attributes.inspect}"}
|
272
|
+
|
273
|
+
new_attributes
|
274
|
+
end
|
275
|
+
|
276
|
+
# formats tracestate sw value from span_id and liboboe decision as 16-byte span_id with 8-bit trace_flags e.g. 1a2b3c4d5e6f7g8h-01
|
277
|
+
def sw_from_span_and_decision(parent_span_context, liboboe_decision)
|
278
|
+
trace_flag = (liboboe_decision["do_sample"] == true) ? "01" : "00"
|
279
|
+
[parent_span_context.hex_span_id, trace_flag].join("-")
|
280
|
+
end
|
281
|
+
|
282
|
+
def otel_sampled?(otel_decision)
|
283
|
+
otel_decision == ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE
|
332
284
|
end
|
333
285
|
end
|
334
286
|
end
|