solarwinds_apm 7.0.0 → 7.1.0

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.
@@ -9,63 +9,66 @@
9
9
  module SolarWindsAPM
10
10
  class TraceOptions
11
11
  TRIGGER_TRACE_KEY = 'trigger-trace'
12
- TIMESTAMP_KEY = 'ts'
13
- SW_KEYS_KEY = 'sw-keys'
14
-
15
- CUSTOM_KEY_REGEX = /^custom-[^\s]*$/
12
+ TIMESTAMP_KEY = 'ts'
13
+ SW_KEYS_KEY = 'sw-keys'
14
+ CUSTOM_KEY_REGEX = /^custom-[^\s]*$/
16
15
 
17
16
  def self.parse_trace_options(header, logger)
17
+ logger.debug { "[#{self.class}/#{__method__}] Parsing trace options header: #{header&.slice(0, 100)}..." }
18
18
  trace_options = TriggerTraceOptions.new(nil, nil, nil, {}, [], TraceOptionsResponse.new(nil, nil, []))
19
19
 
20
- kvs = header.split(';').map do |kv|
20
+ kvs = header.split(';').filter_map do |kv|
21
21
  key, *values = kv.split('=').map(&:strip)
22
+ next if key.nil? || key.empty?
23
+
22
24
  value = values.any? ? values.join('=') : nil
23
25
  [key, value]
24
26
  end
25
27
 
26
- kvs.reject! { |key, _| key.nil? || key.empty? }
28
+ logger.debug { "[#{self.class}/#{__method__}] Parsed kvs #{kvs.inspect}" }
27
29
 
28
30
  kvs.each do |k, v|
29
31
  case k
30
32
  when TRIGGER_TRACE_KEY
31
33
  if v || trace_options.trigger_trace
32
- logger.debug { 'invalid trace option for trigger trace' }
34
+ logger.debug { "[#{self.class}/#{__method__}] invalid trace option for trigger trace: value=#{v}, already_set=#{trace_options.trigger_trace}" }
33
35
  trace_options.ignored << [k, v]
34
36
  next
35
37
  end
36
38
  trace_options.trigger_trace = true
37
39
  when TIMESTAMP_KEY
38
40
  if v.nil? || trace_options.timestamp
39
- logger.debug { 'invalid trace option for timestamp' }
41
+ logger.debug { "[#{self.class}/#{__method__}] invalid trace option for timestamp: value=#{v}, already_set=#{trace_options.timestamp}" }
40
42
  trace_options.ignored << [k, v]
41
43
  next
42
44
  end
43
45
 
44
46
  unless numeric_integer?(v)
45
- logger.debug { 'invalid trace option for timestamp, should be an integer' }
47
+ logger.debug { "[#{self.class}/#{__method__}] invalid trace option for timestamp, should be an integer: #{v}" }
46
48
  trace_options.ignored << [k, v]
47
49
  next
48
50
  end
49
51
  trace_options.timestamp = v.to_i
50
52
  when SW_KEYS_KEY
51
53
  if v.nil? || trace_options.sw_keys
52
- logger.debug { 'invalid trace option for sw keys' }
54
+ logger.debug { "[#{self.class}/#{__method__}] invalid trace option for sw keys: value=#{v}, already_set=#{trace_options.sw_keys}" }
53
55
  trace_options.ignored << [k, v]
54
56
  next
55
57
  end
56
58
  trace_options.sw_keys = v
57
59
  when CUSTOM_KEY_REGEX
58
60
  if v.nil? || trace_options.custom[k]
59
- logger.debug { "invalid trace option for custom key #{k}" }
61
+ logger.debug { "[#{self.class}/#{__method__}] invalid trace option for custom key #{k}: value=#{v}, already_set=#{trace_options.custom[k]}" }
60
62
  trace_options.ignored << [k, v]
61
63
  next
62
64
  end
63
65
  trace_options.custom[k] = v
64
66
  else
67
+ logger.debug { "[#{self.class}/#{__method__}] Unknown key ignored: #{k}=#{v}" }
65
68
  trace_options.ignored << [k, v]
66
69
  end
67
70
  end
68
-
71
+ logger.debug { "[#{self.class}/#{__method__}] Parsing complete: trigger_trace=#{trace_options.trigger_trace}, timestamp=#{trace_options.timestamp}, sw_keys=#{trace_options.sw_keys}, custom_keys=#{trace_options.custom}, ignored=#{trace_options.ignored}" }
69
72
  trace_options
70
73
  end
71
74
 
@@ -86,15 +89,29 @@ module SolarWindsAPM
86
89
  'trigger-trace': trace_options_response.trigger_trace,
87
90
  ignored: trace_options_response.ignored.empty? ? nil : trace_options_response.ignored.join(',')
88
91
  }
89
- kvs.compact.map { |k, v| "#{k}:#{v}" }.join(';')
92
+
93
+ kvs.compact!
94
+ result = kvs.map { |k, v| "#{k}:#{v}" }.join(';')
95
+ SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Stringified trace options response: #{result}" }
96
+ result
90
97
  end
91
98
 
92
99
  def self.validate_signature(header, signature, key, timestamp)
93
- return Auth::NO_SIGNATURE_KEY unless key
94
- return Auth::BAD_TIMESTAMP unless timestamp && (Time.now.to_i - timestamp).abs <= 5 * 60
100
+ unless key
101
+ SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Signature validation failed: no signature key available" }
102
+ return Auth::NO_SIGNATURE_KEY
103
+ end
104
+
105
+ unless timestamp && (Time.now.to_i - timestamp).abs <= 5 * 60
106
+ SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Signature validation failed: bad timestamp (diff more than 300s)" }
107
+ return Auth::BAD_TIMESTAMP
108
+ end
95
109
 
96
110
  digest = OpenSSL::HMAC.hexdigest('SHA1', key, header)
97
- signature == digest ? Auth::OK : Auth::BAD_SIGNATURE
111
+ is_valid = signature == digest
112
+
113
+ SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Signature validation result: #{is_valid ? 'valid' : 'invalid'}" }
114
+ is_valid ? Auth::OK : Auth::BAD_SIGNATURE
98
115
  end
99
116
  end
100
117
  end
@@ -10,7 +10,6 @@ require 'json'
10
10
  require 'fileutils'
11
11
  require 'tempfile'
12
12
  require 'uri'
13
- require 'opentelemetry-sdk'
14
13
 
15
14
  require_relative 'sampling/sampling_constants'
16
15
  require_relative 'sampling/dice'
@@ -64,9 +64,7 @@ module SolarWindsAPM
64
64
 
65
65
  response = nil
66
66
  ::OpenTelemetry::Common::Utilities.untraced do
67
- http = Net::HTTP.new(url.host, url.port)
68
- request = Net::HTTP::Get.new(url)
69
- response = http.request(request)
67
+ response = Net::HTTP.get_response(url)
70
68
  end
71
69
 
72
70
  raise 'Response returned non-200 status code' unless response&.code.to_i == 200
@@ -152,37 +150,41 @@ module SolarWindsAPM
152
150
  end
153
151
 
154
152
  def self.detect_ec2
155
- attribute = ::OpenTelemetry::Resource::Detector::AWS::EC2.detect
156
- SolarWindsAPM.logger.debug { "#{self.class}/#{__method__}] retrieved resource_attributes: #{attribute.instance_variable_get(:@attributes)}" }
157
- attribute
158
- rescue StandardError
159
- ::OpenTelemetry::SDK::Resources::Resource.create({})
153
+ run_opentelemetry_detector(::OpenTelemetry::Resource::Detector::AWS::EC2)
160
154
  end
161
155
 
162
156
  def self.detect_azure
163
- attribute = ::OpenTelemetry::Resource::Detector::Azure.detect
164
- SolarWindsAPM.logger.debug { "#{self.class}/#{__method__}] retrieved resource_attributes: #{attribute.instance_variable_get(:@attributes)}" }
165
- attribute
166
- rescue StandardError
167
- ::OpenTelemetry::SDK::Resources::Resource.create({})
157
+ run_opentelemetry_detector(::OpenTelemetry::Resource::Detector::Azure)
168
158
  end
169
159
 
170
160
  def self.detect_container
171
- attribute = ::OpenTelemetry::Resource::Detector::Container.detect
172
- SolarWindsAPM.logger.debug { "#{self.class}/#{__method__}] retrieved resource_attributes: #{attribute.instance_variable_get(:@attributes)}" }
161
+ run_opentelemetry_detector(::OpenTelemetry::Resource::Detector::Container)
162
+ end
163
+
164
+ def self.run_opentelemetry_detector(detector_class)
165
+ attribute = detector_class.detect
166
+ SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Detector #{detector_class} retrieved: #{attribute.instance_variable_get(:@attributes)}" }
173
167
  attribute
174
- rescue StandardError
168
+ rescue StandardError => e
169
+ SolarWindsAPM.logger.error { "[#{self.class}/#{__method__}] Detector #{detector_class} failed. Error: #{e.message}." }
175
170
  ::OpenTelemetry::SDK::Resources::Resource.create({})
176
171
  end
177
172
 
173
+ def self.number?(string)
174
+ true if Float(string)
175
+ rescue StandardError
176
+ false
177
+ end
178
+
178
179
  def self.safe_integer?(number)
179
180
  min_safe_integer = -((2**53) - 1)
180
181
  max_safe_integer = (2**53) - 1
181
- number.is_a?(Integer) && number >= min_safe_integer && number <= max_safe_integer
182
+ number = number.to_i if number?(number)
183
+ number.between?(min_safe_integer, max_safe_integer)
182
184
  end
183
185
 
184
186
  def self.windows?
185
- %w[mingw32 cygwin].any? { |platform| RUBY_PLATFORM.include?(platform) }
187
+ /mswin|mingw|cygwin/.match?(RUBY_PLATFORM)
186
188
  end
187
189
 
188
190
  def self.random_uuid
@@ -37,17 +37,24 @@ module SolarWindsAPM
37
37
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] enabled_regexps: #{enabled_regexps&.inspect}" }
38
38
  SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] disabled_regexps: #{disabled_regexps&.inspect}" }
39
39
 
40
- return false if disabled_regexps.is_a?(Array) && disabled_regexps.any? { |regex| regex.match?(@url_path) }
41
- return true if enabled_regexps.is_a?(Array) && enabled_regexps.any? { |regex| regex.match?(@url_path) }
42
- return false if disabled_regexps.is_a?(Array) && disabled_regexps.any? { |regex| regex.match?(span_layer) }
43
- return true if enabled_regexps.is_a?(Array) && enabled_regexps.any? { |regex| regex.match?(span_layer) }
40
+ return false if matches_any?(disabled_regexps, @url_path)
41
+ return true if matches_any?(enabled_regexps, @url_path)
42
+ return false if matches_any?(disabled_regexps, span_layer)
43
+ return true if matches_any?(enabled_regexps, span_layer)
44
44
 
45
45
  true
46
46
  rescue StandardError => e
47
47
  SolarWindsAPM.logger.warn do
48
- "[#{self.class}/#{__method__}] Could not determine tracing status for #{kind}. #{e.inspect}. transaction_settings regexps/extensions igonred/unfiltered."
48
+ "[#{self.class}/#{__method__}] Could not determine tracing status for #{@kind}. #{e.inspect}. transaction_settings regexps/extensions igonred/unfiltered."
49
49
  end
50
50
  true
51
51
  end
52
+
53
+ # Checks if a given string matches any regex in a list.
54
+ def matches_any?(regex_list, string_to_match)
55
+ return false unless regex_list.is_a?(Array)
56
+
57
+ regex_list.any? { |regex| regex.match?(string_to_match) }
58
+ end
52
59
  end
53
60
  end
@@ -52,9 +52,7 @@ module SolarWindsAPM
52
52
  txn_name_exist = true
53
53
  @transaction_name[txn_name].exit
54
54
  end
55
- end
56
55
 
57
- @mutex.synchronize do
58
56
  if txn_name_exist || @transaction_name.size < MAX_CARDINALITY
59
57
  @cache[key] = txn_name
60
58
  @transaction_name[txn_name] = Thread.new { cleanup_txn(key, txn_name) }
@@ -12,7 +12,7 @@ module SolarWindsAPM
12
12
  # solarwinds_apm.gemspec during gem build process
13
13
  module Version
14
14
  MAJOR = 7 # breaking,
15
- MINOR = 0 # feature,
15
+ MINOR = 1 # feature,
16
16
  PATCH = 0 # fix => BFF
17
17
  PRE = nil
18
18
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solarwinds_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.0
4
+ version: 7.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maia Engeli
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2025-08-06 00:00:00.000000000 Z
14
+ date: 2025-10-21 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: opentelemetry-exporter-otlp
@@ -27,6 +27,20 @@ dependencies:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
29
  version: 0.29.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: opentelemetry-exporter-otlp-logs
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 0.2.1
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.2.1
30
44
  - !ruby/object:Gem::Dependency
31
45
  name: opentelemetry-exporter-otlp-metrics
32
46
  requirement: !ruby/object:Gem::Requirement
@@ -55,6 +69,34 @@ dependencies:
55
69
  - - ">="
56
70
  - !ruby/object:Gem::Version
57
71
  version: 0.31.0
72
+ - !ruby/object:Gem::Dependency
73
+ name: opentelemetry-instrumentation-logger
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 0.1.0
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: 0.1.0
86
+ - !ruby/object:Gem::Dependency
87
+ name: opentelemetry-logs-sdk
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.4.0
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 0.4.0
58
100
  - !ruby/object:Gem::Dependency
59
101
  name: opentelemetry-metrics-sdk
60
102
  requirement: !ruby/object:Gem::Requirement