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 +4 -4
- data/lib/solarwinds_apm/config.rb +2 -12
- data/lib/solarwinds_apm/noop/README.md +24 -5
- data/lib/solarwinds_apm/noop/api.rb +12 -5
- data/lib/solarwinds_apm/sampling/dice.rb +4 -2
- data/lib/solarwinds_apm/sampling/http_sampler.rb +44 -12
- data/lib/solarwinds_apm/sampling/settings.rb +1 -1
- data/lib/solarwinds_apm/support/resource_detector.rb +9 -2
- data/lib/solarwinds_apm/support/txn_name_manager.rb +0 -2
- data/lib/solarwinds_apm/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62a494962e1b42497287295bec1c5ad4ae5dfa9b57df068d8897707e89e31c73
|
4
|
+
data.tar.gz: dd32d8923f0f93776bad22e506aee1a639c59f153cb67b4c944f92d4018f36d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
25
|
+
- **TransactionName**:
|
26
|
+
- `set_transaction_name(custom_name=nil)` - Always returns true
|
9
27
|
|
10
|
-
|
28
|
+
- **CustomInstrumentation/Tracer**:
|
29
|
+
- `add_tracer(method_name, span_name=nil, options={})` - Always returns nil.
|
11
30
|
|
12
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
70
|
+
begin
|
42
71
|
::OpenTelemetry::Common::Utilities.untraced do
|
43
|
-
Net::HTTP.start(
|
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(
|
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(
|
116
|
+
sleep(GET_SETTING_DURATION)
|
85
117
|
end
|
86
118
|
end
|
87
119
|
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
|
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
|
-
|
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) }
|
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.
|
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-
|
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
|