solarwinds_apm 6.0.0 → 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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/ext/oboe_metal/extconf.rb +42 -41
  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 +30 -27
  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 +8 -4
  17. data/lib/solarwinds_apm/api/tracing.rb +6 -4
  18. data/lib/solarwinds_apm/api/transaction_name.rb +21 -11
  19. data/lib/solarwinds_apm/api.rb +7 -5
  20. data/lib/solarwinds_apm/config.rb +70 -46
  21. data/lib/solarwinds_apm/constants.rb +25 -23
  22. data/lib/solarwinds_apm/logger.rb +2 -0
  23. data/lib/solarwinds_apm/noop/api.rb +3 -1
  24. data/lib/solarwinds_apm/noop/context.rb +2 -0
  25. data/lib/solarwinds_apm/noop/metadata.rb +2 -0
  26. data/lib/solarwinds_apm/noop/span.rb +2 -0
  27. data/lib/solarwinds_apm/noop.rb +8 -6
  28. data/lib/solarwinds_apm/oboe_init_options.rb +47 -39
  29. data/lib/solarwinds_apm/opentelemetry/otlp_processor.rb +135 -0
  30. data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +66 -41
  31. data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +49 -51
  32. data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +30 -22
  33. data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +29 -16
  34. data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +135 -98
  35. data/lib/solarwinds_apm/opentelemetry.rb +8 -5
  36. data/lib/solarwinds_apm/otel_config.rb +32 -25
  37. data/lib/solarwinds_apm/otel_lambda_config.rb +53 -0
  38. data/lib/solarwinds_apm/patch.rb +2 -0
  39. data/lib/solarwinds_apm/support/logger_formatter.rb +4 -2
  40. data/lib/solarwinds_apm/support/logging_log_event.rb +2 -0
  41. data/lib/solarwinds_apm/support/lumberjack_formatter.rb +2 -0
  42. data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +2 -0
  43. data/lib/solarwinds_apm/support/service_key_checker.rb +18 -6
  44. data/lib/solarwinds_apm/support/support_report.rb +5 -3
  45. data/lib/solarwinds_apm/support/swomarginalia/comment.rb +18 -17
  46. data/lib/solarwinds_apm/support/swomarginalia/load_swomarginalia.rb +13 -12
  47. data/lib/solarwinds_apm/support/swomarginalia/railtie.rb +4 -2
  48. data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +3 -1
  49. data/lib/solarwinds_apm/support/transaction_cache.rb +6 -4
  50. data/lib/solarwinds_apm/support/transaction_settings.rb +7 -3
  51. data/lib/solarwinds_apm/support/txn_name_manager.rb +8 -3
  52. data/lib/solarwinds_apm/support/utils.rb +12 -9
  53. data/lib/solarwinds_apm/support/x_trace_options.rb +23 -17
  54. data/lib/solarwinds_apm/support.rb +28 -24
  55. data/lib/solarwinds_apm/version.rb +3 -1
  56. data/lib/solarwinds_apm.rb +44 -34
  57. metadata +14 -23
@@ -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
@@ -10,13 +12,13 @@ module SolarWindsAPM
10
12
  # ResponsePropagator
11
13
  # response propagator error will be rescued by OpenTelemetry::Instrumentation::Rack::Middlewares::EventHandler
12
14
  class TextMapPropagator
13
- HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers".freeze
14
- XTRACE_HEADER_NAME = "x-trace".freeze
15
- XTRACEOPTIONS_RESPONSE_HEADER_NAME = "x-trace-options-response".freeze
16
- INTL_SWO_EQUALS = "=".freeze
15
+ HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = 'Access-Control-Expose-Headers'
16
+ XTRACE_HEADER_NAME = 'x-trace'
17
+ XTRACEOPTIONS_RESPONSE_HEADER_NAME = 'x-trace-options-response'
18
+ INTL_SWO_EQUALS = '='
17
19
 
18
20
  private_constant \
19
- :HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, :XTRACE_HEADER_NAME,
21
+ :HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, :XTRACE_HEADER_NAME,
20
22
  :XTRACEOPTIONS_RESPONSE_HEADER_NAME
21
23
 
22
24
  def extract(carrier, context: ::OpenTelemetry::Context.current, getter: ::OpenTelemetry::Context::Propagation.text_map_getter) # rubocop:disable Lint/UnusedMethodArgument
@@ -30,22 +32,29 @@ module SolarWindsAPM
30
32
  # @param [optional Setter] setter If the optional setter is provided, it
31
33
  # will be used to write context into the carrier, otherwise the default
32
34
  # text map setter will be used.
33
- def inject(carrier, context: ::OpenTelemetry::Context.current, setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
34
-
35
+ def inject(carrier, context: ::OpenTelemetry::Context.current,
36
+ setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
35
37
  span_context = ::OpenTelemetry::Trace.current_span(context).context
36
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] context: #{context.inspect}; span_context: #{span_context.inspect}"}
38
+ SolarWindsAPM.logger.debug do
39
+ "[#{self.class}/#{__method__}] context: #{context.inspect}; span_context: #{span_context.inspect}"
40
+ end
37
41
  return unless span_context&.valid?
38
-
42
+
39
43
  x_trace = Utils.traceparent_from_context(span_context)
40
44
  exposed_headers = [XTRACE_HEADER_NAME]
41
45
  xtraceoptions_response = recover_response_from_tracestate(span_context.tracestate)
42
46
 
43
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] x-trace: #{x_trace}; exposed headers: #{exposed_headers.inspect}; x-trace-options-response: #{xtraceoptions_response}"}
47
+ SolarWindsAPM.logger.debug do
48
+ "[#{self.class}/#{__method__}] x-trace: #{x_trace}; exposed headers: #{exposed_headers.inspect}; x-trace-options-response: #{xtraceoptions_response}"
49
+ end
44
50
  exposed_headers.append(XTRACEOPTIONS_RESPONSE_HEADER_NAME) unless xtraceoptions_response.empty?
45
51
 
46
52
  setter.set(carrier, XTRACE_HEADER_NAME, x_trace)
47
- setter.set(carrier, XTRACEOPTIONS_RESPONSE_HEADER_NAME, xtraceoptions_response) unless xtraceoptions_response.empty?
48
- setter.set(carrier, HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, exposed_headers.join(","))
53
+ unless xtraceoptions_response.empty?
54
+ setter.set(carrier, XTRACEOPTIONS_RESPONSE_HEADER_NAME,
55
+ xtraceoptions_response)
56
+ end
57
+ setter.set(carrier, HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, exposed_headers.join(','))
49
58
  end
50
59
 
51
60
  # Returns the predefined propagation fields. If your carrier is reused, you
@@ -61,10 +70,14 @@ module SolarWindsAPM
61
70
  # sw_xtraceoptions_response_key -> xtrace_options_response
62
71
  def recover_response_from_tracestate(tracestate)
63
72
  sanitized = tracestate.value(XTraceOptions.sw_xtraceoptions_response_key)
64
- sanitized = "" if sanitized.nil?
65
- sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED, SolarWindsAPM::Constants::INTL_SWO_EQUALS)
66
- sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED, SolarWindsAPM::Constants::INTL_SWO_COMMA)
67
- SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] recover_response_from_tracestate sanitized: #{sanitized.inspect}"}
73
+ sanitized = '' if sanitized.nil?
74
+ sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED,
75
+ SolarWindsAPM::Constants::INTL_SWO_EQUALS)
76
+ sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED,
77
+ SolarWindsAPM::Constants::INTL_SWO_COMMA)
78
+ SolarWindsAPM.logger.debug do
79
+ "[#{self.class}/#{__method__}] recover_response_from_tracestate sanitized: #{sanitized.inspect}"
80
+ end
68
81
  sanitized
69
82
  end
70
83
  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
@@ -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 = SolarWindsAPM::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)
@@ -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,20 +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."}
24
+ SolarWindsAPM.logger.warn { "[#{name}/#{__method__}] SolarWindsAPM disabled. No Trace exported. Reason: #{reason}" }
23
25
  end
24
26
 
25
27
  def self.resolve_sampler
26
- sampler_config = {"trigger_trace" => SolarWindsAPM::Config[:trigger_tracing_mode]}
28
+ sampler_config = { 'trigger_trace' => SolarWindsAPM::Config[:trigger_tracing_mode] }
27
29
  @@config[:sampler] =
28
30
  ::OpenTelemetry::SDK::Trace::Samplers.parent_based(
29
31
  root: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config),
30
32
  remote_parent_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config),
31
- remote_parent_not_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config))
33
+ remote_parent_not_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config)
34
+ )
32
35
  end
33
36
 
34
37
  #
@@ -36,18 +39,20 @@ module SolarWindsAPM
36
39
  #
37
40
  def self.resolve_response_propagator
38
41
  response_propagator = SolarWindsAPM::OpenTelemetry::SolarWindsResponsePropagator::TextMapPropagator.new
39
- rack_setting = @@config_map["OpenTelemetry::Instrumentation::Rack"]
40
-
42
+ rack_setting = @@config_map['OpenTelemetry::Instrumentation::Rack']
43
+
41
44
  if rack_setting
42
45
  if rack_setting[:response_propagators].instance_of?(Array)
43
46
  rack_setting[:response_propagators].append(response_propagator)
44
47
  elsif rack_setting[:response_propagators].nil?
45
48
  rack_setting[:response_propagators] = [response_propagator]
46
49
  else
47
- 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
48
53
  end
49
54
  else
50
- @@config_map["OpenTelemetry::Instrumentation::Rack"] = {response_propagators: [response_propagator]}
55
+ @@config_map['OpenTelemetry::Instrumentation::Rack'] = { response_propagators: [response_propagator] }
51
56
  end
52
57
  end
53
58
 
@@ -56,19 +61,20 @@ module SolarWindsAPM
56
61
  end
57
62
 
58
63
  def self.print_config
59
- @@config.each do |k,v|
60
- SolarWindsAPM.logger.debug {"[#{name}/#{__method__}] Config Key/Value: #{k}, #{v.class}"}
64
+ @@config.each do |k, v|
65
+ SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] Config Key/Value: #{k}, #{v.class}" }
61
66
  end
62
- @@config_map.each do |k,v|
63
- SolarWindsAPM.logger.debug {"[#{name}/#{__method__}] Config Key/Value: #{k}, #{v}"}
67
+ @@config_map.each do |k, v|
68
+ SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] Config Key/Value: #{k}, #{v}" }
64
69
  end
65
70
  nil
66
71
  end
67
72
 
68
73
  def self.resolve_solarwinds_processor
69
74
  txn_manager = SolarWindsAPM::TxnNameManager.new
70
- exporter = SolarWindsAPM::OpenTelemetry::SolarWindsExporter.new(txn_manager: txn_manager)
71
- @@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)
72
78
  end
73
79
 
74
80
  def self.resolve_solarwinds_propagator
@@ -77,21 +83,17 @@ module SolarWindsAPM
77
83
 
78
84
  def self.validate_propagator(propagators)
79
85
  if propagators.nil?
80
- disable_agent
86
+ disable_agent(reason: 'propagators are invaliad.')
81
87
  return
82
88
  end
83
89
 
84
- SolarWindsAPM.logger.debug {"[#{name}/#{__method__}] propagators: #{propagators.map(&:class)}"}
85
- unless ([::OpenTelemetry::Trace::Propagation::TraceContext::TextMapPropagator, ::OpenTelemetry::Baggage::Propagation::TextMapPropagator] - propagators.map(&:class)).empty? # rubocop:disable Style/GuardClause
86
- SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Missing tracecontext propagator."}
87
- disable_agent
88
- 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?
89
92
  end
90
93
 
91
94
  def self.initialize
92
95
  unless defined?(::OpenTelemetry::SDK::Configurator)
93
- SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded."}
94
- disable_agent
96
+ disable_agent(reason: 'missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded.')
95
97
  return
96
98
  end
97
99
 
@@ -119,6 +121,7 @@ module SolarWindsAPM
119
121
  ::OpenTelemetry.propagation.instance_variable_get(:@propagators).append(@@config[:propagators])
120
122
 
121
123
  # append our processors (with our exporter)
124
+ ::OpenTelemetry.tracer_provider.add_span_processor(@@config[:metrics_processor])
122
125
  ::OpenTelemetry.tracer_provider.add_span_processor(@@config[:span_processor])
123
126
 
124
127
  # configure sampler afterwards
@@ -140,14 +143,18 @@ module SolarWindsAPM
140
143
  #
141
144
  def self.initialize_with_config
142
145
  unless block_given?
143
- 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
144
149
  return
145
150
  end
146
151
 
147
152
  yield @@config_map
148
153
 
149
154
  if @@config_map.empty?
150
- 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
151
158
  return
152
159
  end
153
160