solarwinds_apm 7.0.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b86f18a58a518205b6f5d0d8e4054e821d2e2cc22b86366ff0ab13d0b2542322
4
- data.tar.gz: 8e6a2c4dbbee0e2689b36086f1f003237d1e6f0adefd36cb13038a782e449e50
3
+ metadata.gz: 62a494962e1b42497287295bec1c5ad4ae5dfa9b57df068d8897707e89e31c73
4
+ data.tar.gz: dd32d8923f0f93776bad22e506aee1a639c59f153cb67b4c944f92d4018f36d8
5
5
  SHA512:
6
- metadata.gz: 1884c401f28f7b57e0df2e2b0784b28964e50785555542376ad0527d72a7c43636115d48d332908a200fff3f2dd42160d8f4e79ec21e3e302e41ae048848f0eb
7
- data.tar.gz: 339c983072fc99986d575593a1bbcd84b5ff9728c61a47279de70ba010416b5fdad7cfa2d9b457c206049b1533ad87212f7b4ac26091f3dbcd7c68ad10a4edf1
6
+ metadata.gz: f28f6c76e3baa28539dc87704f1199e336cea2024a5394e229594d81a77f21280a216738e3bd3a53f5f9a3922a237925e85dd86789e86ff5d47e00b831acb0d0
7
+ data.tar.gz: e9ad6b91c3d1da150dba81c28a974fbeafe376dbe24be277ec27aeabca58809083c526446d9d66b4b705fe1b304b2b337d3073f8bc094b6e3f5f827709f63388
@@ -6,6 +6,8 @@
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 'set'
10
+
9
11
  module SolarWindsAPM
10
12
  ##
11
13
  # This module exposes a nested configuration hash that can be used to
@@ -24,14 +26,6 @@ module SolarWindsAPM
24
26
  6 => { stdlib: ::Logger::DEBUG, otel: 'debug' } }.freeze
25
27
 
26
28
  @@config = {}
27
- @@instrumentation = %i[action_controller action_controller_api action_view
28
- active_record bunnyclient bunnyconsumer curb
29
- dalli delayed_jobclient delayed_jobworker
30
- excon faraday graphql grpc_client grpc_server grape
31
- httpclient nethttp memcached mongo moped padrino rack redis
32
- resqueclient resqueworker rest_client
33
- sequel sidekiqclient sidekiqworker sinatra typhoeus
34
- curb excon faraday httpclient nethttp rest_client typhoeus]
35
29
 
36
30
  ##
37
31
  # load_config_file
@@ -131,8 +125,6 @@ module SolarWindsAPM
131
125
  def self.print_config
132
126
  SolarWindsAPM.logger.debug { "[#{name}/#{__method__}] General configurations list blow:" }
133
127
  @@config.each do |k, v|
134
- next if @@instrumentation.include?(k)
135
-
136
128
  SolarWindsAPM.logger.debug do
137
129
  "[#{name}/#{__method__}] Config Key/Value: #{k}, #{v.inspect}"
138
130
  end
@@ -148,8 +140,6 @@ module SolarWindsAPM
148
140
  # This will be called when require 'solarwinds_apm/config' happen
149
141
  #
150
142
  def self.initialize
151
- # for config file backward compatibility
152
- @@instrumentation.each { |inst| @@config[inst] = {} }
153
143
  @@config[:transaction_name] = {}
154
144
 
155
145
  # Always load the template, it has all the keys and defaults defined,
@@ -1,12 +1,31 @@
1
-
2
1
  # Noop Mode
3
2
 
4
3
  Here we can define modules and classes for noop mode.
5
4
 
6
- Instead of polluting code with SolarWindsAPM.loaded conditionals
5
+ Instead of polluting code with SolarWindsAPM.loaded conditionals, we load these classes when in noop mode and they expose noop behavior.
6
+
7
+ The following methods require noop implementations based on the API modules:
8
+
9
+ ## Currently Implemented Noop Modules in `api.rb`:
10
+
11
+ - **Tracing**:
12
+ - `solarwinds_ready?(wait_milliseconds=3000, integer_response: false)` - Always returns false
13
+
14
+ - **CurrentTraceInfo**:
15
+ - `current_trace_info` - Returns a TraceInfo instance with default/empty values
16
+ - TraceInfo class with noop methods: `for_log`, `hash_for_log`, and attributes: `tracestring`, `trace_id`, `span_id`, `trace_flags`, `do_log`
17
+
18
+ - **CustomMetrics**:
19
+ - `increment_metric(name, count=1, with_hostname=false, tags_kvs={})` - Returns false with deprecation warning
20
+ - `summary_metric(name, value, count=1, with_hostname=false, tags_kvs={})` - Returns false with deprecation warning
21
+
22
+ - **OpenTelemetry**:
23
+ - `in_span(name, attributes: nil, links: nil, start_timestamp: nil, kind: nil, &block)` - Simply yields to the block if given
7
24
 
8
- we load these classes when in noop mode and they expose noop behavior.
25
+ - **TransactionName**:
26
+ - `set_transaction_name(custom_name=nil)` - Always returns true
9
27
 
10
- so far only one class is needed:
28
+ - **CustomInstrumentation/Tracer**:
29
+ - `add_tracer(method_name, span_name=nil, options={})` - Always returns nil.
11
30
 
12
- - SolarWindsAPM::Context and its toString() method from oboe
31
+ These modules are extended into `SolarWindsAPM::API` to provide consistent behavior when SolarWindsAPM is disabled or in noop mode.
@@ -79,10 +79,17 @@ module NoopAPI
79
79
  true
80
80
  end
81
81
  end
82
+
83
+ module Tracer
84
+ def add_tracer(*); end
85
+ end
82
86
  end
83
87
 
84
- SolarWindsAPM::API.extend(NoopAPI::Tracing)
85
- SolarWindsAPM::API.extend(NoopAPI::CurrentTraceInfo)
86
- SolarWindsAPM::API.extend(NoopAPI::CustomMetrics)
87
- SolarWindsAPM::API.extend(NoopAPI::OpenTelemetry)
88
- SolarWindsAPM::API.extend(NoopAPI::TransactionName)
88
+ [
89
+ NoopAPI::Tracing,
90
+ NoopAPI::CurrentTraceInfo,
91
+ NoopAPI::CustomMetrics,
92
+ NoopAPI::OpenTelemetry,
93
+ NoopAPI::TransactionName,
94
+ NoopAPI::Tracer
95
+ ].each { |mod| SolarWindsAPM::API.extend(mod) }
@@ -14,6 +14,7 @@ module SolarWindsAPM
14
14
  def initialize(settings)
15
15
  @scale = settings[:scale]
16
16
  @rate = settings[:rate] || 0
17
+ @random_generator = Random.new
17
18
  end
18
19
 
19
20
  def update(settings)
@@ -23,11 +24,12 @@ module SolarWindsAPM
23
24
 
24
25
  # return Boolean
25
26
  def roll
26
- (rand * @scale) < @rate
27
+ rand_num = @random_generator.rand
28
+ SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] rand_num=#{rand_num.round(6)}, @scale=#{@scale}, @rate=#{@rate}" }
29
+ (rand_num * @scale) < @rate
27
30
  end
28
31
 
29
32
  def rate=(rate)
30
- # Math.max(0, Math.min(this.#scale, n))
31
33
  @rate = rate.clamp(0, @scale)
32
34
  end
33
35
  end
@@ -9,7 +9,7 @@
9
9
  module SolarWindsAPM
10
10
  class HttpSampler < Sampler
11
11
  REQUEST_TIMEOUT = 10 # 10s
12
- GET_SETTING_DURAION = 60 # 60s
12
+ GET_SETTING_DURATION = 60 # 60s
13
13
 
14
14
  # we don't need hostname as it's for separating browser and local env
15
15
  def initialize(config)
@@ -22,11 +22,31 @@ module SolarWindsAPM
22
22
  @hostname = hostname
23
23
  @setting_url = URI.join(@url, "./v1/settings/#{@service}/#{@hostname}")
24
24
 
25
- Thread.new { settings_request }
25
+ @pid = nil
26
+ @thread = nil
27
+
28
+ reset_on_fork
29
+ end
30
+
31
+ # restart the settings request thread after forking
32
+ def should_sample?(params)
33
+ reset_on_fork
34
+ super
26
35
  end
27
36
 
28
37
  private
29
38
 
39
+ def reset_on_fork
40
+ pid = Process.pid
41
+ return if @pid == pid
42
+
43
+ @pid = pid
44
+ @thread = Thread.new { settings_request }
45
+ @logger.debug { "[#{self.class}/#{__method__}] Restart the settings_request thread in process: #{@pid}." }
46
+ rescue ThreadError => e
47
+ @logger.error { "[#{self.class}/#{__method__}] Unexpected error in HttpSampler#reset_on_fork: #{e.message}" }
48
+ end
49
+
30
50
  # Node.js equivalent: Retrieve system hostname
31
51
  # e.g. docker -> docker.swo.ubuntu.development; macos -> NHSDFWSSD
32
52
  def hostname
@@ -36,27 +56,39 @@ module SolarWindsAPM
36
56
 
37
57
  def fetch_with_timeout(url, timeout_seconds = nil)
38
58
  uri = url
59
+ timeout = timeout_seconds || REQUEST_TIMEOUT
60
+
61
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
62
+
63
+ remaining = lambda {
64
+ r = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
65
+ r.negative? ? 0.0 : r
66
+ }
67
+
39
68
  response = nil
40
69
 
41
- thread = Thread.new do
70
+ begin
42
71
  ::OpenTelemetry::Common::Utilities.untraced do
43
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
72
+ Net::HTTP.start(
73
+ uri.host, uri.port,
74
+ use_ssl: uri.scheme == 'https',
75
+ open_timeout: timeout,
76
+ read_timeout: timeout # will shrink later
77
+ ) do |http|
44
78
  request = Net::HTTP::Get.new(uri)
45
79
  request['Authorization'] = @headers
46
80
 
81
+ # Before issuing request, tighten read_timeout to remaining
82
+ http.read_timeout = remaining.call
47
83
  response = http.request(request)
48
84
  end
49
85
  end
86
+ rescue Net::ReadTimeout, Net::OpenTimeout
87
+ @logger.debug { "Request timed out after #{timeout} seconds" }
50
88
  rescue StandardError => e
51
89
  @logger.debug { "Error during request: #{e.message}" }
52
90
  end
53
91
 
54
- thread_join = thread.join(timeout_seconds || REQUEST_TIMEOUT)
55
- if thread_join.nil?
56
- @logger.debug { "Request timed out after #{timeout_seconds} seconds" }
57
- thread.kill
58
- end
59
-
60
92
  response
61
93
  end
62
94
 
@@ -77,11 +109,11 @@ module SolarWindsAPM
77
109
  sleep([0, expiry_timeout].max)
78
110
  else
79
111
  @logger.warn { 'Retrieved sampling settings are invalid. Ensure proper configuration.' }
80
- sleep(GET_SETTING_DURAION)
112
+ sleep(GET_SETTING_DURATION)
81
113
  end
82
114
  rescue StandardError => e
83
115
  @logger.warn { "Failed to retrieve sampling settings (#{e.message}), tracing will be disabled until valid ones are available." }
84
- sleep(GET_SETTING_DURAION)
116
+ sleep(GET_SETTING_DURATION)
85
117
  end
86
118
  end
87
119
  end
@@ -22,7 +22,7 @@ module SolarWindsAPM
22
22
  flags |= SolarWindsAPM::Flags::OVERRIDE
23
23
  end
24
24
 
25
- remote.dup.tap { |merged| merged[:flags] = flags }
25
+ remote.merge(flags: flags)
26
26
  end
27
27
  end
28
28
  end
@@ -175,14 +175,21 @@ module SolarWindsAPM
175
175
  ::OpenTelemetry::SDK::Resources::Resource.create({})
176
176
  end
177
177
 
178
+ def self.number?(string)
179
+ true if Float(string)
180
+ rescue StandardError
181
+ false
182
+ end
183
+
178
184
  def self.safe_integer?(number)
179
185
  min_safe_integer = -((2**53) - 1)
180
186
  max_safe_integer = (2**53) - 1
181
- number.is_a?(Integer) && number >= min_safe_integer && number <= max_safe_integer
187
+ number = number.to_i if number?(number)
188
+ number.between?(min_safe_integer, max_safe_integer)
182
189
  end
183
190
 
184
191
  def self.windows?
185
- %w[mingw32 cygwin].any? { |platform| RUBY_PLATFORM.include?(platform) }
192
+ /mswin|mingw|cygwin/.match?(RUBY_PLATFORM)
186
193
  end
187
194
 
188
195
  def self.random_uuid
@@ -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) }
@@ -13,7 +13,7 @@ module SolarWindsAPM
13
13
  module Version
14
14
  MAJOR = 7 # breaking,
15
15
  MINOR = 0 # feature,
16
- PATCH = 0 # fix => BFF
16
+ PATCH = 1 # fix => BFF
17
17
  PRE = nil
18
18
 
19
19
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
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.0.1
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-09-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: opentelemetry-exporter-otlp