solarwinds_apm 6.1.2 → 7.0.0.prev1

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/lib/solarwinds_apm/api/current_trace_info.rb +10 -6
  4. data/lib/solarwinds_apm/api/custom_metrics.rb +8 -25
  5. data/lib/solarwinds_apm/api/tracing.rb +12 -27
  6. data/lib/solarwinds_apm/api/transaction_name.rb +6 -10
  7. data/lib/solarwinds_apm/config.rb +1 -1
  8. data/lib/solarwinds_apm/constants.rb +1 -0
  9. data/lib/solarwinds_apm/noop/api.rb +5 -2
  10. data/lib/solarwinds_apm/noop.rb +0 -24
  11. data/lib/solarwinds_apm/opentelemetry/otlp_processor.rb +90 -69
  12. data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +0 -2
  13. data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +5 -4
  14. data/lib/solarwinds_apm/opentelemetry.rb +5 -7
  15. data/lib/solarwinds_apm/otel_native_config.rb +177 -0
  16. data/lib/solarwinds_apm/patch/README.md +15 -0
  17. data/lib/solarwinds_apm/{noop/metadata.rb → sampling/dice.rb} +19 -17
  18. data/lib/solarwinds_apm/sampling/http_sampler.rb +87 -0
  19. data/lib/solarwinds_apm/sampling/json_sampler.rb +52 -0
  20. data/lib/solarwinds_apm/sampling/metrics.rb +38 -0
  21. data/lib/solarwinds_apm/sampling/oboe_sampler.rb +348 -0
  22. data/lib/solarwinds_apm/sampling/sampler.rb +197 -0
  23. data/lib/solarwinds_apm/sampling/sampling_constants.rb +127 -0
  24. data/lib/solarwinds_apm/sampling/sampling_patch.rb +49 -0
  25. data/lib/solarwinds_apm/sampling/setting_example.txt +1 -0
  26. data/lib/solarwinds_apm/{noop/context.rb → sampling/settings.rb} +14 -25
  27. data/lib/solarwinds_apm/sampling/token_bucket.rb +126 -0
  28. data/lib/solarwinds_apm/sampling/trace_options.rb +100 -0
  29. data/lib/solarwinds_apm/{patch.rb → sampling.rb} +20 -4
  30. data/lib/solarwinds_apm/{noop/span.rb → support/aws_resource_detector.rb} +5 -18
  31. data/lib/solarwinds_apm/support/logger_formatter.rb +1 -1
  32. data/lib/solarwinds_apm/support/logging_log_event.rb +1 -1
  33. data/lib/solarwinds_apm/support/lumberjack_formatter.rb +1 -1
  34. data/lib/solarwinds_apm/support/otlp_endpoint.rb +99 -0
  35. data/lib/solarwinds_apm/support/resource_detector/aws/beanstalk.rb +51 -0
  36. data/lib/solarwinds_apm/support/resource_detector/aws/ec2.rb +145 -0
  37. data/lib/solarwinds_apm/support/resource_detector/aws/ecs.rb +173 -0
  38. data/lib/solarwinds_apm/support/resource_detector/aws/eks.rb +174 -0
  39. data/lib/solarwinds_apm/support/resource_detector/aws/lambda.rb +66 -0
  40. data/lib/solarwinds_apm/support/resource_detector.rb +192 -0
  41. data/lib/solarwinds_apm/support/service_key_checker.rb +12 -6
  42. data/lib/solarwinds_apm/support/transaction_settings.rb +6 -0
  43. data/lib/solarwinds_apm/support/txn_name_manager.rb +54 -9
  44. data/lib/solarwinds_apm/support/utils.rb +9 -0
  45. data/lib/solarwinds_apm/support.rb +3 -4
  46. data/lib/solarwinds_apm/version.rb +4 -4
  47. data/lib/solarwinds_apm.rb +27 -73
  48. metadata +99 -40
  49. data/ext/oboe_metal/extconf.rb +0 -168
  50. data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +0 -1
  51. data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +0 -1
  52. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +0 -1
  53. data/ext/oboe_metal/lib/liboboe-1.0-lambda-aarch64.so.sha256 +0 -1
  54. data/ext/oboe_metal/lib/liboboe-1.0-lambda-x86_64.so.sha256 +0 -1
  55. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +0 -1
  56. data/ext/oboe_metal/src/VERSION +0 -1
  57. data/ext/oboe_metal/src/bson/bson.h +0 -220
  58. data/ext/oboe_metal/src/bson/platform_hacks.h +0 -91
  59. data/ext/oboe_metal/src/init_solarwinds_apm.cc +0 -18
  60. data/ext/oboe_metal/src/oboe.h +0 -930
  61. data/ext/oboe_metal/src/oboe_api.cpp +0 -793
  62. data/ext/oboe_metal/src/oboe_api.h +0 -621
  63. data/ext/oboe_metal/src/oboe_debug.h +0 -17
  64. data/ext/oboe_metal/src/oboe_swig_wrap.cc +0 -11045
  65. data/lib/oboe_metal.rb +0 -187
  66. data/lib/solarwinds_apm/cert/star.appoptics.com.issuer.crt +0 -24
  67. data/lib/solarwinds_apm/oboe_init_options.rb +0 -222
  68. data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +0 -239
  69. data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +0 -174
  70. data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +0 -333
  71. data/lib/solarwinds_apm/otel_config.rb +0 -174
  72. data/lib/solarwinds_apm/otel_lambda_config.rb +0 -56
  73. data/lib/solarwinds_apm/patch/dummy_patch.rb +0 -12
  74. data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +0 -33
  75. data/lib/solarwinds_apm/support/support_report.rb +0 -99
  76. data/lib/solarwinds_apm/support/transaction_cache.rb +0 -57
  77. data/lib/solarwinds_apm/support/x_trace_options.rb +0 -138
@@ -1,174 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # © 2023 SolarWinds Worldwide, LLC. All rights reserved.
4
- #
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
6
- #
7
- # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
-
9
- module SolarWindsAPM
10
- module OpenTelemetry
11
- # reference: OpenTelemetry::SDK::Trace::SpanProcessor
12
- class SolarWindsProcessor
13
- HTTP_METHOD = 'http.method'
14
- HTTP_ROUTE = 'http.route'
15
- HTTP_STATUS_CODE = 'http.status_code'
16
- HTTP_URL = 'http.url'
17
- LIBOBOE_HTTP_SPAN_STATUS_UNAVAILABLE = 0
18
-
19
- attr_reader :txn_manager
20
-
21
- def initialize(txn_manager)
22
- @txn_manager = txn_manager
23
- end
24
-
25
- # Called when a {Span} is started, if the {Span#recording?}
26
- # returns true.
27
- #
28
- # @param [Span] span the {Span} that just started.
29
- # @param [Context] parent_context the parent {Context} of the newly
30
- # started span.
31
- def on_start(span, parent_context)
32
- SolarWindsAPM.logger.debug do
33
- "[#{self.class}/#{__method__}] processor on_start span: #{span.to_span_data.inspect}, parent_context: #{parent_context.inspect}"
34
- end
35
-
36
- return if non_entry_span(parent_context: parent_context)
37
-
38
- trace_flags = span.context.trace_flags.sampled? ? '01' : '00'
39
- @txn_manager.set_root_context_h(span.context.hex_trace_id, "#{span.context.hex_span_id}-#{trace_flags}")
40
- span.add_attributes({ 'sw.is_entry_span' => true })
41
- rescue StandardError => e
42
- SolarWindsAPM.logger.info { "[#{self.class}/#{__method__}] processor on_start error: #{e.message}" }
43
- end
44
-
45
- # Called when a {Span} is ended, if the {Span#recording?}
46
- # returns true.
47
- #
48
- # @param [Span] span the {Span} that just ended.
49
- def on_finish(span)
50
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] processor on_finish span: #{span.to_span_data.inspect}" }
51
-
52
- return if non_entry_span(span: span)
53
-
54
- span_time = calculate_span_time(start_time: span.start_timestamp, end_time: span.end_timestamp)
55
- domain = nil
56
- has_error = error?(span)
57
- trans_name = calculate_transaction_names(span)
58
- if span_http?(span)
59
- status_code = get_http_status_code(span)
60
- request_method = span.attributes[HTTP_METHOD]
61
- url_tran = span.attributes[HTTP_URL]
62
-
63
- SolarWindsAPM.logger.debug do
64
- "[#{self.class}/#{__method__}] createHttpSpan with\n
65
- trans_name: #{trans_name}\n
66
- url_tran: #{url_tran}\n
67
- domain: #{domain}\n
68
- span_time: #{span_time}\n
69
- status_code: #{status_code}\n
70
- request_method: #{request_method}\n
71
- has_error: #{has_error}"
72
- end
73
-
74
- liboboe_txn_name = SolarWindsAPM::Span.createHttpSpan(trans_name, url_tran, domain, span_time, status_code,
75
- request_method, has_error)
76
-
77
- else
78
-
79
- SolarWindsAPM.logger.debug do
80
- "[#{self.class}/#{__method__}] createSpan with \n
81
- trans_name: #{trans_name}\n
82
- domain: #{domain}\n
83
- span_time: #{span_time}\n
84
- has_error: #{has_error}"
85
- end
86
-
87
- liboboe_txn_name = SolarWindsAPM::Span.createSpan(trans_name, domain, span_time, has_error)
88
- end
89
-
90
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] liboboe_txn_name: #{liboboe_txn_name}" }
91
- if span.context.trace_flags.sampled?
92
- @txn_manager["#{span.context.hex_trace_id}-#{span.context.hex_span_id}"] =
93
- liboboe_txn_name
94
- end
95
- @txn_manager.delete_root_context_h(span.context.hex_trace_id)
96
- rescue StandardError => e
97
- SolarWindsAPM.logger.info do
98
- "[#{self.class}/#{__method__}] solarwinds_processor on_finish error: #{e.message}"
99
- end
100
- end
101
-
102
- # @param [optional Numeric] timeout An optional timeout in seconds.
103
- # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
104
- # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
105
- def force_flush(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
106
- ::OpenTelemetry::SDK::Trace::Export::SUCCESS
107
- end
108
-
109
- # @param [optional Numeric] timeout An optional timeout in seconds.
110
- # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
111
- # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
112
- def shutdown(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
113
- ::OpenTelemetry::SDK::Trace::Export::SUCCESS
114
- end
115
-
116
- private
117
-
118
- # This span from inbound HTTP request if from a SERVER by some http.method
119
- def span_http?(span)
120
- span.kind == ::OpenTelemetry::Trace::SpanKind::SERVER && !span.attributes[HTTP_METHOD].nil?
121
- end
122
-
123
- # Calculate if this span instance has_error
124
- # return [Integer]
125
- def error?(span)
126
- span.status.code == ::OpenTelemetry::Trace::Status::ERROR ? 1 : 0
127
- end
128
-
129
- # Calculate HTTP status_code from span or default to UNAVAILABLE
130
- # Something went wrong in OTel or instrumented service crashed early
131
- # if no status_code in attributes of HTTP span
132
- def get_http_status_code(span)
133
- span.attributes[HTTP_STATUS_CODE] || LIBOBOE_HTTP_SPAN_STATUS_UNAVAILABLE
134
- end
135
-
136
- # check if it's entry span based on no parent or parent is remote
137
- def non_entry_span(span: nil, parent_context: nil)
138
- if parent_context
139
- parent_span = ::OpenTelemetry::Trace.current_span(parent_context)
140
- parent_span && parent_span.context != ::OpenTelemetry::Trace::SpanContext::INVALID && parent_span.context.remote? == false
141
- elsif span
142
- span.attributes['sw.is_entry_span'] != true
143
- end
144
- end
145
-
146
- # Get trans_name and url_tran of this span instance.
147
- # Predecessor order: custom SDK > env var SW_APM_TRANSACTION_NAME > automatic naming
148
- def calculate_transaction_names(span)
149
- trace_span_id = "#{span.context.hex_trace_id}-#{span.context.hex_span_id}"
150
- trans_name = @txn_manager.get(trace_span_id)
151
- if trans_name
152
- SolarWindsAPM.logger.debug do
153
- "[#{self.class}/#{__method__}] found trans name from txn_manager: #{trans_name} by #{trace_span_id}"
154
- end
155
- @txn_manager.del(trace_span_id)
156
- elsif ENV.key?('SW_APM_TRANSACTION_NAME') && ENV['SW_APM_TRANSACTION_NAME'] != ''
157
- trans_name = ENV.fetch('SW_APM_TRANSACTION_NAME', nil)
158
- else
159
- trans_name = span.attributes[HTTP_ROUTE] || nil
160
- trans_name = span.name if span.name && (trans_name.nil? || trans_name.empty?)
161
- end
162
- trans_name
163
- end
164
-
165
- # Calculate span time in microseconds (us) using start and end time
166
- # in nanoseconds (ns). OTel span start/end_time are optional.
167
- def calculate_span_time(start_time: nil, end_time: nil)
168
- return 0 if start_time.nil? || end_time.nil?
169
-
170
- ((end_time.to_i - start_time.to_i) / 1e3).round
171
- end
172
- end
173
- end
174
- end
@@ -1,333 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # © 2023 SolarWinds Worldwide, LLC. All rights reserved.
4
- #
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
6
- #
7
- # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
-
9
- module SolarWindsAPM
10
- module OpenTelemetry
11
- # SolarWindsSampler
12
- class SolarWindsSampler
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'
18
- LIBOBOE_CONTINUED = -1
19
- SW_TRACESTATE_CAPTURE_KEY = 'sw.w3c.tracestate'
20
- SW_TRACESTATE_ROOT_KEY = 'sw.tracestate_parent_id'
21
- UNSET = -1
22
- SWO_TRACING_ENABLED = 1
23
- SWO_TRACING_DISABLED = 0
24
- SWO_TRACING_UNSET = -1
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'
30
-
31
- attr_reader :description
32
-
33
- def initialize(config = {})
34
- @config = config
35
- end
36
-
37
- def ==(other)
38
- @decision == other.decision && @description == other.description
39
- end
40
-
41
- # @api private
42
- #
43
- # See {Samplers}.
44
- # trace_id
45
- # parent_context: OpenTelemetry::Context
46
- def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
47
- SolarWindsAPM.logger.debug do
48
- "[#{self.class}/#{__method__}] should_sample? start parameters \n
49
- trace_id: #{trace_id.unpack1('H*')}\n
50
- parent_context: #{parent_context}\n
51
- parent_context.inspect: #{parent_context.inspect}\n
52
- links: #{links}\n
53
- name: #{name}\n
54
- kind: #{kind}\n
55
- attributes: #{attributes}"
56
- end
57
-
58
- SolarWindsAPM::Reporter.send(:report_init) # This only happens if after_fork enabled
59
-
60
- parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
61
- xtraceoptions = ::SolarWindsAPM::XTraceOptions.new(parent_context)
62
- SolarWindsAPM.logger.debug do
63
- "[#{self.class}/#{__method__}] parent_span_context: #{parent_span_context.inspect}\n xtraceoptions: #{xtraceoptions.inspect}"
64
- end
65
-
66
- liboboe_decision = calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
67
- otel_decision = otel_decision_from_liboboe(liboboe_decision)
68
- new_trace_state = calculate_trace_state(liboboe_decision, parent_span_context, xtraceoptions)
69
- new_attributes = if otel_sampled?(otel_decision)
70
- calculate_attributes(attributes, liboboe_decision,
71
- new_trace_state, parent_span_context, xtraceoptions)
72
- end
73
- sampling_result = ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: otel_decision,
74
- attributes: new_attributes,
75
- tracestate: new_trace_state)
76
-
77
- SolarWindsAPM.logger.debug do
78
- "[#{self.class}/#{__method__}] should_sample? end with sampling_result: #{sampling_result.inspect} from otel_decision: #{otel_decision.inspect} and new_attributes: #{new_attributes.inspect}"
79
- end
80
- sampling_result
81
- rescue StandardError => e
82
- SolarWindsAPM.logger.info { "[#{self.class}/#{__method__}] sampler error: #{e.message}" }
83
- ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP,
84
- attributes: attributes,
85
- tracestate: ::OpenTelemetry::Trace::Tracestate::DEFAULT)
86
- end
87
-
88
- protected
89
-
90
- attr_reader :decision
91
-
92
- private
93
-
94
- ##
95
- # use parent_span_context and xtraceoptions object to feed to liboboe function getDecisions that get liboboe_decision
96
- # name, kind and attributes are used for transaction filter caching (to avoid continous calculate_trace_mode calculation)
97
- # return decision Hash
98
- def calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
99
- tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
100
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] tracestring: #{tracestring}" }
101
-
102
- # otel-ruby contrib use different key to store url info, currently it's using http.target for path
103
- url_path = attributes.nil? ? '' : attributes['http.target']
104
- transaction_naming_key = "#{url_path}-#{name}-#{kind}"
105
- tracing_mode = SolarWindsAPM::TransactionCache.get(transaction_naming_key)
106
- SolarWindsAPM.logger.debug do
107
- "[#{self.class}/#{__method__}] transaction cache: #{transaction_naming_key}; tracing_mode: #{tracing_mode}."
108
- end
109
-
110
- unless tracing_mode
111
- trans_settings = SolarWindsAPM::TransactionSettings.new(url_path: url_path, name: name, kind: kind)
112
- tracing_mode = trans_settings.calculate_trace_mode == 1 ? SWO_TRACING_ENABLED : SWO_TRACING_DISABLED
113
- SolarWindsAPM::TransactionCache.set(transaction_naming_key, tracing_mode)
114
- end
115
-
116
- sw_member_value = parent_span_context.tracestate[SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY]
117
- trigger_trace_mode = SolarWindsAPM::OboeTracingMode.get_oboe_trigger_trace_mode(@config['trigger_trace'])
118
- sample_rate = UNSET
119
- options = xtraceoptions&.options
120
- trigger_trace = xtraceoptions&.intify_trigger_trace || 0
121
- signature = xtraceoptions&.signature
122
- timestamp = xtraceoptions&.timestamp
123
-
124
- SolarWindsAPM.logger.debug do
125
- "[#{self.class}/#{__method__}] get liboboe decision parameters: \n
126
- tracestring: #{tracestring}\n
127
- sw_member_value: #{sw_member_value}\n
128
- tracing_mode: #{tracing_mode}\n
129
- sample_rate: #{sample_rate}\n
130
- trigger_trace: #{trigger_trace}\n
131
- trigger_trace_mode: #{trigger_trace_mode}\n
132
- options: #{options}\n
133
- signature: #{signature}\n
134
- timestamp: #{timestamp}"
135
- end
136
-
137
- args = [tracestring, sw_member_value, tracing_mode, sample_rate,
138
- trigger_trace, trigger_trace_mode, options, signature, timestamp]
139
-
140
- if SolarWindsAPM::OboeInitOptions.instance.lambda_env
141
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] get decision from oboe_api" }
142
- # trigger trace disabled at this point (https://swicloud.atlassian.net/wiki/spaces/NIT/pages/3753116438/AWS+Lambda+Instrumentation+POC#Concerns)
143
- do_metrics, do_sample, rate, source, bucket_rate,
144
- bucket_cap, decision_type, auth, status_msg, auth_msg,
145
- status = SolarWindsAPM.oboe_api.getTracingDecision(*args)
146
- else
147
- do_metrics, do_sample, rate, source, bucket_rate,
148
- bucket_cap, decision_type, auth, status_msg, auth_msg,
149
- status = SolarWindsAPM::Context.getDecisions(*args)
150
- end
151
-
152
- {
153
- 'do_metrics' => do_metrics.positive?,
154
- 'do_sample' => do_sample.positive?,
155
- 'rate' => rate,
156
- 'source' => source,
157
- 'bucket_rate' => bucket_rate,
158
- 'bucket_cap' => bucket_cap,
159
- 'decision_type' => decision_type,
160
- 'auth' => auth,
161
- 'status_msg' => status_msg,
162
- 'auth_msg' => auth_msg,
163
- 'status' => status
164
- }
165
- end
166
-
167
- def otel_decision_from_liboboe(liboboe_decision)
168
- decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP
169
- if liboboe_decision['do_sample']
170
- decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE # even if not do_metrics
171
- elsif liboboe_decision['do_metrics']
172
- decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_ONLY
173
- end
174
- SolarWindsAPM.logger.debug { "otel decision: #{decision} created from liboboe_decision: #{liboboe_decision}" }
175
- decision
176
- end
177
-
178
- ##
179
- # add sw=value and xtrace_options_response=value into the old/new tracestate
180
- # the returned value tracestate will be used in propagating to next services
181
- def calculate_trace_state(liboboe_decision, parent_span_context, xtraceoptions)
182
- if !parent_span_context.valid? || parent_span_context.tracestate.nil?
183
- trace_state = create_new_trace_state(parent_span_context, liboboe_decision)
184
- else
185
- trace_state = parent_span_context.tracestate.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY,
186
- sw_from_span_and_decision(parent_span_context,
187
- liboboe_decision))
188
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] updated trace_state: #{trace_state.inspect}" }
189
- end
190
-
191
- # for setting up the xtrace_options_response
192
- if xtraceoptions&.options
193
- trace_state = trace_state.set_value(XTraceOptions.sw_xtraceoptions_response_key.to_s,
194
- create_xtraceoptions_response_value(liboboe_decision,
195
- parent_span_context, xtraceoptions))
196
- end
197
- trace_state
198
- end
199
-
200
- ##
201
- #
202
- def create_new_trace_state(parent_span_context, liboboe_decision)
203
- decision = sw_from_span_and_decision(parent_span_context, liboboe_decision)
204
- trace_state = ::OpenTelemetry::Trace::Tracestate.from_hash({ SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => decision }) # e.g. sw=3e222c863a04123a-01
205
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] created new trace_state: #{trace_state.inspect}" }
206
- trace_state
207
- end
208
-
209
- ##
210
- #
211
- def create_xtraceoptions_response_value(liboboe_decision, parent_span_context, xtraceoptions)
212
- response = []
213
- w3c_sanitized = SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED
214
- w3c_sanitized_comma = SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED
215
- if xtraceoptions.signature && liboboe_decision['auth_msg']
216
- response << [XTRACEOPTIONS_RESP_AUTH,
217
- liboboe_decision['auth_msg']].join(w3c_sanitized)
218
- end
219
-
220
- if !liboboe_decision['auth'] || liboboe_decision['auth'] < 1
221
- if xtraceoptions.trigger_trace
222
- # If a traceparent header was provided then oboe does not generate the message
223
- tracestring = Utils.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
224
- trigger_msg = tracestring && (liboboe_decision['decision_type']).zero? ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : liboboe_decision['status_msg']
225
- SolarWindsAPM.logger.debug do
226
- "[#{self.class}/#{__method__}] tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"
227
- end
228
- else
229
- trigger_msg = XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED
230
- end
231
-
232
- response << [XTRACEOPTIONS_RESP_TRIGGER_TRACE, trigger_msg].join(w3c_sanitized) # e.g. response << trigger-trace####ok
233
- end
234
-
235
- # appending ignored value from xtraceoptions to response. e.g. response << ignored####invalidkeys,invalidkeys,invalidkeys
236
- unless xtraceoptions.ignored.empty?
237
- response << [XTRACEOPTIONS_RESP_IGNORED,
238
- xtraceoptions.ignored.join(w3c_sanitized_comma)].join(w3c_sanitized)
239
- end
240
- joined_response = response.join(';')
241
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] final response_value: #{joined_response}" }
242
- joined_response
243
- end
244
-
245
- ##
246
- # calculate_attributes is used for getting the otel Result class in last step of sampler should_sample?
247
- # e.g. result = Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
248
- # calculate_attributes use new_trace_state that is derived from current span information and old tracestate from parent_span_context.tracestate
249
- # the sw.w3c.tracestate should perserve the old tracestate value for debugging purpose
250
- def calculate_attributes(attributes, liboboe_decision, trace_state, parent_span_context, xtraceoptions)
251
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] new_trace_state: #{trace_state.inspect}" }
252
- new_attributes = attributes.dup || {}
253
-
254
- # Always (root or is_remote) set _INTERNAL_SW_KEYS if injected
255
- new_attributes[INTERNAL_SW_KEYS] = xtraceoptions.sw_keys if xtraceoptions.sw_keys
256
-
257
- # Always (root or is_remote) set custom KVs if extracted from x-trace-options
258
- xtraceoptions.custom_kvs&.each { |k, v| new_attributes[k] = v }
259
-
260
- # Always (root or is_remote) set service entry internal KVs
261
- new_attributes[INTERNAL_BUCKET_CAPACITY] = liboboe_decision['bucket_cap'].to_s
262
- new_attributes[INTERNAL_BUCKET_RATE] = liboboe_decision['bucket_rate'].to_s
263
- new_attributes[INTERNAL_SAMPLE_RATE] = liboboe_decision['rate']
264
- new_attributes[INTERNAL_SAMPLE_SOURCE] = liboboe_decision['source']
265
-
266
- # set sw.tracestate_parent_id if its tracestate contains "sw"
267
- sw_value = parent_span_context.tracestate.value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY)
268
- new_attributes[SW_TRACESTATE_ROOT_KEY] = sw_value.split('-')[0] if sw_value && parent_span_context.remote?
269
-
270
- # If unsigned or signed TT (root or is_remote), set TriggeredTrace
271
- new_attributes[SolarWindsAPM::Constants::INTERNAL_TRIGGERED_TRACE] = true if xtraceoptions.trigger_trace
272
-
273
- # Trace's root span has no valid traceparent nor tracestate so we can't calculate remaining attributes
274
- if !parent_span_context.valid? || trace_state.nil?
275
- SolarWindsAPM.logger.debug do
276
- "[#{self.class}/#{__method__}] No valid traceparent or no tracestate - returning attributes: #{new_attributes}"
277
- end
278
- return new_attributes.freeze || nil
279
- end
280
-
281
- new_attributes = add_tracestate_capture_to_new_attributes(new_attributes, liboboe_decision, trace_state,
282
- parent_span_context)
283
- new_attributes.freeze
284
- end
285
-
286
- ##
287
- #
288
- def add_tracestate_capture_to_new_attributes(new_attributes, liboboe_decision, trace_state, parent_span_context)
289
- tracestate_capture = new_attributes[SW_TRACESTATE_CAPTURE_KEY]
290
- SolarWindsAPM.logger.debug do
291
- "[#{self.class}/#{__method__}] tracestate_capture #{tracestate_capture.inspect}; new_attributes #{new_attributes.inspect}"
292
- end
293
-
294
- if tracestate_capture.nil?
295
- trace_state_no_response = trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
296
- else
297
- # retain all potential tracestate pairs for attributes and generate new sw=key for tracestate based on root parent_span_id
298
- attr_trace_state = ::OpenTelemetry::Trace::Tracestate.from_string(tracestate_capture)
299
- new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY,
300
- sw_from_span_and_decision(parent_span_context,
301
- liboboe_decision))
302
- trace_state_no_response = new_attr_trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
303
- end
304
- SolarWindsAPM.logger.debug do
305
- "[#{self.class}/#{__method__}] trace_state_no_response #{trace_state_no_response.inspect}"
306
- end
307
-
308
- trace_state_no_response = parent_span_context.tracestate.delete(XTraceOptions.sw_xtraceoptions_response_key)
309
- no_sw_count = trace_state_no_response.to_h.count { |k, _v| k != 'sw' }
310
-
311
- if no_sw_count.positive?
312
- new_attributes[SW_TRACESTATE_CAPTURE_KEY] =
313
- Utils.trace_state_header(trace_state_no_response)
314
- end
315
- SolarWindsAPM.logger.debug do
316
- "[#{self.class}/#{__method__}] new_attributes after add_tracestate_capture_to_new_attributes: #{new_attributes.inspect}"
317
- end
318
-
319
- new_attributes
320
- end
321
-
322
- # formats tracestate sw value from span_id and liboboe decision as 16-byte span_id with 8-bit trace_flags e.g. 1a2b3c4d5e6f7g8h-01
323
- def sw_from_span_and_decision(parent_span_context, liboboe_decision)
324
- trace_flag = liboboe_decision['do_sample'] == true ? '01' : '00'
325
- [parent_span_context.hex_span_id, trace_flag].join('-')
326
- end
327
-
328
- def otel_sampled?(otel_decision)
329
- otel_decision == ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE
330
- end
331
- end
332
- end
333
- end
@@ -1,174 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # © 2023 SolarWinds Worldwide, LLC. All rights reserved.
4
- #
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
6
- #
7
- # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
-
9
- module SolarWindsAPM
10
- # OTelConfig module
11
- # For configure otel component: configurable: propagator, exporter
12
- # non-config: sampler, processor, response_propagator
13
- # Level of this configuration: SolarWindsOTel::Config -> OboeOption -> SolarWindsOTel::OTelConfig
14
- module OTelConfig
15
- @@config = {}
16
- @@config_map = {}
17
-
18
- @@agent_enabled = true
19
-
20
- def self.disable_agent(reason: nil)
21
- return unless @@agent_enabled # only show the msg once
22
-
23
- @@agent_enabled = false
24
- SolarWindsAPM.logger.warn { "[#{name}/#{__method__}] SolarWindsAPM disabled. No Trace exported. Reason: #{reason}" }
25
- end
26
-
27
- def self.resolve_sampler
28
- sampler_config = { 'trigger_trace' => SolarWindsAPM::Config[:trigger_tracing_mode] }
29
- @@config[:sampler] =
30
- ::OpenTelemetry::SDK::Trace::Samplers.parent_based(
31
- root: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config),
32
- remote_parent_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config),
33
- remote_parent_not_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(sampler_config)
34
- )
35
- end
36
-
37
- #
38
- # append/add solarwinds_response_propagator into rack instrumentation
39
- #
40
- def self.resolve_response_propagator
41
- response_propagator = SolarWindsAPM::OpenTelemetry::SolarWindsResponsePropagator::TextMapPropagator.new
42
- rack_setting = @@config_map['OpenTelemetry::Instrumentation::Rack']
43
-
44
- if rack_setting
45
- if rack_setting[:response_propagators].instance_of?(Array)
46
- rack_setting[:response_propagators].append(response_propagator)
47
- elsif rack_setting[:response_propagators].nil?
48
- rack_setting[:response_propagators] = [response_propagator]
49
- else
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
53
- end
54
- else
55
- @@config_map['OpenTelemetry::Instrumentation::Rack'] = { response_propagators: [response_propagator] }
56
- end
57
- end
58
-
59
- def self.[](key)
60
- @@config[key.to_sym]
61
- end
62
-
63
- def self.print_config
64
- @@config.each do |k, v|
65
- SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] Config Key/Value: #{k}, #{v.class}" }
66
- end
67
- @@config_map.each do |k, v|
68
- SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] Config Key/Value: #{k}, #{v}" }
69
- end
70
- nil
71
- end
72
-
73
- def self.resolve_solarwinds_processor
74
- txn_manager = SolarWindsAPM::TxnNameManager.new
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)
78
- end
79
-
80
- def self.resolve_solarwinds_propagator
81
- @@config[:propagators] = SolarWindsAPM::OpenTelemetry::SolarWindsPropagator::TextMapPropagator.new
82
- end
83
-
84
- def self.validate_propagator(propagators)
85
- if propagators.nil?
86
- disable_agent(reason: 'propagators are invaliad.')
87
- return
88
- end
89
-
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?
92
- end
93
-
94
- def self.initialize
95
- unless defined?(::OpenTelemetry::SDK::Configurator)
96
- disable_agent(reason: 'missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded.')
97
- return
98
- end
99
-
100
- resolve_sampler
101
- resolve_solarwinds_propagator
102
- resolve_solarwinds_processor
103
- resolve_response_propagator
104
-
105
- print_config if SolarWindsAPM.logger.level.zero?
106
-
107
- # resolve OTEL environmental variables
108
- ENV['OTEL_TRACES_EXPORTER'] = 'none' if ENV['OTEL_TRACES_EXPORTER'].to_s.empty?
109
- if ENV['OTEL_LOG_LEVEL'].to_s.empty?
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)
112
- end
113
-
114
- # for dbo, traceparent injection as comments
115
- require_relative 'patch/tag_sql_patch' if SolarWindsAPM::Config[:tag_sql]
116
-
117
- ::OpenTelemetry::SDK.configure { |c| c.use_all(@@config_map) }
118
-
119
- validate_propagator(::OpenTelemetry.propagation.instance_variable_get(:@propagators))
120
-
121
- return unless @@agent_enabled
122
-
123
- # append our propagators
124
- ::OpenTelemetry.propagation.instance_variable_get(:@propagators).append(@@config[:propagators])
125
-
126
- # append our processors (with our exporter)
127
- ::OpenTelemetry.tracer_provider.add_span_processor(@@config[:metrics_processor])
128
- ::OpenTelemetry.tracer_provider.add_span_processor(@@config[:span_processor])
129
-
130
- # configure sampler afterwards
131
- ::OpenTelemetry.tracer_provider.sampler = @@config[:sampler]
132
-
133
- if ENV['SW_APM_AUTO_CONFIGURE'] == 'false'
134
- SolarWindsAPM.logger.info '==================================================================='
135
- SolarWindsAPM.logger.info "\e[1mSolarWindsAPM manual initialization was successful.\e[0m"
136
- SolarWindsAPM.logger.info '==================================================================='
137
- end
138
-
139
- nil
140
- end
141
-
142
- #
143
- # Allow initialize after set new value to SolarWindsAPM::Config[:key]=value
144
- #
145
- # Usage:
146
- #
147
- # Default using the use_all to load all instrumentation
148
- # But with specific instrumentation disabled, use {:enabled: false} in config
149
- # SolarWindsAPM::OTelConfig.initialize_with_config do |config|
150
- # config["OpenTelemetry::Instrumentation::Rack"] = {"a" => "b"}
151
- # config["OpenTelemetry::Instrumentation::Dalli"] = {:enabled: false}
152
- # end
153
- #
154
- def self.initialize_with_config
155
- unless block_given?
156
- SolarWindsAPM.logger.warn do
157
- "[#{name}/#{__method__}] Block not given while doing in-code configuration. Agent disabled."
158
- end
159
- return
160
- end
161
-
162
- yield @@config_map
163
-
164
- if @@config_map.empty?
165
- SolarWindsAPM.logger.warn do
166
- "[#{name}/#{__method__}] No configuration given for in-code configuration. Agent disabled."
167
- end
168
- return
169
- end
170
-
171
- initialize
172
- end
173
- end
174
- end