solarwinds_apm 6.0.0.prev6 → 6.0.1
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/extconf.rb +43 -42
- 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-lambda-aarch64.so.sha256 +1 -0
- data/ext/oboe_metal/lib/liboboe-1.0-lambda-x86_64.so.sha256 +1 -0
- data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +1 -1
- data/ext/oboe_metal/src/VERSION +1 -1
- data/ext/oboe_metal/src/oboe.h +3 -0
- data/ext/oboe_metal/src/oboe_api.cpp +1 -1
- data/lib/oboe_metal.rb +46 -84
- data/lib/rails/generators/solarwinds_apm/install_generator.rb +21 -19
- data/lib/solarwinds_apm/api/current_trace_info.rb +16 -9
- data/lib/solarwinds_apm/api/custom_metrics.rb +6 -4
- data/lib/solarwinds_apm/api/opentelemetry.rb +10 -6
- data/lib/solarwinds_apm/api/tracing.rb +7 -5
- data/lib/solarwinds_apm/api/transaction_name.rb +21 -11
- data/lib/solarwinds_apm/api.rb +8 -6
- data/lib/solarwinds_apm/config.rb +72 -47
- data/lib/solarwinds_apm/constants.rb +26 -26
- data/lib/solarwinds_apm/logger.rb +2 -0
- data/lib/solarwinds_apm/noop/README.md +1 -1
- data/lib/solarwinds_apm/noop/api.rb +85 -0
- data/lib/solarwinds_apm/noop/context.rb +15 -2
- data/lib/solarwinds_apm/noop/metadata.rb +7 -2
- data/lib/solarwinds_apm/{base.rb → noop/span.rb} +16 -15
- data/lib/solarwinds_apm/noop.rb +33 -0
- data/lib/solarwinds_apm/oboe_init_options.rb +50 -111
- data/lib/solarwinds_apm/opentelemetry/otlp_processor.rb +135 -0
- data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +66 -41
- data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +50 -52
- data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +30 -22
- data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +29 -16
- data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +136 -99
- data/lib/solarwinds_apm/opentelemetry.rb +8 -5
- data/lib/solarwinds_apm/otel_config.rb +38 -43
- data/lib/solarwinds_apm/otel_lambda_config.rb +53 -0
- data/lib/solarwinds_apm/patch/dummy_patch.rb +12 -0
- data/lib/solarwinds_apm/{thread_local.rb → patch.rb} +6 -22
- data/lib/solarwinds_apm/support/logger_formatter.rb +4 -2
- data/lib/solarwinds_apm/support/logging_log_event.rb +2 -0
- data/lib/solarwinds_apm/support/lumberjack_formatter.rb +2 -0
- data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +22 -22
- data/lib/solarwinds_apm/support/service_key_checker.rb +106 -0
- data/lib/solarwinds_apm/{support_report.rb → support/support_report.rb} +15 -10
- data/lib/solarwinds_apm/support/swomarginalia/comment.rb +18 -17
- data/lib/solarwinds_apm/support/swomarginalia/load_swomarginalia.rb +13 -12
- data/lib/solarwinds_apm/support/swomarginalia/railtie.rb +4 -2
- data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +3 -1
- data/lib/solarwinds_apm/support/transaction_cache.rb +6 -4
- data/lib/solarwinds_apm/support/transaction_settings.rb +7 -3
- data/lib/solarwinds_apm/support/txn_name_manager.rb +8 -3
- data/lib/solarwinds_apm/support/utils.rb +12 -9
- data/lib/solarwinds_apm/support/x_trace_options.rb +23 -17
- data/lib/solarwinds_apm/support.rb +28 -23
- data/lib/solarwinds_apm/version.rb +4 -2
- data/lib/solarwinds_apm.rb +82 -52
- metadata +23 -28
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# © 2023 SolarWinds Worldwide, LLC. All rights reserved.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at:http://www.apache.org/licenses/LICENSE-2.0
|
@@ -8,27 +10,27 @@ module SolarWindsAPM
|
|
8
10
|
module OpenTelemetry
|
9
11
|
# SolarWindsSampler
|
10
12
|
class SolarWindsSampler
|
11
|
-
INTERNAL_BUCKET_CAPACITY =
|
12
|
-
INTERNAL_BUCKET_RATE =
|
13
|
-
INTERNAL_SAMPLE_RATE =
|
14
|
-
INTERNAL_SAMPLE_SOURCE =
|
15
|
-
INTERNAL_SW_KEYS =
|
13
|
+
INTERNAL_BUCKET_CAPACITY = 'BucketCapacity'
|
14
|
+
INTERNAL_BUCKET_RATE = 'BucketRate'
|
15
|
+
INTERNAL_SAMPLE_RATE = 'SampleRate'
|
16
|
+
INTERNAL_SAMPLE_SOURCE = 'SampleSource'
|
17
|
+
INTERNAL_SW_KEYS = 'SWKeys'
|
16
18
|
LIBOBOE_CONTINUED = -1
|
17
|
-
SW_TRACESTATE_CAPTURE_KEY =
|
18
|
-
SW_TRACESTATE_ROOT_KEY =
|
19
|
+
SW_TRACESTATE_CAPTURE_KEY = 'sw.w3c.tracestate'
|
20
|
+
SW_TRACESTATE_ROOT_KEY = 'sw.tracestate_parent_id'
|
19
21
|
UNSET = -1
|
20
22
|
SWO_TRACING_ENABLED = 1
|
21
23
|
SWO_TRACING_DISABLED = 0
|
22
24
|
SWO_TRACING_UNSET = -1
|
23
|
-
XTRACEOPTIONS_RESP_AUTH =
|
24
|
-
XTRACEOPTIONS_RESP_IGNORED =
|
25
|
-
XTRACEOPTIONS_RESP_TRIGGER_IGNORED =
|
26
|
-
XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED =
|
27
|
-
XTRACEOPTIONS_RESP_TRIGGER_TRACE =
|
25
|
+
XTRACEOPTIONS_RESP_AUTH = 'auth'
|
26
|
+
XTRACEOPTIONS_RESP_IGNORED = 'ignored'
|
27
|
+
XTRACEOPTIONS_RESP_TRIGGER_IGNORED = 'ignored'
|
28
|
+
XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED = 'not-requested'
|
29
|
+
XTRACEOPTIONS_RESP_TRIGGER_TRACE = 'trigger-trace'
|
28
30
|
|
29
31
|
attr_reader :description
|
30
32
|
|
31
|
-
def initialize(config={})
|
33
|
+
def initialize(config = {})
|
32
34
|
@config = config
|
33
35
|
end
|
34
36
|
|
@@ -42,8 +44,7 @@ module SolarWindsAPM
|
|
42
44
|
# trace_id
|
43
45
|
# parent_context: OpenTelemetry::Context
|
44
46
|
def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
|
45
|
-
|
46
|
-
SolarWindsAPM.logger.debug do
|
47
|
+
SolarWindsAPM.logger.debug do
|
47
48
|
"[#{self.class}/#{__method__}] should_sample? start parameters \n
|
48
49
|
trace_id: #{trace_id.unpack1('H*')}\n
|
49
50
|
parent_context: #{parent_context}\n
|
@@ -54,24 +55,31 @@ module SolarWindsAPM
|
|
54
55
|
attributes: #{attributes}"
|
55
56
|
end
|
56
57
|
|
57
|
-
parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
|
58
|
+
parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
|
58
59
|
xtraceoptions = ::SolarWindsAPM::XTraceOptions.new(parent_context)
|
59
|
-
SolarWindsAPM.logger.debug
|
60
|
+
SolarWindsAPM.logger.debug do
|
61
|
+
"[#{self.class}/#{__method__}] parent_span_context: #{parent_span_context.inspect}\n xtraceoptions: #{xtraceoptions.inspect}"
|
62
|
+
end
|
60
63
|
|
61
64
|
liboboe_decision = calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
|
62
65
|
otel_decision = otel_decision_from_liboboe(liboboe_decision)
|
63
66
|
new_trace_state = calculate_trace_state(liboboe_decision, parent_span_context, xtraceoptions)
|
64
|
-
new_attributes = otel_sampled?(otel_decision)
|
65
|
-
|
66
|
-
|
67
|
+
new_attributes = if otel_sampled?(otel_decision)
|
68
|
+
calculate_attributes(attributes, liboboe_decision,
|
69
|
+
new_trace_state, parent_span_context, xtraceoptions)
|
70
|
+
end
|
71
|
+
sampling_result = ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: otel_decision,
|
72
|
+
attributes: new_attributes,
|
67
73
|
tracestate: new_trace_state)
|
68
|
-
|
69
|
-
SolarWindsAPM.logger.debug
|
74
|
+
|
75
|
+
SolarWindsAPM.logger.debug do
|
76
|
+
"[#{self.class}/#{__method__}] should_sample? end with sampling_result: #{sampling_result.inspect} from otel_decision: #{otel_decision.inspect} and new_attributes: #{new_attributes.inspect}"
|
77
|
+
end
|
70
78
|
sampling_result
|
71
79
|
rescue StandardError => e
|
72
|
-
SolarWindsAPM.logger.info {"[#{self.class}/#{__method__}] sampler error: #{e.message}"}
|
73
|
-
::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP,
|
74
|
-
attributes: attributes,
|
80
|
+
SolarWindsAPM.logger.info { "[#{self.class}/#{__method__}] sampler error: #{e.message}" }
|
81
|
+
::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP,
|
82
|
+
attributes: attributes,
|
75
83
|
tracestate: ::OpenTelemetry::Trace::Tracestate::DEFAULT)
|
76
84
|
end
|
77
85
|
|
@@ -87,29 +95,31 @@ module SolarWindsAPM
|
|
87
95
|
# return decision Hash
|
88
96
|
def calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
|
89
97
|
tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
|
90
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] tracestring: #{tracestring}"}
|
98
|
+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] tracestring: #{tracestring}" }
|
91
99
|
|
92
100
|
# otel-ruby contrib use different key to store url info, currently it's using http.target for path
|
93
|
-
url_path = attributes.nil
|
101
|
+
url_path = attributes.nil? ? '' : attributes['http.target']
|
94
102
|
transaction_naming_key = "#{url_path}-#{name}-#{kind}"
|
95
103
|
tracing_mode = SolarWindsAPM::TransactionCache.get(transaction_naming_key)
|
96
|
-
SolarWindsAPM.logger.debug
|
104
|
+
SolarWindsAPM.logger.debug do
|
105
|
+
"[#{self.class}/#{__method__}] transaction cache: #{transaction_naming_key}; tracing_mode: #{tracing_mode}."
|
106
|
+
end
|
97
107
|
|
98
108
|
unless tracing_mode
|
99
|
-
trans_settings = SolarWindsAPM::TransactionSettings.new(url_path: url_path, name: name, kind: kind)
|
109
|
+
trans_settings = SolarWindsAPM::TransactionSettings.new(url_path: url_path, name: name, kind: kind)
|
100
110
|
tracing_mode = trans_settings.calculate_trace_mode == 1 ? SWO_TRACING_ENABLED : SWO_TRACING_DISABLED
|
101
111
|
SolarWindsAPM::TransactionCache.set(transaction_naming_key, tracing_mode)
|
102
112
|
end
|
103
113
|
|
104
114
|
sw_member_value = parent_span_context.tracestate[SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY]
|
105
|
-
trigger_trace_mode = OboeTracingMode.get_oboe_trigger_trace_mode(@config[
|
115
|
+
trigger_trace_mode = SolarWindsAPM::OboeTracingMode.get_oboe_trigger_trace_mode(@config['trigger_trace'])
|
106
116
|
sample_rate = UNSET
|
107
117
|
options = xtraceoptions&.options
|
108
118
|
trigger_trace = xtraceoptions&.intify_trigger_trace || 0
|
109
119
|
signature = xtraceoptions&.signature
|
110
120
|
timestamp = xtraceoptions&.timestamp
|
111
121
|
|
112
|
-
SolarWindsAPM.logger.debug do
|
122
|
+
SolarWindsAPM.logger.debug do
|
113
123
|
"[#{self.class}/#{__method__}] get liboboe decision parameters: \n
|
114
124
|
tracestring: #{tracestring}\n
|
115
125
|
sw_member_value: #{sw_member_value}\n
|
@@ -123,85 +133,96 @@ module SolarWindsAPM
|
|
123
133
|
end
|
124
134
|
|
125
135
|
args = [tracestring, sw_member_value, tracing_mode, sample_rate,
|
126
|
-
trigger_trace, trigger_trace_mode, options, signature,timestamp]
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
136
|
+
trigger_trace, trigger_trace_mode, options, signature, timestamp]
|
137
|
+
|
138
|
+
if SolarWindsAPM::OboeInitOptions.instance.lambda_env
|
139
|
+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] get decision from oboe_api" }
|
140
|
+
# trigger trace disabled at this point (https://swicloud.atlassian.net/wiki/spaces/NIT/pages/3753116438/AWS+Lambda+Instrumentation+POC#Concerns)
|
141
|
+
do_metrics, do_sample, rate, source, bucket_rate,
|
142
|
+
bucket_cap, decision_type, auth, status_msg, auth_msg,
|
143
|
+
status = SolarWindsAPM.oboe_api.getTracingDecision(*args)
|
144
|
+
else
|
145
|
+
do_metrics, do_sample, rate, source, bucket_rate,
|
146
|
+
bucket_cap, decision_type, auth, status_msg, auth_msg,
|
147
|
+
status = SolarWindsAPM::Context.getDecisions(*args)
|
148
|
+
end
|
149
|
+
|
150
|
+
{
|
151
|
+
'do_metrics' => do_metrics.positive?,
|
152
|
+
'do_sample' => do_sample.positive?,
|
153
|
+
'rate' => rate,
|
154
|
+
'source' => source,
|
155
|
+
'bucket_rate' => bucket_rate,
|
156
|
+
'bucket_cap' => bucket_cap,
|
157
|
+
'decision_type' => decision_type,
|
158
|
+
'auth' => auth,
|
159
|
+
'status_msg' => status_msg,
|
160
|
+
'auth_msg' => auth_msg,
|
161
|
+
'status' => status
|
162
|
+
}
|
145
163
|
end
|
146
164
|
|
147
165
|
def otel_decision_from_liboboe(liboboe_decision)
|
148
|
-
|
149
166
|
decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP
|
150
|
-
if liboboe_decision[
|
151
|
-
decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE
|
152
|
-
elsif liboboe_decision[
|
167
|
+
if liboboe_decision['do_sample']
|
168
|
+
decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE # even if not do_metrics
|
169
|
+
elsif liboboe_decision['do_metrics']
|
153
170
|
decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_ONLY
|
154
171
|
end
|
155
|
-
SolarWindsAPM.logger.debug {"otel decision: #{decision} created from liboboe_decision: #{liboboe_decision}"}
|
172
|
+
SolarWindsAPM.logger.debug { "otel decision: #{decision} created from liboboe_decision: #{liboboe_decision}" }
|
156
173
|
decision
|
157
174
|
end
|
158
175
|
|
159
176
|
##
|
160
|
-
# add sw=value and xtrace_options_response=value into the old/new tracestate
|
177
|
+
# add sw=value and xtrace_options_response=value into the old/new tracestate
|
161
178
|
# the returned value tracestate will be used in propagating to next services
|
162
179
|
def calculate_trace_state(liboboe_decision, parent_span_context, xtraceoptions)
|
163
|
-
if !parent_span_context.valid?
|
164
|
-
trace_state = create_new_trace_state(parent_span_context, liboboe_decision)
|
165
|
-
elsif parent_span_context.tracestate.nil?
|
180
|
+
if !parent_span_context.valid? || parent_span_context.tracestate.nil?
|
166
181
|
trace_state = create_new_trace_state(parent_span_context, liboboe_decision)
|
167
182
|
else
|
168
|
-
trace_state = parent_span_context.tracestate.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY,
|
169
|
-
sw_from_span_and_decision(parent_span_context,
|
170
|
-
|
183
|
+
trace_state = parent_span_context.tracestate.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY,
|
184
|
+
sw_from_span_and_decision(parent_span_context,
|
185
|
+
liboboe_decision))
|
186
|
+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] updated trace_state: #{trace_state.inspect}" }
|
171
187
|
end
|
172
188
|
|
173
189
|
# for setting up the xtrace_options_response
|
174
190
|
if xtraceoptions&.options
|
175
|
-
trace_state = trace_state.set_value(XTraceOptions.sw_xtraceoptions_response_key.to_s,
|
176
|
-
create_xtraceoptions_response_value(liboboe_decision,
|
191
|
+
trace_state = trace_state.set_value(XTraceOptions.sw_xtraceoptions_response_key.to_s,
|
192
|
+
create_xtraceoptions_response_value(liboboe_decision,
|
193
|
+
parent_span_context, xtraceoptions))
|
177
194
|
end
|
178
195
|
trace_state
|
179
196
|
end
|
180
197
|
|
181
198
|
##
|
182
|
-
#
|
199
|
+
#
|
183
200
|
def create_new_trace_state(parent_span_context, liboboe_decision)
|
184
201
|
decision = sw_from_span_and_decision(parent_span_context, liboboe_decision)
|
185
|
-
trace_state = ::OpenTelemetry::Trace::Tracestate.from_hash({SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => decision}) # e.g. sw=3e222c863a04123a-01
|
186
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] created new trace_state: #{trace_state.inspect}"}
|
202
|
+
trace_state = ::OpenTelemetry::Trace::Tracestate.from_hash({ SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => decision }) # e.g. sw=3e222c863a04123a-01
|
203
|
+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] created new trace_state: #{trace_state.inspect}" }
|
187
204
|
trace_state
|
188
205
|
end
|
189
206
|
|
190
207
|
##
|
191
208
|
#
|
192
209
|
def create_xtraceoptions_response_value(liboboe_decision, parent_span_context, xtraceoptions)
|
193
|
-
|
194
210
|
response = []
|
195
211
|
w3c_sanitized = SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED
|
196
212
|
w3c_sanitized_comma = SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED
|
197
|
-
|
198
|
-
|
199
|
-
|
213
|
+
if xtraceoptions.signature && liboboe_decision['auth_msg']
|
214
|
+
response << [XTRACEOPTIONS_RESP_AUTH,
|
215
|
+
liboboe_decision['auth_msg']].join(w3c_sanitized)
|
216
|
+
end
|
217
|
+
|
218
|
+
if !liboboe_decision['auth'] || liboboe_decision['auth'] < 1
|
200
219
|
if xtraceoptions.trigger_trace
|
201
220
|
# If a traceparent header was provided then oboe does not generate the message
|
202
|
-
tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
|
203
|
-
trigger_msg = tracestring && liboboe_decision['decision_type']
|
204
|
-
SolarWindsAPM.logger.debug
|
221
|
+
tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
|
222
|
+
trigger_msg = tracestring && (liboboe_decision['decision_type']).zero? ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : liboboe_decision['status_msg']
|
223
|
+
SolarWindsAPM.logger.debug do
|
224
|
+
"[#{self.class}/#{__method__}] tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"
|
225
|
+
end
|
205
226
|
else
|
206
227
|
trigger_msg = XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED
|
207
228
|
end
|
@@ -210,80 +231,96 @@ module SolarWindsAPM
|
|
210
231
|
end
|
211
232
|
|
212
233
|
# appending ignored value from xtraceoptions to response. e.g. response << ignored####invalidkeys,invalidkeys,invalidkeys
|
213
|
-
|
234
|
+
unless xtraceoptions.ignored.empty?
|
235
|
+
response << [XTRACEOPTIONS_RESP_IGNORED,
|
236
|
+
xtraceoptions.ignored.join(w3c_sanitized_comma)].join(w3c_sanitized)
|
237
|
+
end
|
214
238
|
joined_response = response.join(';')
|
215
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] final response_value: #{joined_response}"}
|
239
|
+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] final response_value: #{joined_response}" }
|
216
240
|
joined_response
|
217
241
|
end
|
218
242
|
|
219
243
|
##
|
220
|
-
# calculate_attributes is used for getting the otel Result class in last step of sampler should_sample?
|
244
|
+
# calculate_attributes is used for getting the otel Result class in last step of sampler should_sample?
|
221
245
|
# e.g. result = Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
|
222
246
|
# calculate_attributes use new_trace_state that is derived from current span information and old tracestate from parent_span_context.tracestate
|
223
247
|
# the sw.w3c.tracestate should perserve the old tracestate value for debugging purpose
|
224
248
|
def calculate_attributes(attributes, liboboe_decision, trace_state, parent_span_context, xtraceoptions)
|
225
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_trace_state: #{trace_state.inspect}"}
|
249
|
+
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] new_trace_state: #{trace_state.inspect}" }
|
226
250
|
new_attributes = attributes.dup || {}
|
227
251
|
|
228
252
|
# Always (root or is_remote) set _INTERNAL_SW_KEYS if injected
|
229
253
|
new_attributes[INTERNAL_SW_KEYS] = xtraceoptions.sw_keys if xtraceoptions.sw_keys
|
230
254
|
|
231
255
|
# Always (root or is_remote) set custom KVs if extracted from x-trace-options
|
232
|
-
xtraceoptions.custom_kvs&.each {|k,v| new_attributes[k] = v}
|
256
|
+
xtraceoptions.custom_kvs&.each { |k, v| new_attributes[k] = v }
|
233
257
|
|
234
|
-
# Always (root or is_remote) set service entry internal KVs
|
235
|
-
new_attributes[INTERNAL_BUCKET_CAPACITY] = liboboe_decision[
|
236
|
-
new_attributes[INTERNAL_BUCKET_RATE] = liboboe_decision[
|
237
|
-
new_attributes[INTERNAL_SAMPLE_RATE] = liboboe_decision[
|
238
|
-
new_attributes[INTERNAL_SAMPLE_SOURCE] = liboboe_decision[
|
258
|
+
# Always (root or is_remote) set service entry internal KVs
|
259
|
+
new_attributes[INTERNAL_BUCKET_CAPACITY] = liboboe_decision['bucket_cap'].to_s
|
260
|
+
new_attributes[INTERNAL_BUCKET_RATE] = liboboe_decision['bucket_rate'].to_s
|
261
|
+
new_attributes[INTERNAL_SAMPLE_RATE] = liboboe_decision['rate']
|
262
|
+
new_attributes[INTERNAL_SAMPLE_SOURCE] = liboboe_decision['source']
|
239
263
|
|
240
264
|
# set sw.tracestate_parent_id if its tracestate contains "sw"
|
241
265
|
sw_value = parent_span_context.tracestate.value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY)
|
242
|
-
new_attributes[SW_TRACESTATE_ROOT_KEY] =
|
266
|
+
new_attributes[SW_TRACESTATE_ROOT_KEY] = sw_value.split('-')[0] if sw_value && parent_span_context.remote?
|
243
267
|
|
244
268
|
# If unsigned or signed TT (root or is_remote), set TriggeredTrace
|
245
269
|
new_attributes[SolarWindsAPM::Constants::INTERNAL_TRIGGERED_TRACE] = true if xtraceoptions.trigger_trace
|
246
270
|
|
247
271
|
# Trace's root span has no valid traceparent nor tracestate so we can't calculate remaining attributes
|
248
272
|
if !parent_span_context.valid? || trace_state.nil?
|
249
|
-
SolarWindsAPM.logger.debug
|
273
|
+
SolarWindsAPM.logger.debug do
|
274
|
+
"[#{self.class}/#{__method__}] No valid traceparent or no tracestate - returning attributes: #{new_attributes}"
|
275
|
+
end
|
250
276
|
return new_attributes.freeze || nil
|
251
277
|
end
|
252
278
|
|
253
|
-
new_attributes = add_tracestate_capture_to_new_attributes(new_attributes, liboboe_decision, trace_state,
|
279
|
+
new_attributes = add_tracestate_capture_to_new_attributes(new_attributes, liboboe_decision, trace_state,
|
280
|
+
parent_span_context)
|
254
281
|
new_attributes.freeze
|
255
282
|
end
|
256
283
|
|
257
284
|
##
|
258
|
-
#
|
285
|
+
#
|
259
286
|
def add_tracestate_capture_to_new_attributes(new_attributes, liboboe_decision, trace_state, parent_span_context)
|
260
|
-
|
261
287
|
tracestate_capture = new_attributes[SW_TRACESTATE_CAPTURE_KEY]
|
262
|
-
SolarWindsAPM.logger.debug
|
288
|
+
SolarWindsAPM.logger.debug do
|
289
|
+
"[#{self.class}/#{__method__}] tracestate_capture #{tracestate_capture.inspect}; new_attributes #{new_attributes.inspect}"
|
290
|
+
end
|
263
291
|
|
264
292
|
if tracestate_capture.nil?
|
265
293
|
trace_state_no_response = trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
|
266
|
-
else
|
294
|
+
else
|
267
295
|
# retain all potential tracestate pairs for attributes and generate new sw=key for tracestate based on root parent_span_id
|
268
296
|
attr_trace_state = ::OpenTelemetry::Trace::Tracestate.from_string(tracestate_capture)
|
269
|
-
new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY,
|
297
|
+
new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY,
|
298
|
+
sw_from_span_and_decision(parent_span_context,
|
299
|
+
liboboe_decision))
|
270
300
|
trace_state_no_response = new_attr_trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
|
271
301
|
end
|
272
|
-
SolarWindsAPM.logger.debug
|
302
|
+
SolarWindsAPM.logger.debug do
|
303
|
+
"[#{self.class}/#{__method__}] trace_state_no_response #{trace_state_no_response.inspect}"
|
304
|
+
end
|
273
305
|
|
274
306
|
trace_state_no_response = parent_span_context.tracestate.delete(XTraceOptions.sw_xtraceoptions_response_key)
|
275
307
|
no_sw_count = trace_state_no_response.to_h.count { |k, _v| k != 'sw' }
|
276
308
|
|
277
|
-
|
278
|
-
|
279
|
-
|
309
|
+
if no_sw_count.positive?
|
310
|
+
new_attributes[SW_TRACESTATE_CAPTURE_KEY] =
|
311
|
+
Utils.trace_state_header(trace_state_no_response)
|
312
|
+
end
|
313
|
+
SolarWindsAPM.logger.debug do
|
314
|
+
"[#{self.class}/#{__method__}] new_attributes after add_tracestate_capture_to_new_attributes: #{new_attributes.inspect}"
|
315
|
+
end
|
316
|
+
|
280
317
|
new_attributes
|
281
318
|
end
|
282
319
|
|
283
320
|
# formats tracestate sw value from span_id and liboboe decision as 16-byte span_id with 8-bit trace_flags e.g. 1a2b3c4d5e6f7g8h-01
|
284
321
|
def sw_from_span_and_decision(parent_span_context, liboboe_decision)
|
285
|
-
trace_flag =
|
286
|
-
[parent_span_context.hex_span_id, trace_flag].join(
|
322
|
+
trace_flag = liboboe_decision['do_sample'] == true ? '01' : '00'
|
323
|
+
[parent_span_context.hex_span_id, trace_flag].join('-')
|
287
324
|
end
|
288
325
|
|
289
326
|
def otel_sampled?(otel_decision)
|
@@ -291,4 +328,4 @@ module SolarWindsAPM
|
|
291
328
|
end
|
292
329
|
end
|
293
330
|
end
|
294
|
-
end
|
331
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# © 2023 SolarWinds Worldwide, LLC. All rights reserved.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at:http://www.apache.org/licenses/LICENSE-2.0
|
@@ -7,8 +9,9 @@
|
|
7
9
|
require 'opentelemetry/sdk'
|
8
10
|
require 'opentelemetry/instrumentation/all'
|
9
11
|
|
10
|
-
require_relative '
|
11
|
-
require_relative '
|
12
|
-
require_relative '
|
13
|
-
require_relative '
|
14
|
-
require_relative '
|
12
|
+
require_relative 'opentelemetry/solarwinds_propagator'
|
13
|
+
require_relative 'opentelemetry/solarwinds_processor'
|
14
|
+
require_relative 'opentelemetry/solarwinds_sampler'
|
15
|
+
require_relative 'opentelemetry/solarwinds_exporter'
|
16
|
+
require_relative 'opentelemetry/solarwinds_response_propagator'
|
17
|
+
require_relative 'opentelemetry/otlp_processor'
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# © 2023 SolarWinds Worldwide, LLC. All rights reserved.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at:http://www.apache.org/licenses/LICENSE-2.0
|
@@ -15,26 +17,21 @@ module SolarWindsAPM
|
|
15
17
|
|
16
18
|
@@agent_enabled = true
|
17
19
|
|
18
|
-
def self.disable_agent
|
19
|
-
return unless @@agent_enabled
|
20
|
+
def self.disable_agent(reason: nil)
|
21
|
+
return unless @@agent_enabled # only show the msg once
|
20
22
|
|
21
23
|
@@agent_enabled = false
|
22
|
-
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}]
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.validate_service_key
|
26
|
-
return unless (ENV['SW_APM_REPORTER'] || 'ssl') == 'ssl'
|
27
|
-
|
28
|
-
disable_agent unless ENV['SW_APM_SERVICE_KEY'] || SolarWindsAPM::Config[:service_key]
|
24
|
+
SolarWindsAPM.logger.warn { "[#{name}/#{__method__}] SolarWindsAPM disabled. No Trace exported. Reason: #{reason}" }
|
29
25
|
end
|
30
26
|
|
31
27
|
def self.resolve_sampler
|
32
|
-
sampler_config = {
|
28
|
+
sampler_config = { 'trigger_trace' => SolarWindsAPM::Config[:trigger_tracing_mode] }
|
33
29
|
@@config[:sampler] =
|
34
30
|
::OpenTelemetry::SDK::Trace::Samplers.parent_based(
|
35
31
|
root: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config),
|
36
32
|
remote_parent_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config),
|
37
|
-
remote_parent_not_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config)
|
33
|
+
remote_parent_not_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config)
|
34
|
+
)
|
38
35
|
end
|
39
36
|
|
40
37
|
#
|
@@ -42,18 +39,20 @@ module SolarWindsAPM
|
|
42
39
|
#
|
43
40
|
def self.resolve_response_propagator
|
44
41
|
response_propagator = SolarWindsAPM::OpenTelemetry::SolarWindsResponsePropagator::TextMapPropagator.new
|
45
|
-
rack_setting = @@config_map[
|
46
|
-
|
42
|
+
rack_setting = @@config_map['OpenTelemetry::Instrumentation::Rack']
|
43
|
+
|
47
44
|
if rack_setting
|
48
45
|
if rack_setting[:response_propagators].instance_of?(Array)
|
49
46
|
rack_setting[:response_propagators].append(response_propagator)
|
50
47
|
elsif rack_setting[:response_propagators].nil?
|
51
48
|
rack_setting[:response_propagators] = [response_propagator]
|
52
49
|
else
|
53
|
-
SolarWindsAPM.logger.warn
|
50
|
+
SolarWindsAPM.logger.warn do
|
51
|
+
"[#{name}/#{__method__}] Rack response propagator resolve failed. Provided type #{rack_setting[:response_propagators].class}, please provide Array e.g. [#{rack_setting[:response_propagators]}]"
|
52
|
+
end
|
54
53
|
end
|
55
54
|
else
|
56
|
-
@@config_map[
|
55
|
+
@@config_map['OpenTelemetry::Instrumentation::Rack'] = { response_propagators: [response_propagator] }
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
@@ -62,18 +61,20 @@ module SolarWindsAPM
|
|
62
61
|
end
|
63
62
|
|
64
63
|
def self.print_config
|
65
|
-
@@config.each do |
|
66
|
-
SolarWindsAPM.logger.
|
64
|
+
@@config.each do |k, v|
|
65
|
+
SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] Config Key/Value: #{k}, #{v.class}" }
|
67
66
|
end
|
68
|
-
@@config_map.each do |
|
69
|
-
SolarWindsAPM.logger.
|
67
|
+
@@config_map.each do |k, v|
|
68
|
+
SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] Config Key/Value: #{k}, #{v}" }
|
70
69
|
end
|
70
|
+
nil
|
71
71
|
end
|
72
72
|
|
73
73
|
def self.resolve_solarwinds_processor
|
74
74
|
txn_manager = SolarWindsAPM::TxnNameManager.new
|
75
|
-
exporter
|
76
|
-
@@config[:
|
75
|
+
exporter = SolarWindsAPM::OpenTelemetry::SolarWindsExporter.new(txn_manager: txn_manager)
|
76
|
+
@@config[:metrics_processor] = SolarWindsAPM::OpenTelemetry::SolarWindsProcessor.new(txn_manager)
|
77
|
+
@@config[:span_processor] = ::OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(exporter)
|
77
78
|
end
|
78
79
|
|
79
80
|
def self.resolve_solarwinds_propagator
|
@@ -82,47 +83,36 @@ module SolarWindsAPM
|
|
82
83
|
|
83
84
|
def self.validate_propagator(propagators)
|
84
85
|
if propagators.nil?
|
85
|
-
disable_agent
|
86
|
+
disable_agent(reason: 'propagators are invaliad.')
|
86
87
|
return
|
87
88
|
end
|
88
89
|
|
89
|
-
SolarWindsAPM.logger.debug {"[#{name}/#{__method__}] propagators: #{propagators.map(&:class)}"}
|
90
|
-
unless ([::OpenTelemetry::Trace::Propagation::TraceContext::TextMapPropagator, ::OpenTelemetry::Baggage::Propagation::TextMapPropagator] - propagators.map(&:class)).empty?
|
91
|
-
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Missing tracecontext propagator."}
|
92
|
-
disable_agent
|
93
|
-
end
|
90
|
+
SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] propagators: #{propagators.map(&:class)}" }
|
91
|
+
disable_agent(reason: 'Missing tracecontext propagator.') unless ([::OpenTelemetry::Trace::Propagation::TraceContext::TextMapPropagator, ::OpenTelemetry::Baggage::Propagation::TextMapPropagator] - propagators.map(&:class)).empty?
|
94
92
|
end
|
95
93
|
|
96
94
|
def self.initialize
|
97
95
|
unless defined?(::OpenTelemetry::SDK::Configurator)
|
98
|
-
|
99
|
-
disable_agent
|
96
|
+
disable_agent(reason: 'missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded.')
|
100
97
|
return
|
101
98
|
end
|
102
99
|
|
103
|
-
validate_service_key
|
104
|
-
|
105
|
-
return unless @@agent_enabled
|
106
|
-
|
107
100
|
resolve_sampler
|
108
|
-
|
109
101
|
resolve_solarwinds_propagator
|
110
102
|
resolve_solarwinds_processor
|
111
103
|
resolve_response_propagator
|
112
104
|
|
113
105
|
print_config if SolarWindsAPM.logger.level.zero?
|
114
106
|
|
107
|
+
# resolve OTEL environmental variables
|
115
108
|
ENV['OTEL_TRACES_EXPORTER'] = 'none' if ENV['OTEL_TRACES_EXPORTER'].to_s.empty?
|
116
|
-
|
117
109
|
if ENV['OTEL_LOG_LEVEL'].to_s.empty?
|
118
|
-
::
|
119
|
-
|
120
|
-
c.use_all(@@config_map)
|
121
|
-
end
|
122
|
-
else
|
123
|
-
::OpenTelemetry::SDK.configure { |c| c.use_all(@@config_map) }
|
110
|
+
log_level = (ENV['SW_APM_DEBUG_LEVEL'] || SolarWindsAPM::Config[:debug_level] || 3).to_i
|
111
|
+
ENV['OTEL_LOG_LEVEL'] = SolarWindsAPM::Config::SW_LOG_LEVEL_MAPPING.dig(log_level, :otel)
|
124
112
|
end
|
125
113
|
|
114
|
+
::OpenTelemetry::SDK.configure { |c| c.use_all(@@config_map) }
|
115
|
+
|
126
116
|
validate_propagator(::OpenTelemetry.propagation.instance_variable_get(:@propagators))
|
127
117
|
|
128
118
|
return unless @@agent_enabled
|
@@ -131,6 +121,7 @@ module SolarWindsAPM
|
|
131
121
|
::OpenTelemetry.propagation.instance_variable_get(:@propagators).append(@@config[:propagators])
|
132
122
|
|
133
123
|
# append our processors (with our exporter)
|
124
|
+
::OpenTelemetry.tracer_provider.add_span_processor(@@config[:metrics_processor])
|
134
125
|
::OpenTelemetry.tracer_provider.add_span_processor(@@config[:span_processor])
|
135
126
|
|
136
127
|
# configure sampler afterwards
|
@@ -152,14 +143,18 @@ module SolarWindsAPM
|
|
152
143
|
#
|
153
144
|
def self.initialize_with_config
|
154
145
|
unless block_given?
|
155
|
-
SolarWindsAPM.logger.warn
|
146
|
+
SolarWindsAPM.logger.warn do
|
147
|
+
"[#{name}/#{__method__}] Block not given while doing in-code configuration. Agent disabled."
|
148
|
+
end
|
156
149
|
return
|
157
150
|
end
|
158
151
|
|
159
152
|
yield @@config_map
|
160
153
|
|
161
154
|
if @@config_map.empty?
|
162
|
-
SolarWindsAPM.logger.warn
|
155
|
+
SolarWindsAPM.logger.warn do
|
156
|
+
"[#{name}/#{__method__}] No configuration given for in-code configuration. Agent disabled."
|
157
|
+
end
|
163
158
|
return
|
164
159
|
end
|
165
160
|
|