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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/ext/oboe_metal/extconf.rb +43 -42
  3. data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +1 -1
  4. data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +1 -1
  5. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +1 -1
  6. data/ext/oboe_metal/lib/liboboe-1.0-lambda-aarch64.so.sha256 +1 -0
  7. data/ext/oboe_metal/lib/liboboe-1.0-lambda-x86_64.so.sha256 +1 -0
  8. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +1 -1
  9. data/ext/oboe_metal/src/VERSION +1 -1
  10. data/ext/oboe_metal/src/oboe.h +3 -0
  11. data/ext/oboe_metal/src/oboe_api.cpp +1 -1
  12. data/lib/oboe_metal.rb +46 -84
  13. data/lib/rails/generators/solarwinds_apm/install_generator.rb +21 -19
  14. data/lib/solarwinds_apm/api/current_trace_info.rb +16 -9
  15. data/lib/solarwinds_apm/api/custom_metrics.rb +6 -4
  16. data/lib/solarwinds_apm/api/opentelemetry.rb +10 -6
  17. data/lib/solarwinds_apm/api/tracing.rb +7 -5
  18. data/lib/solarwinds_apm/api/transaction_name.rb +21 -11
  19. data/lib/solarwinds_apm/api.rb +8 -6
  20. data/lib/solarwinds_apm/config.rb +72 -47
  21. data/lib/solarwinds_apm/constants.rb +26 -26
  22. data/lib/solarwinds_apm/logger.rb +2 -0
  23. data/lib/solarwinds_apm/noop/README.md +1 -1
  24. data/lib/solarwinds_apm/noop/api.rb +85 -0
  25. data/lib/solarwinds_apm/noop/context.rb +15 -2
  26. data/lib/solarwinds_apm/noop/metadata.rb +7 -2
  27. data/lib/solarwinds_apm/{base.rb → noop/span.rb} +16 -15
  28. data/lib/solarwinds_apm/noop.rb +33 -0
  29. data/lib/solarwinds_apm/oboe_init_options.rb +50 -111
  30. data/lib/solarwinds_apm/opentelemetry/otlp_processor.rb +135 -0
  31. data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +66 -41
  32. data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +50 -52
  33. data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +30 -22
  34. data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +29 -16
  35. data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +136 -99
  36. data/lib/solarwinds_apm/opentelemetry.rb +8 -5
  37. data/lib/solarwinds_apm/otel_config.rb +38 -43
  38. data/lib/solarwinds_apm/otel_lambda_config.rb +53 -0
  39. data/lib/solarwinds_apm/patch/dummy_patch.rb +12 -0
  40. data/lib/solarwinds_apm/{thread_local.rb → patch.rb} +6 -22
  41. data/lib/solarwinds_apm/support/logger_formatter.rb +4 -2
  42. data/lib/solarwinds_apm/support/logging_log_event.rb +2 -0
  43. data/lib/solarwinds_apm/support/lumberjack_formatter.rb +2 -0
  44. data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +22 -22
  45. data/lib/solarwinds_apm/support/service_key_checker.rb +106 -0
  46. data/lib/solarwinds_apm/{support_report.rb → support/support_report.rb} +15 -10
  47. data/lib/solarwinds_apm/support/swomarginalia/comment.rb +18 -17
  48. data/lib/solarwinds_apm/support/swomarginalia/load_swomarginalia.rb +13 -12
  49. data/lib/solarwinds_apm/support/swomarginalia/railtie.rb +4 -2
  50. data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +3 -1
  51. data/lib/solarwinds_apm/support/transaction_cache.rb +6 -4
  52. data/lib/solarwinds_apm/support/transaction_settings.rb +7 -3
  53. data/lib/solarwinds_apm/support/txn_name_manager.rb +8 -3
  54. data/lib/solarwinds_apm/support/utils.rb +12 -9
  55. data/lib/solarwinds_apm/support/x_trace_options.rb +23 -17
  56. data/lib/solarwinds_apm/support.rb +28 -23
  57. data/lib/solarwinds_apm/version.rb +4 -2
  58. data/lib/solarwinds_apm.rb +82 -52
  59. 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 = "BucketCapacity".freeze
12
- INTERNAL_BUCKET_RATE = "BucketRate".freeze
13
- INTERNAL_SAMPLE_RATE = "SampleRate".freeze
14
- INTERNAL_SAMPLE_SOURCE = "SampleSource".freeze
15
- INTERNAL_SW_KEYS = "SWKeys".freeze
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 = "sw.w3c.tracestate".freeze
18
- SW_TRACESTATE_ROOT_KEY = "sw.tracestate_parent_id".freeze
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 = "auth".freeze
24
- XTRACEOPTIONS_RESP_IGNORED = "ignored".freeze
25
- XTRACEOPTIONS_RESP_TRIGGER_IGNORED = "ignored".freeze
26
- XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED = "not-requested".freeze
27
- XTRACEOPTIONS_RESP_TRIGGER_TRACE = "trigger-trace".freeze
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 {"[#{self.class}/#{__method__}] parent_span_context: #{parent_span_context.inspect}\n xtraceoptions: #{xtraceoptions.inspect}"}
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)? calculate_attributes(attributes, liboboe_decision, new_trace_state, parent_span_context, xtraceoptions) : nil
65
- sampling_result = ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: otel_decision,
66
- attributes: new_attributes,
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 {"[#{self.class}/#{__method__}] should_sample? end with sampling_result: #{sampling_result.inspect} from otel_decision: #{otel_decision.inspect} and new_attributes: #{new_attributes.inspect}"}
74
+
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?? '' : attributes['http.target']
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 {"[#{self.class}/#{__method__}] transaction cache: #{transaction_naming_key}; tracing_mode: #{tracing_mode}."}
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["trigger_trace"])
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
- do_metrics, do_sample, rate, source, bucket_rate,
129
- bucket_cap, decision_type, auth, status_msg, auth_msg,
130
- status = SolarWindsAPM::Context.getDecisions(*args)
131
-
132
- decision = {}
133
- decision["do_metrics"] = do_metrics > 0
134
- decision["do_sample"] = do_sample > 0
135
- decision["rate"] = rate
136
- decision["source"] = source
137
- decision["bucket_rate"] = bucket_rate
138
- decision["bucket_cap"] = bucket_cap
139
- decision["decision_type"] = decision_type
140
- decision["auth"] = auth
141
- decision["status_msg"] = status_msg
142
- decision["auth_msg"] = auth_msg
143
- decision["status"] = status
144
- decision
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["do_sample"]
151
- decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE # even if not do_metrics
152
- elsif liboboe_decision["do_metrics"]
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, liboboe_decision))
170
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] updated trace_state: #{trace_state.inspect}"}
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, parent_span_context, xtraceoptions))
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
- response << [XTRACEOPTIONS_RESP_AUTH, liboboe_decision['auth_msg']].join(w3c_sanitized) if xtraceoptions.signature && liboboe_decision['auth_msg']
198
-
199
- if !liboboe_decision["auth"] || liboboe_decision["auth"] < 1
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'] == 0 ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : liboboe_decision['status_msg']
204
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"}
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
- response << [XTRACEOPTIONS_RESP_IGNORED, xtraceoptions.ignored.join(w3c_sanitized_comma)].join(w3c_sanitized) unless xtraceoptions.ignored.empty?
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["bucket_cap"].to_s
236
- new_attributes[INTERNAL_BUCKET_RATE] = liboboe_decision["bucket_rate"].to_s
237
- new_attributes[INTERNAL_SAMPLE_RATE] = liboboe_decision["rate"]
238
- new_attributes[INTERNAL_SAMPLE_SOURCE] = liboboe_decision["source"]
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] = sw_value.split("-")[0] if sw_value && parent_span_context.remote?
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 {"[#{self.class}/#{__method__}] No valid traceparent or no tracestate - returning attributes: #{new_attributes}"}
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, parent_span_context)
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 {"[#{self.class}/#{__method__}] tracestate_capture #{tracestate_capture.inspect}; new_attributes #{new_attributes.inspect}"}
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, sw_from_span_and_decision(parent_span_context,liboboe_decision))
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 {"[#{self.class}/#{__method__}] trace_state_no_response #{trace_state_no_response.inspect}"}
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
- new_attributes[SW_TRACESTATE_CAPTURE_KEY] = Utils.trace_state_header(trace_state_no_response) if no_sw_count > 0
278
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_attributes after add_tracestate_capture_to_new_attributes: #{new_attributes.inspect}"}
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 = (liboboe_decision["do_sample"] == true) ? "01" : "00"
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 './opentelemetry/solarwinds_propagator'
11
- require_relative './opentelemetry/solarwinds_processor'
12
- require_relative './opentelemetry/solarwinds_sampler'
13
- require_relative './opentelemetry/solarwinds_exporter'
14
- require_relative './opentelemetry/solarwinds_response_propagator'
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 # only show the msg once
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__}] Agent disabled. No Trace exported."}
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 = {"trigger_trace" => SolarWindsAPM::Config[:trigger_tracing_mode]}
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["OpenTelemetry::Instrumentation::Rack"]
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 {"[#{name}/#{__method__}] Rack response propagator resolve failed. Provided type #{rack_setting[:response_propagators].class}, please provide Array e.g. [#{rack_setting[:response_propagators]}]"}
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["OpenTelemetry::Instrumentation::Rack"] = {response_propagators: [response_propagator]}
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 |config, value|
66
- SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] config: #{config} = #{value}"}
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 |config, value|
69
- SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] config_map: #{config} = #{value}"}
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 = SolarWindsAPM::OpenTelemetry::SolarWindsExporter.new(txn_manager: txn_manager)
76
- @@config[:span_processor] = SolarWindsAPM::OpenTelemetry::SolarWindsProcessor.new(exporter, txn_manager)
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? # rubocop:disable Style/GuardClause
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
- SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded."}
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
- ::OpenTelemetry::SDK.configure do |c|
119
- c.logger = ::Logger.new($stdout, level: ::SolarWindsAPM.logger.level) # sync solarwinds_apm logger to otel log
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 {"[#{name}/#{__method__}] Block not given while doing in-code configuration. Agent disabled."}
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 {"[#{name}/#{__method__}] No configuration given for in-code configuration. Agent disabled."}
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