solarwinds_apm 6.0.0.preV1 → 6.0.0.preV3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +28 -54
  3. data/ext/oboe_metal/extconf.rb +23 -23
  4. data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +1 -1
  5. data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +1 -1
  6. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +1 -1
  7. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +1 -1
  8. data/ext/oboe_metal/src/VERSION +1 -2
  9. data/ext/oboe_metal/src/init_solarwinds_apm.cc +0 -6
  10. data/ext/oboe_metal/src/oboe.h +0 -12
  11. data/ext/oboe_metal/src/oboe_swig_wrap.cc +5 -5
  12. data/lib/oboe_metal.rb +3 -3
  13. data/lib/rails/generators/solarwinds_apm/install_generator.rb +0 -3
  14. data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +8 -25
  15. data/lib/solarwinds_apm/api/current_trace_info.rb +1 -2
  16. data/lib/solarwinds_apm/api/opentelemetry.rb +39 -0
  17. data/lib/solarwinds_apm/api/transaction_name.rb +18 -17
  18. data/lib/solarwinds_apm/api.rb +2 -0
  19. data/lib/solarwinds_apm/config.rb +21 -63
  20. data/lib/solarwinds_apm/constants.rb +2 -4
  21. data/lib/solarwinds_apm/noop/context.rb +1 -1
  22. data/lib/solarwinds_apm/oboe_init_options.rb +11 -16
  23. data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +92 -87
  24. data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +27 -23
  25. data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +28 -28
  26. data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +9 -14
  27. data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +132 -175
  28. data/lib/solarwinds_apm/otel_config.rb +55 -36
  29. data/lib/solarwinds_apm/support/lumberjack_formatter.rb +17 -2
  30. data/lib/solarwinds_apm/support/swomarginalia/comment.rb +2 -2
  31. data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +6 -5
  32. data/lib/solarwinds_apm/support/transaction_cache.rb +27 -2
  33. data/lib/solarwinds_apm/support/transaction_settings.rb +10 -10
  34. data/lib/solarwinds_apm/support/txn_name_manager.rb +34 -17
  35. data/lib/solarwinds_apm/support/utils.rb +24 -0
  36. data/lib/solarwinds_apm/support.rb +4 -2
  37. data/lib/solarwinds_apm/version.rb +1 -1
  38. data/lib/solarwinds_apm.rb +3 -4
  39. metadata +18 -33
  40. data/lib/oboe.rb +0 -7
  41. data/lib/solarwinds_apm/noop/profiling.rb +0 -17
  42. 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 {"[#{self.class}/#{__method__}] should_sample? parameters \n
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
- # if the upstream has tracestate: sw=.... then it should capture it
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
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] liboboe_decision: #{liboboe_decision.inspect}"}
59
-
60
- otel_decision = otel_decision_from_liboboe(liboboe_decision)
61
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] otel_decision: #{otel_decision.inspect}"}
62
-
63
- new_trace_state = calculate_trace_state(liboboe_decision,parent_span_context,xtraceoptions)
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 = ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
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
- # return Hash
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
- tracestring = nil
85
- if parent_span_context.valid? && parent_span_context.remote?
86
- tracestring = Transformer.traceparent_from_context(parent_span_context)
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
- if tracing_mode.nil?
98
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache NOT found: #{transaction_naming_key}."}
99
- trans_settings = SolarWindsAPM::TransactionSettings.new(url: url, name: name, kind: kind)
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
- options = nil
113
- trigger_trace = 0
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
- args = [tracestring,sw_member_value,tracing_mode,sample_rate,trigger_trace,trigger_trace_mode,options,signature,timestamp]
135
- do_metrics, do_sample, rate, source, bucket_rate, \
136
- bucket_cap, decision_type, auth, status_msg, auth_msg, status = SolarWindsAPM::Context.getDecisions(*args)
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 {"OTel decision created: #{decision}"}
149
+ SolarWindsAPM.logger.debug {"otel decision: #{decision} created from liboboe_decision: #{liboboe_decision}"}
162
150
  decision
163
151
  end
164
152
 
165
- def create_xtraceoptions_response_value(decision, parent_span_context, xtraceoptions)
166
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] create_xtraceoptions_response_value decision[auth]: #{decision['auth']}; decision[auth_msg]: #{decision['auth_msg']}; xtraceoptions.trigger_trace: #{xtraceoptions.trigger_trace}"}
167
-
168
- response = []
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
- trace_state = create_new_trace_state(parent_span_context, decision)
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
- parent_trace_state = parent_span_context.tracestate
220
- if parent_trace_state.nil?
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
- XTraceOptions.sw_xtraceoptions_response_key.to_s,
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
- # SW_TRACESTATE_CAPTURE_KEY = "sw.w3c.tracestate"
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
- # tracestate_capture seems always nil because attributes_dict never have the SW_TRACESTATE_CAPTURE_KEY (sw.w3c.tracestate)
250
- # since tracestate_capture is always nil, so the sw always have new value for sw=key
251
- if tracestate_capture.nil?
252
- trace_state_no_response = trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
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
- else
255
- # Must retain all potential tracestate pairs for attributes
256
- attr_trace_state = ::OpenTelemetry::Trace::Tracestate.from_string(tracestate_capture)
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
- # This step generated the new sw=key for tracestate based on root parent_span_id
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
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] trace_state_no_response #{trace_state_no_response.inspect}"}
265
-
266
- # other approach
267
- trace_state_no_response = parent_span_context.tracestate.delete(XTraceOptions.sw_xtraceoptions_response_key)
268
- no_sw_count = trace_state_no_response.to_h.reject { |k, _v| k == "sw" }.count
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? e.g. result = Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
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
- def calculate_attributes(attributes, decision, trace_state, parent_span_context, xtraceoptions)
281
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Received attributes: #{attributes.inspect}; decision:#{decision.inspect}; trace_state:#{trace_state.inspect}; parent_span_context:#{parent_span_context.inspect}; xtraceoptions:#{xtraceoptions.inspect}"}
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] = decision["bucket_cap"].to_s
300
- new_attributes[INTERNAL_BUCKET_RATE] = decision["bucket_rate"].to_s
301
- new_attributes[INTERNAL_SAMPLE_RATE] = decision["rate"]
302
- new_attributes[INTERNAL_SAMPLE_SOURCE] = decision["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
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_attributes sw_value: #{sw_value.inspect} parent_span_context.tracestate #{parent_span_context.tracestate.inspect}"}
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 = add_tracestate_capture_to_attributes_dict(new_attributes,decision,trace_state,parent_span_context)
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
- def sw_from_span_and_decision(parent_span_context, decision)
325
- trace_flag = Transformer.trace_flags_from_boolean(decision["do_sample"])
326
- Transformer.sw_from_span_and_decision(parent_span_context.hex_span_id, trace_flag)
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 {"[#{self.name}/#{__method__}] Agent disabled. No Trace exported."}
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.resolve_for_response_propagator
46
- response_propagator = SolarWindsAPM::OpenTelemetry::SolarWindsResponsePropagator::TextMapPropagator.new
47
- if @@config_map["OpenTelemetry::Instrumentation::Rack"]
48
- if @@config_map["OpenTelemetry::Instrumentation::Rack"][:response_propagators].instance_of?(Array)
49
- @@config_map["OpenTelemetry::Instrumentation::Rack"][:response_propagators].append(response_propagator)
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
- @@config_map["OpenTelemetry::Instrumentation::Rack"][:response_propagators] = [response_propagator]
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 {"[#{self.name}/#{__method__}] config: #{config} = #{value}"}
82
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] config: #{config} = #{value}"}
65
83
  end
66
84
  @@config_map.each do |config, value|
67
- SolarWindsAPM.logger.warn {"[#{self.name}/#{__method__}] config_map: #{config} = #{value}"}
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::OpenTelemetry::TxnNameManager.new
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 {"[#{self.name}/#{__method__}] propagators: #{propagators.map(&:class)}"}
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 {"[#{self.name}/#{__method__}] Missing tracecontext propagator."}
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 {"[#{self.name}/#{__method__}] missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded."}
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
- resolve_for_response_propagator
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'].nil?
114
- ::OpenTelemetry::SDK.configure do |c|
115
- c.use_all(@@config_map)
116
- end
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 {"[#{self.name}/#{__method__}] Block not given while doing in-code configuration. Agent disabled."}
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 {"[#{self.name}/#{__method__}] No configuration given for in-code configuration. Agent disabled."}
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) 2019 SolarWinds, LLC.
1
+ # Copyright (c) 2023 SolarWinds, LLC.
2
2
  # All rights reserved.
3
3
 
4
4
  require_relative 'logger_formatter'
5
5
 
6
- Lumberjack::Formatter.prepend(SolarWindsAPM::Logger::Formatter) if SolarWindsAPM.loaded && defined?(Lumberjack::Formatter)
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}.freeze
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 {"[#{self.name}/#{__method__}] Couldn't find OpenTelemetry. Error: #{e.message}"}
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')