solarwinds_apm 6.0.0.preV1 → 6.0.0.preV3
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/LICENSE +28 -54
- data/ext/oboe_metal/extconf.rb +23 -23
- 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/init_solarwinds_apm.cc +0 -6
- data/ext/oboe_metal/src/oboe.h +0 -12
- data/ext/oboe_metal/src/oboe_swig_wrap.cc +5 -5
- data/lib/oboe_metal.rb +3 -3
- data/lib/rails/generators/solarwinds_apm/install_generator.rb +0 -3
- data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +8 -25
- data/lib/solarwinds_apm/api/current_trace_info.rb +1 -2
- data/lib/solarwinds_apm/api/opentelemetry.rb +39 -0
- data/lib/solarwinds_apm/api/transaction_name.rb +18 -17
- data/lib/solarwinds_apm/api.rb +2 -0
- data/lib/solarwinds_apm/config.rb +21 -63
- 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 +11 -16
- data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +92 -87
- data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +27 -23
- 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 +132 -175
- data/lib/solarwinds_apm/otel_config.rb +55 -36
- data/lib/solarwinds_apm/support/lumberjack_formatter.rb +17 -2
- data/lib/solarwinds_apm/support/swomarginalia/comment.rb +2 -2
- data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +6 -5
- data/lib/solarwinds_apm/support/transaction_cache.rb +27 -2
- data/lib/solarwinds_apm/support/transaction_settings.rb +10 -10
- 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 +4 -2
- data/lib/solarwinds_apm/version.rb +1 -1
- data/lib/solarwinds_apm.rb +3 -4
- metadata +18 -33
- data/lib/oboe.rb +0 -7
- data/lib/solarwinds_apm/noop/profiling.rb +0 -17
- data/lib/solarwinds_apm/support/transformer.rb +0 -56
@@ -37,39 +37,36 @@ module SolarWindsAPM
|
|
37
37
|
# parent_context: OpenTelemetry::Context
|
38
38
|
def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
|
39
39
|
|
40
|
-
SolarWindsAPM.logger.debug
|
40
|
+
SolarWindsAPM.logger.debug do
|
41
|
+
"[#{self.class}/#{__method__}] should_sample? start parameters \n
|
41
42
|
trace_id: #{trace_id.unpack1('H*')}\n
|
42
43
|
parent_context: #{parent_context}\n
|
43
44
|
parent_context.inspect: #{parent_context.inspect}\n
|
44
45
|
links: #{links}\n
|
45
46
|
name: #{name}\n
|
46
47
|
kind: #{kind}\n
|
47
|
-
attributes: #{attributes}"
|
48
|
+
attributes: #{attributes}"
|
49
|
+
end
|
48
50
|
|
49
|
-
|
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}"}
|
50
54
|
|
51
|
-
parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
|
52
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] should_sample? parent_span_context: #{parent_span_context.inspect}"}
|
53
|
-
|
54
|
-
xtraceoptions = SolarWindsAPM::XTraceOptions.new(parent_context)
|
55
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] xtraceoptions: #{xtraceoptions.inspect}"}
|
56
|
-
|
57
55
|
liboboe_decision = calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_trace_state: #{new_trace_state.inspect}"}
|
65
|
-
|
66
|
-
new_attributes = calculate_attributes(attributes,liboboe_decision,new_trace_state,parent_span_context,xtraceoptions)
|
67
|
-
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)
|
68
62
|
|
69
|
-
sampling_result
|
70
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] sampling_result: #{sampling_result.inspect}"}
|
71
|
-
|
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}"}
|
72
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)
|
73
70
|
end
|
74
71
|
|
75
72
|
protected
|
@@ -78,49 +75,36 @@ module SolarWindsAPM
|
|
78
75
|
|
79
76
|
private
|
80
77
|
|
81
|
-
|
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
|
82
82
|
def calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
|
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}"}
|
83
85
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_liboboe_decision parent_span_context.remote? #{parent_span_context.remote?} with #{tracestring}"}
|
88
|
-
end
|
89
|
-
|
90
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] name: #{name}, kind: #{kind}, attributes: #{attributes.inspect}"}
|
91
|
-
|
92
|
-
url = attributes.nil?? '' : attributes['http.host'] || attributes['http.url'] || attributes['net.peer.name'] # otel-ruby contrib use different key to store url info
|
93
|
-
transaction_naming_key = "#{url}-#{name}-#{kind}"
|
94
|
-
|
86
|
+
# otel-ruby contrib use different key to store url info, currently it's using http.target for path
|
87
|
+
url_path = attributes.nil?? '' : attributes['http.target']
|
88
|
+
transaction_naming_key = "#{url_path}-#{name}-#{kind}"
|
95
89
|
tracing_mode = SolarWindsAPM::TransactionCache.get(transaction_naming_key)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
trans_settings = SolarWindsAPM::TransactionSettings.new(
|
90
|
+
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache: #{transaction_naming_key}; tracing_mode: #{tracing_mode}."}
|
91
|
+
|
92
|
+
unless tracing_mode
|
93
|
+
trans_settings = SolarWindsAPM::TransactionSettings.new(url_path: url_path, name: name, kind: kind)
|
100
94
|
tracing_mode = trans_settings.calculate_trace_mode == 1 ? SWO_TRACING_ENABLED : SWO_TRACING_DISABLED
|
101
95
|
SolarWindsAPM::TransactionCache.set(transaction_naming_key, tracing_mode)
|
102
|
-
else
|
103
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache found: #{transaction_naming_key}."}
|
104
96
|
end
|
105
97
|
|
106
98
|
sw_member_value = parent_span_context.tracestate[SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY]
|
107
|
-
|
108
|
-
# need to create the config class
|
109
99
|
trigger_trace_mode = OboeTracingMode.get_oboe_trigger_trace_mode(@config["trigger_trace"])
|
110
100
|
sample_rate = UNSET
|
101
|
+
options = xtraceoptions&.options
|
102
|
+
trigger_trace = xtraceoptions&.intify_trigger_trace || 0
|
103
|
+
signature = xtraceoptions&.signature
|
104
|
+
timestamp = xtraceoptions&.timestamp
|
111
105
|
|
112
|
-
|
113
|
-
|
114
|
-
signature = nil
|
115
|
-
timestamp = nil
|
116
|
-
if xtraceoptions
|
117
|
-
options = xtraceoptions.options
|
118
|
-
trigger_trace = xtraceoptions.intify_trigger_trace
|
119
|
-
signature = xtraceoptions.signature
|
120
|
-
timestamp = xtraceoptions.timestamp
|
121
|
-
end
|
122
|
-
|
123
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] decision parameters \n
|
106
|
+
SolarWindsAPM.logger.debug do
|
107
|
+
"[#{self.class}/#{__method__}] get liboboe decision parameters: \n
|
124
108
|
tracestring: #{tracestring}\n
|
125
109
|
sw_member_value: #{sw_member_value}\n
|
126
110
|
tracing_mode: #{tracing_mode}\n
|
@@ -129,11 +113,15 @@ module SolarWindsAPM
|
|
129
113
|
trigger_trace_mode: #{trigger_trace_mode}\n
|
130
114
|
options: #{options}\n
|
131
115
|
signature: #{signature}\n
|
132
|
-
timestamp: #{timestamp}"
|
116
|
+
timestamp: #{timestamp}"
|
117
|
+
end
|
118
|
+
|
119
|
+
args = [tracestring, sw_member_value, tracing_mode, sample_rate,
|
120
|
+
trigger_trace, trigger_trace_mode, options, signature,timestamp]
|
133
121
|
|
134
|
-
|
135
|
-
|
136
|
-
|
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)
|
137
125
|
|
138
126
|
decision = {}
|
139
127
|
decision["do_metrics"] = do_metrics > 0
|
@@ -158,136 +146,78 @@ module SolarWindsAPM
|
|
158
146
|
elsif liboboe_decision["do_metrics"]
|
159
147
|
decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_ONLY
|
160
148
|
end
|
161
|
-
SolarWindsAPM.logger.debug {"
|
149
|
+
SolarWindsAPM.logger.debug {"otel decision: #{decision} created from liboboe_decision: #{liboboe_decision}"}
|
162
150
|
decision
|
163
151
|
end
|
164
152
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
w3c_sanitized = SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED
|
170
|
-
response << [XTRACEOPTIONS_RESP_AUTH, decision['auth_msg']].join(w3c_sanitized) if xtraceoptions.signature && decision['auth_msg']
|
171
|
-
if !decision["auth"] || decision["auth"] < 1
|
172
|
-
trigger_msg = ""
|
173
|
-
tracestring = nil
|
174
|
-
if xtraceoptions.trigger_trace
|
175
|
-
# If a traceparent header was provided then oboe does not generate the message
|
176
|
-
tracestring = Transformer.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
|
177
|
-
trigger_msg = tracestring && decision['decision_type'] == 0 ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : decision['status_msg']
|
178
|
-
else
|
179
|
-
trigger_msg = XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED
|
180
|
-
end
|
181
|
-
|
182
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] create_xtraceoptions_response_value parent_span_context: #{parent_span_context}; tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"}
|
183
|
-
|
184
|
-
# e.g. response << trigger-trace####ok
|
185
|
-
response << [XTRACEOPTIONS_RESP_TRIGGER_TRACE, trigger_msg].join(w3c_sanitized)
|
186
|
-
|
187
|
-
end
|
188
|
-
|
189
|
-
# so far the x-trace-options are only used for liboboe calculate decision for x-trace feature
|
190
|
-
# probably not need for remaining services since liboboe decision only calculate once
|
191
|
-
unless xtraceoptions.ignored.empty?
|
192
|
-
ignored_response = [XTRACEOPTIONS_RESP_IGNORED, xtraceoptions.ignored.join(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED)]
|
193
|
-
response << ignored_response.join(w3c_sanitized)
|
194
|
-
# e.g. response << ignored####invalidkeys,invalidkeys,invalidkeys
|
195
|
-
end
|
196
|
-
|
197
|
-
response.join(';') # e.g. trigger-trace####ok;ignored####invalidkeys,invalidkeys,invalidkeys
|
198
|
-
end
|
199
|
-
|
200
|
-
def create_new_trace_state(parent_span_context, decision)
|
201
|
-
decision = sw_from_span_and_decision(parent_span_context, decision)
|
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}"}
|
204
|
-
trace_state
|
205
|
-
end
|
206
|
-
|
207
|
-
#
|
208
|
-
# calculate_trace_state
|
209
|
-
# This function merely just add sw=value and xtrace_options_response=value into the old/new tracestate
|
210
|
-
# The return value tracestate will be used in propagating to next services
|
211
|
-
#
|
212
|
-
def calculate_trace_state(decision, parent_span_context, xtraceoptions)
|
213
|
-
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)
|
214
157
|
if !parent_span_context.valid?
|
215
|
-
|
216
|
-
|
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)
|
217
161
|
else
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
trace_state = create_new_trace_state(parent_span_context, decision)
|
222
|
-
else
|
223
|
-
trace_state = parent_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context, decision))
|
224
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Updated trace_state with span_id and sw trace_flags: #{trace_state.inspect}"}
|
225
|
-
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}"}
|
226
165
|
end
|
227
166
|
|
228
167
|
# for setting up the xtrace_options_response
|
229
168
|
if xtraceoptions&.options
|
230
|
-
trace_state = trace_state.set_value(
|
231
|
-
|
232
|
-
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))
|
233
171
|
end
|
172
|
+
trace_state
|
173
|
+
end
|
234
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}"}
|
235
181
|
trace_state
|
236
182
|
end
|
237
183
|
|
184
|
+
##
|
238
185
|
#
|
239
|
-
|
240
|
-
# sw_xtraceoptions_response_key = "xtrace_options_response"
|
241
|
-
# trace_state is the new trace_state from existing span information
|
242
|
-
# parent_span_context.trace_state is from its parent
|
243
|
-
#
|
244
|
-
def add_tracestate_capture_to_attributes_dict(attributes_dict, decision, trace_state, parent_span_context)
|
245
|
-
|
246
|
-
tracestate_capture = attributes_dict[SW_TRACESTATE_CAPTURE_KEY]
|
247
|
-
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)
|
248
187
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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']
|
253
192
|
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
257
202
|
|
258
|
-
|
259
|
-
new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context,decision))
|
260
|
-
|
261
|
-
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
|
262
204
|
end
|
263
205
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
attributes_dict[SW_TRACESTATE_CAPTURE_KEY] = Transformer.trace_state_header(trace_state_no_response) if no_sw_count > 0
|
270
|
-
|
271
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] attributes_dict #{attributes_dict.inspect}"}
|
272
|
-
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
|
273
211
|
end
|
274
212
|
|
275
213
|
##
|
276
|
-
# 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)
|
277
216
|
# calculate_attributes use new_trace_state that is derived from current span information and old tracestate from parent_span_context.tracestate
|
278
217
|
# the sw.w3c.tracestate should perserve the old tracestate value for debugging purpose
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
otel_decision = otel_decision_from_liboboe(decision)
|
284
|
-
return nil if Transformer.sampled?(otel_decision) == false
|
285
|
-
|
286
|
-
SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Trace decision is_sampled - setting attributes #{otel_decision.inspect}"}
|
287
|
-
|
288
|
-
new_attributes = {}
|
289
|
-
# Copy existing MappingProxyType KV into new_attributes for modification.
|
290
|
-
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 || {}
|
291
221
|
|
292
222
|
# Always (root or is_remote) set _INTERNAL_SW_KEYS if injected
|
293
223
|
new_attributes[INTERNAL_SW_KEYS] = xtraceoptions.sw_keys if xtraceoptions.sw_keys
|
@@ -296,16 +226,14 @@ module SolarWindsAPM
|
|
296
226
|
xtraceoptions.custom_kvs&.each {|k,v| new_attributes[k] = v}
|
297
227
|
|
298
228
|
# Always (root or is_remote) set service entry internal KVs
|
299
|
-
new_attributes[INTERNAL_BUCKET_CAPACITY] =
|
300
|
-
new_attributes[INTERNAL_BUCKET_RATE] =
|
301
|
-
new_attributes[INTERNAL_SAMPLE_RATE] =
|
302
|
-
new_attributes[INTERNAL_SAMPLE_SOURCE] =
|
303
|
-
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"]
|
304
233
|
|
305
234
|
# set sw.tracestate_parent_id if its tracestate contains "sw"
|
306
235
|
sw_value = parent_span_context.tracestate.value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY)
|
307
|
-
|
308
|
-
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?
|
309
237
|
|
310
238
|
# If unsigned or signed TT (root or is_remote), set TriggeredTrace
|
311
239
|
new_attributes[SolarWindsAPM::Constants::INTERNAL_TRIGGERED_TRACE] = true if xtraceoptions.trigger_trace
|
@@ -316,14 +244,43 @@ module SolarWindsAPM
|
|
316
244
|
return new_attributes.freeze || nil
|
317
245
|
end
|
318
246
|
|
319
|
-
new_attributes =
|
320
|
-
# 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)
|
321
248
|
new_attributes.freeze
|
322
249
|
end
|
323
250
|
|
324
|
-
|
325
|
-
|
326
|
-
|
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
|
327
284
|
end
|
328
285
|
end
|
329
286
|
end
|
@@ -6,14 +6,14 @@ module SolarWindsAPM
|
|
6
6
|
module OTelConfig
|
7
7
|
@@config = {}
|
8
8
|
@@config_map = {}
|
9
|
-
|
9
|
+
|
10
10
|
@@agent_enabled = true
|
11
11
|
|
12
12
|
def self.disable_agent
|
13
13
|
return unless @@agent_enabled # only show the msg once
|
14
|
-
|
14
|
+
|
15
15
|
@@agent_enabled = false
|
16
|
-
SolarWindsAPM.logger.warn {"[#{
|
16
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Agent disabled. No Trace exported."}
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.validate_service_key
|
@@ -25,51 +25,69 @@ module SolarWindsAPM
|
|
25
25
|
def self.resolve_sampler
|
26
26
|
|
27
27
|
resolve_sampler_config
|
28
|
-
@@config[:sampler] =
|
28
|
+
@@config[:sampler] =
|
29
29
|
::OpenTelemetry::SDK::Trace::Samplers.parent_based(
|
30
30
|
root: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(@@config[:sampler_config]),
|
31
31
|
remote_parent_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(@@config[:sampler_config]),
|
32
32
|
remote_parent_not_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(@@config[:sampler_config]))
|
33
33
|
end
|
34
34
|
|
35
|
-
def self.resolve_sampler_config
|
35
|
+
def self.resolve_sampler_config
|
36
36
|
sampler_config = {}
|
37
|
-
sampler_config["trigger_trace"] = "enabled"
|
37
|
+
sampler_config["trigger_trace"] = "enabled"
|
38
38
|
sampler_config["trigger_trace"] = nil if ENV["SW_APM_TRIGGER_TRACING_MODE"] == 'disabled'
|
39
39
|
@@config[:sampler_config] = sampler_config
|
40
40
|
end
|
41
41
|
|
42
42
|
#
|
43
43
|
# append/add solarwinds_response_propagator into rack instrumentation
|
44
|
-
#
|
45
|
-
def self.
|
46
|
-
response_propagator
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
#
|
45
|
+
def self.resolve_response_propagator
|
46
|
+
response_propagator = SolarWindsAPM::OpenTelemetry::SolarWindsResponsePropagator::TextMapPropagator.new
|
47
|
+
rack_setting = @@config_map["OpenTelemetry::Instrumentation::Rack"]
|
48
|
+
|
49
|
+
if rack_setting
|
50
|
+
if rack_setting[:response_propagators].instance_of?(Array)
|
51
|
+
rack_setting[:response_propagators].append(response_propagator)
|
52
|
+
elsif rack_setting[:response_propagators].nil?
|
53
|
+
rack_setting[:response_propagators] = [response_propagator]
|
50
54
|
else
|
51
|
-
|
55
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Rack response propagator resolve failed. Provided type #{rack_setting[:response_propagators].class}, please provide Array e.g. [#{rack_setting[:response_propagators]}]"}
|
52
56
|
end
|
53
57
|
else
|
54
58
|
@@config_map["OpenTelemetry::Instrumentation::Rack"] = {response_propagators: [response_propagator]}
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
62
|
+
def self.obfuscate_helper(instrumentation)
|
63
|
+
if @@config_map[instrumentation] # user provided the option
|
64
|
+
@@config_map[instrumentation][:db_statement] = :obfuscate unless @@config_map[instrumentation][:db_statement] # user provided the db_statement, ignore our default setting
|
65
|
+
else
|
66
|
+
@@config_map[instrumentation] = {db_statement: :obfuscate}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.obfuscate_query
|
71
|
+
obfuscate_helper("OpenTelemetry::Instrumentation::Dalli")
|
72
|
+
obfuscate_helper("OpenTelemetry::Instrumentation::Mysql2")
|
73
|
+
obfuscate_helper("OpenTelemetry::Instrumentation::PG")
|
74
|
+
end
|
75
|
+
|
58
76
|
def self.[](key)
|
59
77
|
@@config[key.to_sym]
|
60
78
|
end
|
61
79
|
|
62
80
|
def self.print_config
|
63
81
|
@@config.each do |config, value|
|
64
|
-
SolarWindsAPM.logger.warn {"[#{
|
82
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] config: #{config} = #{value}"}
|
65
83
|
end
|
66
84
|
@@config_map.each do |config, value|
|
67
|
-
SolarWindsAPM.logger.warn {"[#{
|
85
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] config_map: #{config} = #{value}"}
|
68
86
|
end
|
69
87
|
end
|
70
88
|
|
71
89
|
def self.resolve_solarwinds_processor
|
72
|
-
txn_manager = SolarWindsAPM::
|
90
|
+
txn_manager = SolarWindsAPM::TxnNameManager.new
|
73
91
|
exporter = SolarWindsAPM::OpenTelemetry::SolarWindsExporter.new(txn_manager: txn_manager)
|
74
92
|
@@config[:span_processor] = SolarWindsAPM::OpenTelemetry::SolarWindsProcessor.new(exporter, txn_manager)
|
75
93
|
end
|
@@ -84,16 +102,16 @@ module SolarWindsAPM
|
|
84
102
|
return
|
85
103
|
end
|
86
104
|
|
87
|
-
SolarWindsAPM.logger.debug {"[#{
|
105
|
+
SolarWindsAPM.logger.debug {"[#{name}/#{__method__}] propagators: #{propagators.map(&:class)}"}
|
88
106
|
unless ([::OpenTelemetry::Trace::Propagation::TraceContext::TextMapPropagator, ::OpenTelemetry::Baggage::Propagation::TextMapPropagator] - propagators.map(&:class)).empty? # rubocop:disable Style/GuardClause
|
89
|
-
SolarWindsAPM.logger.warn {"[#{
|
107
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Missing tracecontext propagator."}
|
90
108
|
disable_agent
|
91
109
|
end
|
92
110
|
end
|
93
111
|
|
94
112
|
def self.initialize
|
95
113
|
unless defined?(::OpenTelemetry::SDK::Configurator)
|
96
|
-
SolarWindsAPM.logger.warn {"[#{
|
114
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded."}
|
97
115
|
disable_agent
|
98
116
|
return
|
99
117
|
end
|
@@ -103,39 +121,40 @@ module SolarWindsAPM
|
|
103
121
|
return unless @@agent_enabled
|
104
122
|
|
105
123
|
resolve_sampler
|
106
|
-
|
124
|
+
|
107
125
|
resolve_solarwinds_propagator
|
108
126
|
resolve_solarwinds_processor
|
109
|
-
|
127
|
+
resolve_response_propagator
|
128
|
+
|
129
|
+
obfuscate_query
|
110
130
|
|
111
131
|
print_config if SolarWindsAPM.logger.level.zero?
|
112
132
|
|
113
|
-
ENV['OTEL_TRACES_EXPORTER'] = 'none' if ENV['OTEL_TRACES_EXPORTER'].
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
133
|
+
ENV['OTEL_TRACES_EXPORTER'] = 'none' if ENV['OTEL_TRACES_EXPORTER'].to_s.empty?
|
134
|
+
|
135
|
+
::OpenTelemetry::SDK.configure { |c| c.use_all(@@config_map) }
|
136
|
+
|
118
137
|
validate_propagator(::OpenTelemetry.propagation.instance_variable_get(:@propagators))
|
119
138
|
|
120
139
|
return unless @@agent_enabled
|
121
140
|
|
122
141
|
# append our propagators
|
123
|
-
::OpenTelemetry.propagation.instance_variable_get(:@propagators).append(@@config[:propagators])
|
124
|
-
|
125
|
-
# append our processors (with our exporter)
|
142
|
+
::OpenTelemetry.propagation.instance_variable_get(:@propagators).append(@@config[:propagators])
|
143
|
+
|
144
|
+
# append our processors (with our exporter)
|
126
145
|
::OpenTelemetry.tracer_provider.add_span_processor(@@config[:span_processor])
|
127
|
-
|
146
|
+
|
128
147
|
# configure sampler afterwards
|
129
148
|
::OpenTelemetry.tracer_provider.sampler = @@config[:sampler]
|
130
149
|
nil
|
131
150
|
end
|
132
151
|
|
133
|
-
#
|
152
|
+
#
|
134
153
|
# Allow initialize after set new value to SolarWindsAPM::Config[:key]=value
|
135
|
-
#
|
154
|
+
#
|
136
155
|
# Usage:
|
137
|
-
#
|
138
|
-
# Default using the use_all to load all instrumentation
|
156
|
+
#
|
157
|
+
# Default using the use_all to load all instrumentation
|
139
158
|
# But with specific instrumentation disabled, use {:enabled: false} in config
|
140
159
|
# SolarWindsAPM::OTelConfig.initialize_with_config do |config|
|
141
160
|
# config["OpenTelemetry::Instrumentation::Rack"] = {"a" => "b"}
|
@@ -144,14 +163,14 @@ module SolarWindsAPM
|
|
144
163
|
#
|
145
164
|
def self.initialize_with_config
|
146
165
|
unless block_given?
|
147
|
-
SolarWindsAPM.logger.warn {"[#{
|
166
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Block not given while doing in-code configuration. Agent disabled."}
|
148
167
|
return
|
149
168
|
end
|
150
169
|
|
151
170
|
yield @@config_map
|
152
171
|
|
153
172
|
if @@config_map.empty?
|
154
|
-
SolarWindsAPM.logger.warn {"[#{
|
173
|
+
SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] No configuration given for in-code configuration. Agent disabled."}
|
155
174
|
return
|
156
175
|
end
|
157
176
|
|
@@ -1,6 +1,21 @@
|
|
1
|
-
# Copyright (c)
|
1
|
+
# Copyright (c) 2023 SolarWinds, LLC.
|
2
2
|
# All rights reserved.
|
3
3
|
|
4
4
|
require_relative 'logger_formatter'
|
5
5
|
|
6
|
-
|
6
|
+
module SolarWindsAPM
|
7
|
+
module Lumberjack
|
8
|
+
module LogEntry
|
9
|
+
include SolarWindsAPM::Logger::Formatter # provides #insert_trace_id
|
10
|
+
|
11
|
+
def initialize(time, severity, message, progname, pid, tags)
|
12
|
+
super if SolarWindsAPM::Config[:log_traceId] == :never
|
13
|
+
|
14
|
+
message = insert_trace_id(message)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Lumberjack::LogEntry.prepend(SolarWindsAPM::Lumberjack::LogEntry) if SolarWindsAPM.loaded && defined?(Lumberjack::LogEntry)
|
@@ -107,7 +107,7 @@ module SolarWindsAPM
|
|
107
107
|
marginalia_job["class"] if marginalia_job.respond_to?(:[])
|
108
108
|
end
|
109
109
|
|
110
|
-
DEFAULT_LINES_TO_IGNORE_REGEX = %r{\.rvm|/ruby/gems/|vendor/|marginalia|rbenv|monitor\.rb.*mon_synchronize}
|
110
|
+
DEFAULT_LINES_TO_IGNORE_REGEX = %r{\.rvm|/ruby/gems/|vendor/|marginalia|rbenv|monitor\.rb.*mon_synchronize}
|
111
111
|
|
112
112
|
def self.line
|
113
113
|
SWOMarginalia::Comment.lines_to_ignore ||= DEFAULT_LINES_TO_IGNORE_REGEX
|
@@ -179,7 +179,7 @@ module SolarWindsAPM
|
|
179
179
|
span_id: span_context.hex_span_id,
|
180
180
|
trace_flags: trace_flag)
|
181
181
|
rescue NameError => e
|
182
|
-
SolarWindsAPM.logger.error {"[#{
|
182
|
+
SolarWindsAPM.logger.error {"[#{name}/#{__method__}] Couldn't find OpenTelemetry. Error: #{e.message}"}
|
183
183
|
end
|
184
184
|
|
185
185
|
if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('6.1')
|