solarwinds_apm 6.0.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
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