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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73f2befcea2c555995f79719d36c68d2448ae31f56656741d5c6e8d49e61963b
4
- data.tar.gz: 4ba3986285dcda8adc5629c2e08b5221638928d06733dd6fc0c009379b97ab4b
3
+ metadata.gz: 2d66514bebfccf2f3c5a2b2e6924fe7fc7abe6a33fa90ce2dcc94de968e64018
4
+ data.tar.gz: 55cd6df597c8db22130b6322ede1deae4911c1409674734cb11ccf9223734dcb
5
5
  SHA512:
6
- metadata.gz: 777aebc61cfb1f6776f0640f9b0cbd6005c5c375989a046809ce4f97551a8f2925154496eeef798726ac2242641646bb590c48d23caf71165473b0ac0cfa6b52
7
- data.tar.gz: c244cb0d3c1252cbcb85f3381cc82195ca8d59bf0ea4991f2c41f89a49d29a92efcf089ee35ae6274614493bd7e89d7ecb5851ec876a572d24fc5015ec7d5625
6
+ metadata.gz: fa5dd5bb46399211d816833f257b69fec64a325cb6ee3c256d612d64be0e4cb50e5b1e195025afad69dee4d5c79726d9e5b6ff8f5a25d952724b01521e8d9331
7
+ data.tar.gz: 6b9e3f5bc1e8e35b419d9a2c04c43bca23268ea9fed3832368d5f0c708925abd1e46d9f2e215833fae5e6154fa77c7ec696e3819a0ff37188c1c407aa4965522
data/README.md CHANGED
@@ -89,6 +89,7 @@ add_tracer :method_name, 'custom_span_name', { attributes: { 'any' => 'attribute
89
89
  For example, if you want to instrument class or instance method `create_session` inside an application controller:
90
90
 
91
91
  To instrument instance method
92
+
92
93
  ```ruby
93
94
  class SessionsController < ApplicationController
94
95
  include SolarWindsAPM::API::Tracer
@@ -107,6 +108,7 @@ end
107
108
  ```
108
109
 
109
110
  To instrument class method
111
+
110
112
  ```ruby
111
113
  class SessionsController < ApplicationController
112
114
  def create
@@ -142,10 +144,10 @@ trace.trace_flags # 01
142
144
 
143
145
  On startup, this library initializes and maintains a connection to a SolarWinds Observability collector, and receives settings used for making tracing decisions. This process can take up to a few seconds depending on the connection. If the application receives requests before initialization has completed, these requests will not be traced. While this is not critical for long-running server processes, it might be a problem for short-running apps such as cron jobs or CLI apps.
144
146
 
145
- A call to the `solarwinds_ready` method allows the application to block until initialization has completed and the library is ready for tracing. The method accepts an optional timeout parameter in milliseconds.
147
+ A call to the `solarwinds_ready?` method allows the application to block until initialization has completed and the library is ready for tracing. The method accepts an optional timeout parameter in milliseconds.
146
148
 
147
149
  ```ruby
148
- SolarWindsAPM::API.solarwinds_ready(wait_milliseconds=3000)
150
+ SolarWindsAPM::API.solarwinds_ready?(wait_milliseconds=3000)
149
151
  ```
150
152
 
151
153
  #### Set a Custom Transaction Name
@@ -111,12 +111,16 @@ module SolarWindsAPM
111
111
  private
112
112
 
113
113
  def current_span
114
- span = ::OpenTelemetry::Trace.current_span if defined?(::OpenTelemetry::Trace)
115
- trace_id = span.context.hex_trace_id
116
- span_id = span.context.hex_span_id
117
- trace_flags = span.context.trace_flags.sampled? ? '01' : '00'
118
- tracestring = "00-#{trace_id}-#{span_id}-#{trace_flags}"
119
- [trace_id, span_id, trace_flags, tracestring]
114
+ if defined?(::OpenTelemetry::Trace)
115
+ span = ::OpenTelemetry::Trace.current_span
116
+ trace_id = span.context.hex_trace_id
117
+ span_id = span.context.hex_span_id
118
+ trace_flags = span.context.trace_flags.sampled? ? '01' : '00'
119
+ tracestring = "00-#{trace_id}-#{span_id}-#{trace_flags}"
120
+ [trace_id, span_id, trace_flags, tracestring]
121
+ else
122
+ %w[00000000000000000000000000000000 00000000 00 00-00000000000000000000000000000000-00000000-00]
123
+ end
120
124
  end
121
125
 
122
126
  # if true the trace info should be added to the log message
@@ -32,12 +32,9 @@ module SolarWindsAPM
32
32
  # === Returns:
33
33
  # * Boolean
34
34
  #
35
- def increment_metric(name, count = 1, with_hostname = false, tags_kvs = {}) # rubocop:disable Style/OptionalBooleanParameter
36
- return true unless SolarWindsAPM.loaded
37
-
38
- with_hostname = with_hostname ? 1 : 0
39
- tags, tags_count = make_tags(tags_kvs)
40
- SolarWindsAPM::CustomMetrics.increment(name.to_s, count, with_hostname, nil, tags, tags_count).zero?
35
+ def increment_metric(_name, _count = 1, _with_hostname = false, _tags_kvs = {}) # rubocop:disable Style/OptionalBooleanParameter
36
+ SolarWindsAPM.logger.warn { 'increment_metric have been deprecated. Please use opentelemetry metrics-sdk to log metrics data.' }
37
+ false
41
38
  end
42
39
 
43
40
  # Send values with counts
@@ -66,29 +63,15 @@ module SolarWindsAPM
66
63
  # === Returns:
67
64
  # * Boolean
68
65
  #
69
- def summary_metric(name, value, count = 1, with_hostname = false, tags_kvs = {}) # rubocop:disable Style/OptionalBooleanParameter
70
- return true unless SolarWindsAPM.loaded
71
-
72
- with_hostname = with_hostname ? 1 : 0
73
- tags, tags_count = make_tags(tags_kvs)
74
- SolarWindsAPM::CustomMetrics.summary(name.to_s, value, count, with_hostname, nil, tags, tags_count).zero?
66
+ def summary_metric(_name, _value, _count = 1, _with_hostname = false, _tags_kvs = {}) # rubocop:disable Style/OptionalBooleanParameter
67
+ SolarWindsAPM.logger.warn { 'summary_metric have been deprecated. Please use opentelemetry metrics-sdk to log metrics data.' }
68
+ false
75
69
  end
76
70
 
77
71
  private
78
72
 
79
- def make_tags(tags_kvs)
80
- unless tags_kvs.is_a?(Hash)
81
- SolarWindsAPM.logger.warn("[solarwinds_apm/metrics] CustomMetrics received tags_kvs that are not a Hash (found #{tags_kvs.class}), setting tags_kvs = {}")
82
- tags_kvs = {}
83
- end
84
- count = tags_kvs.size
85
- tags = SolarWindsAPM::MetricTags.new(count)
86
-
87
- tags_kvs.each_with_index do |(k, v), i|
88
- tags.add(i, k.to_s, v.to_s)
89
- end
90
-
91
- [tags, count]
73
+ def make_tags(_tags_kvs)
74
+ nil
92
75
  end
93
76
  end
94
77
  end
@@ -12,22 +12,12 @@ module SolarWindsAPM
12
12
  # Wait for SolarWinds to be ready to send traces.
13
13
  #
14
14
  # This may be useful in short lived background processes when it is important to capture
15
- # information during the whole time the process is running. It returns boolean if <tt>integer_response</tt> is false,
16
- # and it will return integer if setting <tt>integer_response</tt> as true.
15
+ # information during the whole time the process is running.
17
16
  # Usually SolarWinds doesn't block an application while it is starting up.
18
17
  #
19
- # For status code reference:
20
- # 0: unknown error
21
- # 1: is ready
22
- # 2: not ready yet, try later
23
- # 3: limit exceeded
24
- # 4: invalid API key
25
- # 5: connection error
26
- #
27
18
  # === Argument:
28
19
  #
29
20
  # * +wait_milliseconds+ - (int, default 3000) the maximum time to wait in milliseconds
30
- # * +integer_response+ - (boolean, default false) determine whether return status code of reporter or not
31
21
  #
32
22
  # === Example:
33
23
  #
@@ -35,24 +25,19 @@ module SolarWindsAPM
35
25
  # Logger.info "SolarWindsAPM not ready after 10 seconds, no metrics will be sent"
36
26
  # end
37
27
  #
38
- # # with status code print out
39
- # status = SolarWindsAPM::API.solarwinds_ready?(10_000, integer_response: true)
40
- # unless status == 1
41
- # Logger.info "SolarWindsAPM not ready after 10 seconds, no metrics will be sent. Error code "#{status}"
42
- # end
43
- #
44
28
  # === Returns:
45
- # * Boolean (if integer_response: false)
46
- # * Integer (if integer_response: true)
47
- #
48
- def solarwinds_ready?(wait_milliseconds = 3000, integer_response: false)
49
- return false unless SolarWindsAPM.loaded
50
-
51
- is_ready = SolarWindsAPM::Context.isReady(wait_milliseconds)
52
-
53
- return is_ready if integer_response
29
+ # * Boolean
30
+ #
31
+ def solarwinds_ready?(wait_milliseconds = 3000, integer_response: nil)
32
+ unless integer_response.nil?
33
+ SolarWindsAPM.logger.warn do
34
+ 'Deprecation: solarwinds_ready? no longer accepts integer_response, this parameter will be removed in the next release.'
35
+ end
36
+ end
54
37
 
55
- is_ready == 1
38
+ root_sampler = ::OpenTelemetry.tracer_provider.sampler.instance_variable_get(:@root)
39
+ is_ready = root_sampler.wait_until_ready(wait_milliseconds / 1000)
40
+ !!is_ready
56
41
  end
57
42
  end
58
43
  end
@@ -39,23 +39,19 @@ module SolarWindsAPM
39
39
  #
40
40
  def set_transaction_name(custom_name = nil)
41
41
  status = true
42
- if ENV.fetch('SW_APM_ENABLED', 'true') == 'false' ||
43
- SolarWindsAPM::Context.toString == '99-00000000000000000000000000000000-0000000000000000-00'
44
- # library disabled or noop, just log and skip work.
45
- # TODO: can we have a single indicator that the API is in noop mode?
42
+ if ENV.fetch('SW_APM_ENABLED', 'true') == 'false'
46
43
  SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] SolarWindsAPM is in disabled or noop mode." }
47
- elsif custom_name.nil? || custom_name.empty?
44
+ elsif SolarWindsAPM::OTelNativeConfig[:metrics_processor].nil?
48
45
  SolarWindsAPM.logger.warn do
49
- "[#{name}/#{__method__}] Set transaction name failed: custom_name is either nil or empty string."
46
+ "[#{name}/#{__method__}] Set transaction name failed: Solarwinds processor is missing. Noop mode."
50
47
  end
51
- status = false
52
- elsif SolarWindsAPM::OTelConfig[:metrics_processor].nil?
48
+ elsif custom_name.nil? || custom_name.empty?
53
49
  SolarWindsAPM.logger.warn do
54
- "[#{name}/#{__method__}] Set transaction name failed: Solarwinds processor is missing."
50
+ "[#{name}/#{__method__}] Set transaction name failed: custom_name is either nil or empty string."
55
51
  end
56
52
  status = false
57
53
  else
58
- solarwinds_processor = SolarWindsAPM::OTelConfig[:metrics_processor]
54
+ solarwinds_processor = SolarWindsAPM::OTelNativeConfig[:metrics_processor]
59
55
  current_span = ::OpenTelemetry::Trace.current_span
60
56
 
61
57
  if current_span.context.valid?
@@ -212,7 +212,7 @@ module SolarWindsAPM
212
212
 
213
213
  # Assure value is an integer
214
214
  @@config[key.to_sym] = new_value.to_i
215
- SolarWindsAPM.sample_rate(new_value) if SolarWindsAPM.loaded
215
+ SolarWindsAPM.sample_rate(new_value)
216
216
 
217
217
  when :transaction_settings
218
218
  compile_settings(value)
@@ -32,5 +32,6 @@ module SolarWindsAPM
32
32
  INTL_SWO_OTEL_STATUS = 'otel.status_code'
33
33
  INTL_SWO_OTEL_STATUS_DESCRIPTION = 'otel.status_description'
34
34
  INTERNAL_TRIGGERED_TRACE = 'TriggeredTrace'
35
+ MAX_TXN_NAME_LENGTH = 256
35
36
  end
36
37
  end
@@ -19,8 +19,9 @@ module NoopAPI
19
19
  # Tracing
20
20
  module Tracing
21
21
  # (wait_milliseconds=3000, integer_response: false)
22
- def solarwinds_ready?(*_args, **options)
23
- options && options[:integer_response] ? 0 : false
22
+ def solarwinds_ready?(_wait_milliseconds = 3000, integer_response: false)
23
+ _noop = integer_response
24
+ false
24
25
  end
25
26
  end
26
27
 
@@ -55,10 +56,12 @@ module NoopAPI
55
56
  # CustomMetrics
56
57
  module CustomMetrics
57
58
  def increment_metric(*)
59
+ SolarWindsAPM.logger.warn { 'increment_metric have been deprecated. Please use opentelemetry metrics-sdk to log metrics data.' }
58
60
  false
59
61
  end
60
62
 
61
63
  def summary_metric(*)
64
+ SolarWindsAPM.logger.warn { 'summary_metric have been deprecated. Please use opentelemetry metrics-sdk to log metrics data.' }
62
65
  false
63
66
  end
64
67
  end
@@ -6,28 +6,4 @@
6
6
  #
7
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
8
 
9
- require_relative 'noop/context'
10
- require_relative 'noop/metadata'
11
- require_relative 'noop/span'
12
9
  require_relative 'noop/api'
13
-
14
- module SolarWindsAPM
15
- include Oboe_metal
16
- # Reporter noop
17
- class Reporter
18
- ##
19
- # noop version of :send_report
20
- #
21
- def self.send_report(event, with_system_timestamp: false); end
22
-
23
- ##
24
- # noop version of :send_status
25
- #
26
- def self.send_status(event, context = nil, with_system_timestamp: false); end
27
-
28
- ##
29
- # noop version of :start
30
- #
31
- def self.start; end
32
- end
33
- end
@@ -8,41 +8,60 @@
8
8
 
9
9
  module SolarWindsAPM
10
10
  module OpenTelemetry
11
- # reference: OpenTelemetry::SDK::Trace::SpanProcessor; inheritance: SolarWindsProcessor
12
- class OTLPProcessor < SolarWindsProcessor
13
- attr_accessor :description
14
-
15
- def initialize
16
- super(nil)
17
- @meters = init_meters
18
- @metrics = init_metrics
11
+ # reference: OpenTelemetry::SDK::Trace::SpanProcessor
12
+ class OTLPProcessor
13
+ attr_reader :txn_manager
14
+
15
+ SW_TRANSACTION_NAME = 'sw.transaction'
16
+ SW_IS_ENTRY_SPAN = 'sw.is_entry_span'
17
+ SW_IS_ERROR = 'sw.is_error'
18
+
19
+ HTTP_METHOD = 'http.method'
20
+ HTTP_ROUTE = 'http.route'
21
+ HTTP_STATUS_CODE = 'http.status_code'
22
+ HTTP_URL = 'http.url'
23
+
24
+ INVALID_HTTP_STATUS_CODE = 0
25
+
26
+ def initialize(txn_manager)
27
+ @txn_manager = txn_manager
28
+ @meters = { 'sw.apm.request.metrics' => ::OpenTelemetry.meter_provider.meter('sw.apm.request.metrics') }
29
+ @metrics = { response_time: @meters['sw.apm.request.metrics'].create_histogram('trace.service.response_time', unit: 'ms', description: 'Duration of each entry span for the service, typically meaning the time taken to process an inbound request.') }
30
+ @transaction_name = nil
19
31
  end
20
32
 
21
- # @param [Span] span the {Span} that just started.
22
- # @param [Context] parent_context the
23
- # started span.
33
+ # @param [Span] span the (mutable) {Span} that just started.
34
+ # @param [Context] parent_context of the started span.
24
35
  def on_start(span, parent_context)
25
36
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] processor on_start span: #{span.to_span_data.inspect}" }
26
37
 
27
38
  return if non_entry_span(parent_context: parent_context)
28
39
 
29
- span.add_attributes(span_attributes(span))
30
- span.add_attributes({ 'sw.is_entry_span' => true })
40
+ trace_flags = span.context.trace_flags.sampled? ? '01' : '00'
41
+ @txn_manager&.set_root_context_h(span.context.hex_trace_id, "#{span.context.hex_span_id}-#{trace_flags}")
42
+ span.add_attributes({ SW_IS_ENTRY_SPAN => true })
31
43
  rescue StandardError => e
32
44
  SolarWindsAPM.logger.info { "[#{self.class}/#{__method__}] processor on_start error: #{e.message}" }
33
45
  end
34
46
 
35
- # @param [Span] span the {Span} that just ended.
47
+ def on_finishing(span)
48
+ @transaction_name = calculate_transaction_names(span)
49
+ span.set_attribute(SW_TRANSACTION_NAME, @transaction_name)
50
+ @txn_manager.delete_root_context_h(span.context.hex_trace_id)
51
+ end
52
+
53
+ # @param [Span] span the (immutable) {Span} that just ended.
36
54
  def on_finish(span)
37
55
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] processor on_finish span: #{span.to_span_data.inspect}" }
38
-
39
- # return if span is non-entry span
40
56
  return if non_entry_span(span: span)
41
57
 
42
58
  record_request_metrics(span)
43
- record_sampling_metrics
44
59
 
45
- ::OpenTelemetry.meter_provider.metric_readers.each(&:pull)
60
+ # pull should work on any instrument from oboe_sampler
61
+ ::OpenTelemetry.meter_provider.metric_readers.each do |reader|
62
+ reader.pull if reader.respond_to? :pull
63
+ end
64
+
46
65
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] processor on_finish succeed" }
47
66
  rescue StandardError => e
48
67
  SolarWindsAPM.logger.info { "[#{self.class}/#{__method__}] error processing span on_finish: #{e.message}" }
@@ -50,24 +69,10 @@ module SolarWindsAPM
50
69
 
51
70
  private
52
71
 
53
- # Create two meters for sampling and request count
54
- def init_meters
55
- {
56
- 'sw.apm.sampling.metrics' => ::OpenTelemetry.meter_provider.meter('sw.apm.sampling.metrics'),
57
- 'sw.apm.request.metrics' => ::OpenTelemetry.meter_provider.meter('sw.apm.request.metrics')
58
- }
59
- end
60
-
61
- def span_attributes(span)
62
- span_attrs = { 'sw.transaction' => calculate_lambda_transaction_name(span) }
63
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] span_attrs: #{span_attrs.inspect}" }
64
- span_attrs
65
- end
66
-
67
72
  def meter_attributes(span)
68
73
  meter_attrs = {
69
- 'sw.is_error' => error?(span) == 1,
70
- 'sw.transaction' => calculate_lambda_transaction_name(span)
74
+ SW_IS_ERROR => error?(span) == 1,
75
+ SW_TRANSACTION_NAME => @transaction_name
71
76
  }
72
77
 
73
78
  if span_http?(span)
@@ -76,26 +81,33 @@ module SolarWindsAPM
76
81
  meter_attrs['http.method'] = span.attributes[HTTP_METHOD] if span.attributes[HTTP_METHOD]
77
82
  end
78
83
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] meter_attrs: #{meter_attrs.inspect}" }
84
+ meter_attrs.compact!
79
85
  meter_attrs
80
86
  end
81
87
 
82
- def calculate_lambda_transaction_name(span)
83
- (ENV['SW_APM_TRANSACTION_NAME'] || ENV['AWS_LAMBDA_FUNCTION_NAME'] || span.name || 'unknown').slice(0, 255)
88
+ def calculate_lambda_transaction_name(span_name)
89
+ (ENV['SW_APM_TRANSACTION_NAME'] || ENV['AWS_LAMBDA_FUNCTION_NAME'] || span_name || 'unknown').slice(0, SolarWindsAPM::Constants::MAX_TXN_NAME_LENGTH)
84
90
  end
85
91
 
86
- def init_metrics
87
- request_meter = @meters['sw.apm.request.metrics']
88
- sampling_meter = @meters['sw.apm.sampling.metrics']
89
-
90
- metrics = {}
91
- metrics[:response_time] = request_meter.create_histogram('trace.service.response_time', unit: 'ms', description: 'measures the duration of an inbound HTTP request')
92
- metrics[:tracecount] = sampling_meter.create_counter('trace.service.tracecount')
93
- metrics[:samplecount] = sampling_meter.create_counter('trace.service.samplecount')
94
- metrics[:request_count] = sampling_meter.create_counter('trace.service.request_count')
95
- metrics[:toex_count] = sampling_meter.create_counter('trace.service.tokenbucket_exhaustion_count')
96
- metrics[:through_count] = sampling_meter.create_counter('trace.service.through_trace_count')
97
- metrics[:tt_count] = sampling_meter.create_counter('trace.service.triggered_trace_count')
98
- metrics
92
+ # Get trans_name and url_tran of this span instance.
93
+ # Predecessor order: custom SDK > env var SW_APM_TRANSACTION_NAME > automatic naming
94
+ def calculate_transaction_names(span)
95
+ return calculate_lambda_transaction_name(span.name) if SolarWindsAPM::Utils.determine_lambda
96
+
97
+ trace_span_id = "#{span.context.hex_trace_id}-#{span.context.hex_span_id}"
98
+ trans_name = @txn_manager.get(trace_span_id)
99
+ if trans_name
100
+ SolarWindsAPM.logger.debug do
101
+ "[#{self.class}/#{__method__}] found trans name from txn_manager: #{trans_name} by #{trace_span_id}"
102
+ end
103
+ @txn_manager.del(trace_span_id)
104
+ elsif !ENV['SW_APM_TRANSACTION_NAME'].to_s.empty?
105
+ trans_name = ENV.fetch('SW_APM_TRANSACTION_NAME', nil)
106
+ else
107
+ trans_name = span.attributes[HTTP_ROUTE] || nil
108
+ trans_name = span.name if trans_name.to_s.empty? && span.name
109
+ end
110
+ trans_name.to_s.slice(0, SolarWindsAPM::Constants::MAX_TXN_NAME_LENGTH)
99
111
  end
100
112
 
101
113
  def record_request_metrics(span)
@@ -106,31 +118,40 @@ module SolarWindsAPM
106
118
  @metrics[:response_time].record(span_time, attributes: meter_attrs)
107
119
  end
108
120
 
109
- # oboe_api will return 0 in case of failed operation, and report 0 value
110
- def record_sampling_metrics
111
- _, trace_count = SolarWindsAPM.oboe_api.consumeTraceCount
112
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] trace_count: #{trace_count}" }
113
- @metrics[:tracecount].add(trace_count)
121
+ # Calculate span time in microseconds (us) using start and end time
122
+ # in nanoseconds (ns). OTel span start/end_time are optional.
123
+ def calculate_span_time(start_time: nil, end_time: nil)
124
+ return 0 if start_time.nil? || end_time.nil?
114
125
 
115
- _, sample_count = SolarWindsAPM.oboe_api.consumeSampleCount
116
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] sample_count: #{sample_count}" }
117
- @metrics[:samplecount].add(sample_count)
126
+ ((end_time.to_i - start_time.to_i) / 1e3).round
127
+ end
118
128
 
119
- _, request_count = SolarWindsAPM.oboe_api.consumeRequestCount
120
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] request_count: #{request_count}" }
121
- @metrics[:request_count].add(request_count)
129
+ # Calculate if this span instance has_error
130
+ # return [Integer]
131
+ def error?(span)
132
+ span.status.code == ::OpenTelemetry::Trace::Status::ERROR ? 1 : 0
133
+ end
122
134
 
123
- _, token_bucket_exhaustion_count = SolarWindsAPM.oboe_api.consumeTokenBucketExhaustionCount
124
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] tokenbucket_exhaustion_count: #{token_bucket_exhaustion_count}" }
125
- @metrics[:toex_count].add(token_bucket_exhaustion_count)
135
+ # This span from inbound HTTP request if from a SERVER by some http.method
136
+ def span_http?(span)
137
+ span.kind == ::OpenTelemetry::Trace::SpanKind::SERVER && !span.attributes[HTTP_METHOD].nil?
138
+ end
126
139
 
127
- _, through_trace_count = SolarWindsAPM.oboe_api.consumeThroughTraceCount
128
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] through_trace_count: #{through_trace_count}" }
129
- @metrics[:through_count].add(through_trace_count)
140
+ # Calculate HTTP status_code from span or default to UNAVAILABLE
141
+ # Something went wrong in OTel or instrumented service crashed early
142
+ # if no status_code in attributes of HTTP span
143
+ def get_http_status_code(span)
144
+ span.attributes[HTTP_STATUS_CODE] || INVALID_HTTP_STATUS_CODE
145
+ end
130
146
 
131
- _, triggered_trace_count = SolarWindsAPM.oboe_api.consumeTriggeredTraceCount
132
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] triggered_trace_count: #{triggered_trace_count}" }
133
- @metrics[:tt_count].add(triggered_trace_count)
147
+ # check if it's entry span based on no parent or parent is remote
148
+ def non_entry_span(span: nil, parent_context: nil)
149
+ if parent_context
150
+ parent_span = ::OpenTelemetry::Trace.current_span(parent_context)
151
+ parent_span && parent_span.context != ::OpenTelemetry::Trace::SpanContext::INVALID && parent_span.context.remote? == false
152
+ elsif span
153
+ span.attributes['sw.is_entry_span'] != true
154
+ end
134
155
  end
135
156
  end
136
157
  end
@@ -51,8 +51,6 @@ module SolarWindsAPM
51
51
  # text map setter will be used.
52
52
  def inject(carrier, context: ::OpenTelemetry::Context.current,
53
53
  setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
54
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] inject context: #{context.inspect}" }
55
-
56
54
  span_context = ::OpenTelemetry::Trace.current_span(context)&.context
57
55
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] span_context #{span_context.inspect}" }
58
56
  return unless span_context&.valid?
@@ -16,6 +16,7 @@ module SolarWindsAPM
16
16
  XTRACE_HEADER_NAME = 'x-trace'
17
17
  XTRACEOPTIONS_RESPONSE_HEADER_NAME = 'x-trace-options-response'
18
18
  INTL_SWO_EQUALS = '='
19
+ SW_XTRACEOPTIONS_RESPONSE_KEY = 'xtrace_options_response'
19
20
 
20
21
  private_constant \
21
22
  :HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, :XTRACE_HEADER_NAME,
@@ -36,14 +37,13 @@ module SolarWindsAPM
36
37
  setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
37
38
  span_context = ::OpenTelemetry::Trace.current_span(context).context
38
39
 
39
- SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] context current_span: #{context.instance_variable_get(:@entries)&.values&.first.inspect}" }
40
40
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] span_context: #{span_context.inspect}" }
41
41
 
42
42
  return unless span_context&.valid?
43
43
 
44
44
  x_trace = Utils.traceparent_from_context(span_context)
45
45
  exposed_headers = [XTRACE_HEADER_NAME]
46
- xtraceoptions_response = recover_response_from_tracestate(span_context.tracestate)
46
+ xtraceoptions_response = recover_response_from_tracestate(span_context)
47
47
 
48
48
  SolarWindsAPM.logger.debug do
49
49
  "[#{self.class}/#{__method__}] x-trace: #{x_trace}; exposed headers: #{exposed_headers.inspect}; x-trace-options-response: #{xtraceoptions_response}"
@@ -69,11 +69,12 @@ module SolarWindsAPM
69
69
  private
70
70
 
71
71
  # sw_xtraceoptions_response_key -> xtrace_options_response
72
- def recover_response_from_tracestate(tracestate)
73
- sanitized = tracestate.value(XTraceOptions.sw_xtraceoptions_response_key)
72
+ def recover_response_from_tracestate(span_context)
73
+ sanitized = span_context.tracestate.value(SW_XTRACEOPTIONS_RESPONSE_KEY)
74
74
  sanitized = '' if sanitized.nil?
75
75
  sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED,
76
76
  SolarWindsAPM::Constants::INTL_SWO_EQUALS)
77
+ sanitized = sanitized.gsub(':', SolarWindsAPM::Constants::INTL_SWO_EQUALS)
77
78
  sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED,
78
79
  SolarWindsAPM::Constants::INTL_SWO_COMMA)
79
80
  SolarWindsAPM.logger.debug do
@@ -6,14 +6,12 @@
6
6
  #
7
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
8
 
9
- require 'opentelemetry/sdk'
10
- require 'opentelemetry/instrumentation/all'
11
-
12
- # TODO: in future, it should add opentelemetry-metrics-sdk and require it here
9
+ require 'opentelemetry-sdk'
10
+ require 'opentelemetry-metrics-sdk'
11
+ require 'opentelemetry-exporter-otlp'
12
+ require 'opentelemetry-exporter-otlp-metrics'
13
+ require 'opentelemetry-instrumentation-all'
13
14
 
14
15
  require_relative 'opentelemetry/solarwinds_propagator'
15
- require_relative 'opentelemetry/solarwinds_processor'
16
- require_relative 'opentelemetry/solarwinds_sampler'
17
- require_relative 'opentelemetry/solarwinds_exporter'
18
16
  require_relative 'opentelemetry/solarwinds_response_propagator'
19
17
  require_relative 'opentelemetry/otlp_processor'