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.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/solarwinds_apm/api/current_trace_info.rb +10 -6
- data/lib/solarwinds_apm/api/custom_metrics.rb +8 -25
- data/lib/solarwinds_apm/api/tracing.rb +12 -27
- data/lib/solarwinds_apm/api/transaction_name.rb +6 -10
- data/lib/solarwinds_apm/config.rb +1 -1
- data/lib/solarwinds_apm/constants.rb +1 -0
- data/lib/solarwinds_apm/noop/api.rb +5 -2
- data/lib/solarwinds_apm/noop.rb +0 -24
- data/lib/solarwinds_apm/opentelemetry/otlp_processor.rb +90 -69
- data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +0 -2
- data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +5 -4
- data/lib/solarwinds_apm/opentelemetry.rb +5 -7
- data/lib/solarwinds_apm/otel_native_config.rb +177 -0
- data/lib/solarwinds_apm/patch/README.md +15 -0
- data/lib/solarwinds_apm/{noop/metadata.rb → sampling/dice.rb} +19 -17
- data/lib/solarwinds_apm/sampling/http_sampler.rb +87 -0
- data/lib/solarwinds_apm/sampling/json_sampler.rb +52 -0
- data/lib/solarwinds_apm/sampling/metrics.rb +38 -0
- data/lib/solarwinds_apm/sampling/oboe_sampler.rb +348 -0
- data/lib/solarwinds_apm/sampling/sampler.rb +197 -0
- data/lib/solarwinds_apm/sampling/sampling_constants.rb +127 -0
- data/lib/solarwinds_apm/sampling/sampling_patch.rb +49 -0
- data/lib/solarwinds_apm/sampling/setting_example.txt +1 -0
- data/lib/solarwinds_apm/{noop/context.rb → sampling/settings.rb} +14 -25
- data/lib/solarwinds_apm/sampling/token_bucket.rb +126 -0
- data/lib/solarwinds_apm/sampling/trace_options.rb +100 -0
- data/lib/solarwinds_apm/{patch.rb → sampling.rb} +20 -4
- data/lib/solarwinds_apm/{noop/span.rb → support/aws_resource_detector.rb} +5 -18
- data/lib/solarwinds_apm/support/logger_formatter.rb +1 -1
- data/lib/solarwinds_apm/support/logging_log_event.rb +1 -1
- data/lib/solarwinds_apm/support/lumberjack_formatter.rb +1 -1
- data/lib/solarwinds_apm/support/otlp_endpoint.rb +99 -0
- data/lib/solarwinds_apm/support/resource_detector/aws/beanstalk.rb +51 -0
- data/lib/solarwinds_apm/support/resource_detector/aws/ec2.rb +145 -0
- data/lib/solarwinds_apm/support/resource_detector/aws/ecs.rb +173 -0
- data/lib/solarwinds_apm/support/resource_detector/aws/eks.rb +174 -0
- data/lib/solarwinds_apm/support/resource_detector/aws/lambda.rb +66 -0
- data/lib/solarwinds_apm/support/resource_detector.rb +192 -0
- data/lib/solarwinds_apm/support/service_key_checker.rb +12 -6
- data/lib/solarwinds_apm/support/transaction_settings.rb +6 -0
- data/lib/solarwinds_apm/support/txn_name_manager.rb +54 -9
- data/lib/solarwinds_apm/support/utils.rb +9 -0
- data/lib/solarwinds_apm/support.rb +3 -4
- data/lib/solarwinds_apm/version.rb +4 -4
- data/lib/solarwinds_apm.rb +27 -73
- metadata +99 -40
- data/ext/oboe_metal/extconf.rb +0 -168
- data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +0 -1
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +0 -1
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +0 -1
- data/ext/oboe_metal/lib/liboboe-1.0-lambda-aarch64.so.sha256 +0 -1
- data/ext/oboe_metal/lib/liboboe-1.0-lambda-x86_64.so.sha256 +0 -1
- data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +0 -1
- data/ext/oboe_metal/src/VERSION +0 -1
- data/ext/oboe_metal/src/bson/bson.h +0 -220
- data/ext/oboe_metal/src/bson/platform_hacks.h +0 -91
- data/ext/oboe_metal/src/init_solarwinds_apm.cc +0 -18
- data/ext/oboe_metal/src/oboe.h +0 -930
- data/ext/oboe_metal/src/oboe_api.cpp +0 -793
- data/ext/oboe_metal/src/oboe_api.h +0 -621
- data/ext/oboe_metal/src/oboe_debug.h +0 -17
- data/ext/oboe_metal/src/oboe_swig_wrap.cc +0 -11045
- data/lib/oboe_metal.rb +0 -187
- data/lib/solarwinds_apm/cert/star.appoptics.com.issuer.crt +0 -24
- data/lib/solarwinds_apm/oboe_init_options.rb +0 -222
- data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +0 -239
- data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +0 -174
- data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +0 -333
- data/lib/solarwinds_apm/otel_config.rb +0 -174
- data/lib/solarwinds_apm/otel_lambda_config.rb +0 -56
- data/lib/solarwinds_apm/patch/dummy_patch.rb +0 -12
- data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +0 -33
- data/lib/solarwinds_apm/support/support_report.rb +0 -99
- data/lib/solarwinds_apm/support/transaction_cache.rb +0 -57
- data/lib/solarwinds_apm/support/x_trace_options.rb +0 -138
data/lib/oboe_metal.rb
DELETED
@@ -1,187 +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
|
-
# Disable docs and Camelcase warns since we're implementing
|
10
|
-
# an interface here. See OboeBase for details.
|
11
|
-
module SolarWindsAPM
|
12
|
-
include Oboe_metal
|
13
|
-
|
14
|
-
@loaded = false
|
15
|
-
@reporter = nil
|
16
|
-
@oboe_api = nil
|
17
|
-
@init_sent = false
|
18
|
-
|
19
|
-
class << self
|
20
|
-
attr_accessor :reporter, :loaded, :oboe_api, :init_sent
|
21
|
-
|
22
|
-
def sample_rate(rate)
|
23
|
-
return unless SolarWindsAPM.loaded
|
24
|
-
|
25
|
-
# Update liboboe with the new SampleRate value
|
26
|
-
SolarWindsAPM::Context.setDefaultSampleRate(rate.to_i)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Reporter that send span data to SWO
|
31
|
-
class Reporter
|
32
|
-
class << self
|
33
|
-
##
|
34
|
-
# start
|
35
|
-
#
|
36
|
-
# Start the SolarWindsAPM Reporter
|
37
|
-
#
|
38
|
-
def start
|
39
|
-
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe # creates an array with the options in the right order
|
40
|
-
SolarWindsAPM.reporter = Oboe_metal::Reporter.new(*options)
|
41
|
-
SolarWindsAPM.loaded = true
|
42
|
-
report_init if (options[22]).zero? # report init at beginning if no after fork enabled
|
43
|
-
rescue StandardError => e
|
44
|
-
warn e.message
|
45
|
-
SolarWindsAPM.loaded = false
|
46
|
-
end
|
47
|
-
alias restart start
|
48
|
-
|
49
|
-
##
|
50
|
-
# sendReport
|
51
|
-
#
|
52
|
-
# Send the report for the given event
|
53
|
-
#
|
54
|
-
def send_report(evt, with_system_timestamp: true)
|
55
|
-
SolarWindsAPM.reporter.sendReport(evt, with_system_timestamp)
|
56
|
-
end
|
57
|
-
|
58
|
-
##
|
59
|
-
# sendStatus
|
60
|
-
#
|
61
|
-
# Send the report for the given event
|
62
|
-
#
|
63
|
-
def send_status(evt, context = nil, with_system_timestamp: true)
|
64
|
-
SolarWindsAPM.reporter.sendStatus(evt, context, with_system_timestamp)
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
# Internal: Report that instrumentation for the given layer has been
|
70
|
-
# installed, as well as the version of instrumentation and version of
|
71
|
-
# layer.
|
72
|
-
#
|
73
|
-
def report_init(layer = :rack) # :nodoc:
|
74
|
-
# Don't send __Init in test or if SolarWindsAPM isn't fully loaded (e.g. missing c-extension)
|
75
|
-
# or if already sent (e.g. SolarWindsAPM.init_sent = true)
|
76
|
-
return if SolarWindsAPM.init_sent
|
77
|
-
return unless SolarWindsAPM.loaded
|
78
|
-
|
79
|
-
platform_info = build_swo_init_report
|
80
|
-
log_init(layer, platform_info)
|
81
|
-
|
82
|
-
SolarWindsAPM.init_sent = true
|
83
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Init message has been sent." }
|
84
|
-
end
|
85
|
-
|
86
|
-
##
|
87
|
-
# :nodoc:
|
88
|
-
# Internal: Reports agent init to the collector
|
89
|
-
#
|
90
|
-
# ==== Arguments
|
91
|
-
#
|
92
|
-
# * +layer+ - The layer the reported event belongs to
|
93
|
-
# * +kvs+ - A hash containing key/value pairs that will be reported along with this event
|
94
|
-
def log_init(layer = :rack, kvs = {})
|
95
|
-
context = SolarWindsAPM::Metadata.makeRandom
|
96
|
-
return SolarWindsAPM::Context.toString unless context.isValid
|
97
|
-
|
98
|
-
event = context.createEvent
|
99
|
-
event.addInfo('Layer', layer.to_s)
|
100
|
-
event.addInfo('Label', 'single')
|
101
|
-
kvs.each do |k, v|
|
102
|
-
event.addInfo(k, v.to_s)
|
103
|
-
end
|
104
|
-
|
105
|
-
SolarWindsAPM::Reporter.send_status(event, context, with_system_timestamp: true)
|
106
|
-
SolarWindsAPM::Context.toString
|
107
|
-
end
|
108
|
-
|
109
|
-
##
|
110
|
-
# build_swo_init_report
|
111
|
-
#
|
112
|
-
# Internal: Build a hash of KVs that reports on the status of the
|
113
|
-
# running environment for swo only. This is used on stack boot in __Init reporting
|
114
|
-
# and for SolarWindsAPM.support_report.
|
115
|
-
#
|
116
|
-
def build_swo_init_report
|
117
|
-
platform_info = { '__Init' => true }
|
118
|
-
|
119
|
-
begin
|
120
|
-
platform_info['APM.Version'] = SolarWindsAPM::Version::STRING
|
121
|
-
platform_info['APM.Extension.Version'] = extension_lib_version
|
122
|
-
|
123
|
-
# OTel Resource Attributes (Optional)
|
124
|
-
platform_info['process.executable.path'] =
|
125
|
-
File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']).sub(/.*\s.*/m, '"\&"')
|
126
|
-
platform_info['process.executable.name'] = RbConfig::CONFIG['ruby_install_name']
|
127
|
-
platform_info['process.command_line'] = $PROGRAM_NAME
|
128
|
-
platform_info['process.telemetry.path'] = Gem::Specification.find_by_name('solarwinds_apm')&.full_gem_path
|
129
|
-
platform_info['os.type'] = RUBY_PLATFORM
|
130
|
-
|
131
|
-
platform_info.merge!(report_gem_in_use)
|
132
|
-
|
133
|
-
# Collect up opentelemetry sdk version (Instrumented Library Versions) (Required)
|
134
|
-
begin
|
135
|
-
require 'opentelemetry/sdk'
|
136
|
-
::OpenTelemetry::SDK::Resources::Resource.telemetry_sdk.attribute_enumerator.each do |k, v|
|
137
|
-
platform_info[k] = v
|
138
|
-
end
|
139
|
-
::OpenTelemetry::SDK::Resources::Resource.process.attribute_enumerator.each { |k, v| platform_info[k] = v }
|
140
|
-
rescue StandardError => e
|
141
|
-
SolarWindsAPM.logger.warn do
|
142
|
-
"[#{self.class}/#{__method__}] Fail to extract telemetry attributes. Error: #{e.message}"
|
143
|
-
end
|
144
|
-
end
|
145
|
-
rescue StandardError, ScriptError => e
|
146
|
-
# Also rescue ScriptError (aka SyntaxError) in case one of the expected
|
147
|
-
# version defines don't exist
|
148
|
-
|
149
|
-
platform_info['Error'] = "Error in build_report: #{e.message}"
|
150
|
-
|
151
|
-
SolarWindsAPM.logger.warn { "[#{self.class}/#{__method__}] Error in build_init_report: #{e.message}" }
|
152
|
-
SolarWindsAPM.logger.debug { e.backtrace }
|
153
|
-
end
|
154
|
-
platform_info
|
155
|
-
end
|
156
|
-
|
157
|
-
##
|
158
|
-
# Collect up the loaded gems
|
159
|
-
##
|
160
|
-
def report_gem_in_use
|
161
|
-
platform_info = {}
|
162
|
-
if defined?(Gem) && Gem.respond_to?(:loaded_specs)
|
163
|
-
Gem.loaded_specs.each_pair { |k, v| platform_info["Ruby.#{k}.Version"] = v.version.to_s }
|
164
|
-
else
|
165
|
-
platform_info.merge!(legacy_build_init_report)
|
166
|
-
end
|
167
|
-
platform_info
|
168
|
-
end
|
169
|
-
|
170
|
-
##
|
171
|
-
# get extension library version by looking at the VERSION file
|
172
|
-
# oboe not loaded yet, can't use oboe_api function to read oboe VERSION
|
173
|
-
##
|
174
|
-
def extension_lib_version
|
175
|
-
gem_location = Gem::Specification.find_by_name('solarwinds_apm')
|
176
|
-
clib_version_file = File.join(gem_location&.gem_dir, 'ext', 'oboe_metal', 'src', 'VERSION')
|
177
|
-
File.read(clib_version_file).strip
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# Setup an alias
|
184
|
-
SolarWindsApm = SolarWindsAPM
|
185
|
-
SolarwindsApm = SolarWindsAPM
|
186
|
-
SolarwindsAPM = SolarWindsAPM
|
187
|
-
Solarwindsapm = SolarWindsAPM
|
@@ -1,24 +0,0 @@
|
|
1
|
-
-----BEGIN CERTIFICATE-----
|
2
|
-
MIID8TCCAtmgAwIBAgIJAMoDz7Npas2/MA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD
|
3
|
-
VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j
|
4
|
-
aXNjbzEVMBMGA1UECgwMTGlicmF0byBJbmMuMRUwEwYDVQQDDAxBcHBPcHRpY3Mg
|
5
|
-
Q0ExJDAiBgkqhkiG9w0BCQEWFXN1cHBvcnRAYXBwb3B0aWNzLmNvbTAeFw0xNzA5
|
6
|
-
MTUyMjAxMzlaFw0yNzA5MTMyMjAxMzlaMIGOMQswCQYDVQQGEwJVUzETMBEGA1UE
|
7
|
-
CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEVMBMGA1UECgwM
|
8
|
-
TGlicmF0byBJbmMuMRUwEwYDVQQDDAxBcHBPcHRpY3MgQ0ExJDAiBgkqhkiG9w0B
|
9
|
-
CQEWFXN1cHBvcnRAYXBwb3B0aWNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
10
|
-
ADCCAQoCggEBAOxO0wsGba3iI4r3L5BMST0rAO/gGaUhpQre6nRwVTmPCnLw1bmn
|
11
|
-
GdiFgYv/oRRwU+VieumHSQqoOmyFrg+ajGmvUDp2WqQ0It+XhcbaHFiAp2H7+mLf
|
12
|
-
cUH6S43/em0WUxZHeRzRupRDyO1bX6Hh2jgxykivlFrn5HCIQD5Hx1/SaZoW9v2n
|
13
|
-
oATCbgFOiPW6kU/AVs4R0VBujon13HCehVelNKkazrAEBT1i6RvdOB6aQQ32seW+
|
14
|
-
gLV5yVWSPEJvA9ZJqad/nQ8EQUMSSlVN191WOjp4bGpkJE1svs7NmM+Oja50W56l
|
15
|
-
qOH5eWermr/8qWjdPlDJ+I0VkgN0UyHVuRECAwEAAaNQME4wHQYDVR0OBBYEFOuL
|
16
|
-
KDTFhRQXwlBRxhPqhukrNYeRMB8GA1UdIwQYMBaAFOuLKDTFhRQXwlBRxhPqhukr
|
17
|
-
NYeRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJQtH446NZhjusy6
|
18
|
-
iCyvmnD95ybfNPDpjHmNx5n9Y6w9n+9y1o3732HUJE+WjvbLS3h1o7wujGKMcRJn
|
19
|
-
7I7eTDd26ZhLvnh5/AitYjdxrtUkQDgyxwLFJKhZu0ik2vXqj0fL961/quJL8Gyp
|
20
|
-
hNj3Nf7WMohQMSohEmCCX2sHyZGVGYmQHs5omAtkH/NNySqmsWNcpgd3M0aPDRBZ
|
21
|
-
5VFreOSGKBTJnoLNqods/S9RV0by84hm3j6aQ/tMDIVE9VCJtrE6evzC0MWyVFwR
|
22
|
-
ftgwcxyEq5SkiR+6BCwdzAMqADV37TzXDHLjwSrMIrgLV5xZM20Kk6chxI5QAr/f
|
23
|
-
7tsqAxw=
|
24
|
-
-----END CERTIFICATE-----
|
@@ -1,222 +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
|
-
require 'singleton'
|
10
|
-
require 'uri'
|
11
|
-
|
12
|
-
require_relative 'support/service_key_checker'
|
13
|
-
|
14
|
-
module SolarWindsAPM
|
15
|
-
# OboeInitOptions
|
16
|
-
class OboeInitOptions
|
17
|
-
include Singleton
|
18
|
-
|
19
|
-
attr_reader :reporter, :host, :service_name, :ec2_md_timeout, :grpc_proxy, :lambda_env # exposing these mainly for testing
|
20
|
-
|
21
|
-
def initialize
|
22
|
-
# determining the lambda env based on env var (not used in array_for_oboe for oboe initialization)
|
23
|
-
@lambda_env = determine_lambda
|
24
|
-
# optional hostname alias
|
25
|
-
@hostname_alias = ENV['SW_APM_HOSTNAME_ALIAS'] || SolarWindsAPM::Config[:hostname_alias] || ''
|
26
|
-
# level at which log messages will be written to log file (0-6)
|
27
|
-
@debug_level = (ENV['SW_APM_DEBUG_LEVEL'] || SolarWindsAPM::Config[:debug_level] || 3).to_i
|
28
|
-
# file name including path for log file
|
29
|
-
# TODO eventually find better way to combine ruby and oboe logs
|
30
|
-
@log_file_path = ENV['SW_APM_LOG_FILEPATH'] || ''
|
31
|
-
# maximum number of transaction names to track
|
32
|
-
@max_transactions = (ENV['SW_APM_MAX_TRANSACTIONS'] || -1).to_i
|
33
|
-
# maximum wait time for flushing data before terminating in milli seconds
|
34
|
-
@max_flush_wait_time = (ENV['SW_APM_FLUSH_MAX_WAIT_TIME'] || -1).to_i
|
35
|
-
# events flush timeout in seconds (threshold for batching messages before sending off)
|
36
|
-
@events_flush_interval = (ENV['SW_APM_EVENTS_FLUSH_INTERVAL'] || -1).to_i
|
37
|
-
# events flush batch size in KB (threshold for batching messages before sending off)
|
38
|
-
@event_flush_batch_size = (ENV['SW_APM_EVENTS_FLUSH_BATCH_SIZE'] || -1).to_i
|
39
|
-
|
40
|
-
# the reporter to be used (ssl, upd, file, null)
|
41
|
-
# collector endpoint (reporter=ssl), udp address (reporter=udp), or file path (reporter=file)
|
42
|
-
@reporter, @host = reporter_and_host
|
43
|
-
|
44
|
-
# the service key
|
45
|
-
@service_key = read_and_validate_service_key
|
46
|
-
# certificate content
|
47
|
-
@certificates = read_certificates
|
48
|
-
# size of the message buffer
|
49
|
-
@buffer_size = (ENV['SW_APM_BUFSIZE'] || -1).to_i
|
50
|
-
# flag indicating if trace metrics reporting should be enabled (default) or disabled
|
51
|
-
@trace_metrics = (ENV['SW_APM_TRACE_METRICS'] || -1).to_i
|
52
|
-
# the histogram precision (only for ssl)
|
53
|
-
@histogram_precision = (ENV['SW_APM_HISTOGRAM_PRECISION'] || -1).to_i
|
54
|
-
# custom token bucket capacity
|
55
|
-
@token_bucket_capacity = (ENV['SW_APM_TOKEN_BUCKET_CAPACITY'] || -1).to_i
|
56
|
-
# custom token bucket rate
|
57
|
-
@token_bucket_rate = (ENV['SW_APM_TOKEN_BUCKET_RATE'] || -1).to_i
|
58
|
-
# use single files in file reporter for each event
|
59
|
-
@file_single = ENV['SW_APM_REPORTER_FILE_SINGLE'].to_s.casecmp('true').zero? ? 1 : 0
|
60
|
-
# timeout for ec2 metadata
|
61
|
-
@ec2_md_timeout = read_and_validate_ec2_md_timeout
|
62
|
-
@grpc_proxy = read_and_validate_proxy
|
63
|
-
# hardcoded arg for lambda (lambda not supported yet)
|
64
|
-
# hardcoded arg for grpc hack
|
65
|
-
# hardcoded arg for trace id format to use w3c format
|
66
|
-
# flag for format of metric (0 = Both; 1 = AppOptics only; 2 = SWO only; default = 0)
|
67
|
-
@metric_format = determine_the_metric_model
|
68
|
-
# log type (0 = stderr; 1 = stdout; 2 = file; 3 = null; 4 = disabled; default = 0)
|
69
|
-
@log_type = determine_oboe_log_type
|
70
|
-
# after fork enablement (0 = disable; 1 = enabled, parent process will not init oboe but fork child process will init oboe; default = 0)
|
71
|
-
@after_fork = determine_oboe_after_fork
|
72
|
-
end
|
73
|
-
|
74
|
-
# for testing with changed ENV vars
|
75
|
-
def re_init
|
76
|
-
initialize
|
77
|
-
end
|
78
|
-
|
79
|
-
def array_for_oboe
|
80
|
-
[
|
81
|
-
@hostname_alias, # 0
|
82
|
-
@debug_level, # 1
|
83
|
-
@log_file_path, # 2
|
84
|
-
@max_transactions, # 3
|
85
|
-
@max_flush_wait_time, # 4
|
86
|
-
@events_flush_interval, # 5
|
87
|
-
@event_flush_batch_size, # 6
|
88
|
-
@reporter, # 7
|
89
|
-
@host, # 8
|
90
|
-
@service_key, # 9
|
91
|
-
@certificates, # 10
|
92
|
-
@buffer_size, # 11
|
93
|
-
@trace_metrics, # 12
|
94
|
-
@histogram_precision, # 13
|
95
|
-
@token_bucket_capacity, # 14
|
96
|
-
@token_bucket_rate, # 15
|
97
|
-
@file_single, # 16
|
98
|
-
@ec2_md_timeout, # 17
|
99
|
-
@grpc_proxy, # 18
|
100
|
-
0, # 19 arg for lambda (no lambda for ruby yet)
|
101
|
-
@metric_format, # 20
|
102
|
-
@log_type, # 21
|
103
|
-
@after_fork # 22
|
104
|
-
]
|
105
|
-
end
|
106
|
-
|
107
|
-
def service_key_ok?
|
108
|
-
!@service_key.empty? || @reporter != 'ssl'
|
109
|
-
end
|
110
|
-
|
111
|
-
private
|
112
|
-
|
113
|
-
def reporter_and_host
|
114
|
-
reporter = ENV['SW_APM_REPORTER'] || 'ssl'
|
115
|
-
|
116
|
-
host = case reporter
|
117
|
-
when 'ssl', 'file'
|
118
|
-
ENV['SW_APM_COLLECTOR'] || ''
|
119
|
-
else
|
120
|
-
''
|
121
|
-
end
|
122
|
-
|
123
|
-
host = sanitize_collector_uri(host) unless reporter == 'file'
|
124
|
-
[reporter, host]
|
125
|
-
end
|
126
|
-
|
127
|
-
def read_and_validate_service_key
|
128
|
-
service_key_checker = SolarWindsAPM::ServiceKeyChecker.new(@reporter, @lambda_env)
|
129
|
-
service_key = service_key_checker.read_and_validate_service_key
|
130
|
-
@service_name = service_key.split(':', 2).last # instance variable used in testing
|
131
|
-
service_key
|
132
|
-
end
|
133
|
-
|
134
|
-
def read_and_validate_ec2_md_timeout
|
135
|
-
timeout = ENV['SW_APM_EC2_METADATA_TIMEOUT'] || SolarWindsAPM::Config[:ec2_metadata_timeout]
|
136
|
-
return 1000 unless timeout.is_a?(Integer) || timeout =~ /^\d+$/
|
137
|
-
|
138
|
-
timeout = timeout.to_i
|
139
|
-
timeout.between?(0, 3000) ? timeout : 1000
|
140
|
-
end
|
141
|
-
|
142
|
-
def read_and_validate_proxy
|
143
|
-
proxy = ENV['SW_APM_PROXY'] || SolarWindsAPM::Config[:http_proxy] || ''
|
144
|
-
return proxy if proxy == ''
|
145
|
-
|
146
|
-
unless %r{http://.*:\d+$}.match?(proxy)
|
147
|
-
SolarWindsAPM.logger.error { "[#{self.class}/#{__method__}] SW_APM_PROXY/http_proxy doesn't start with 'http://', #{proxy}" }
|
148
|
-
return '' # try without proxy, it may work, shouldn't crash but may not report
|
149
|
-
end
|
150
|
-
|
151
|
-
proxy
|
152
|
-
end
|
153
|
-
|
154
|
-
def read_certificates
|
155
|
-
certificate = ''
|
156
|
-
|
157
|
-
file = appoptics_collector? ? "#{__dir__}/cert/star.appoptics.com.issuer.crt" : ENV.fetch('SW_APM_TRUSTEDPATH', nil)
|
158
|
-
return certificate if file.nil? || file&.empty?
|
159
|
-
|
160
|
-
begin
|
161
|
-
certificate = File.read(file)
|
162
|
-
rescue StandardError => e
|
163
|
-
SolarWindsAPM.logger.error do
|
164
|
-
"[#{self.class}/#{__method__}] certificates: #{file} doesn't exist or caused by #{e.message}."
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
certificate
|
169
|
-
end
|
170
|
-
|
171
|
-
def determine_the_metric_model
|
172
|
-
appoptics_collector? ? 1 : 2
|
173
|
-
end
|
174
|
-
|
175
|
-
def appoptics_collector?
|
176
|
-
allowed_uri = ['collector.appoptics.com', 'collector-stg.appoptics.com',
|
177
|
-
'collector.appoptics.com:443', 'collector-stg.appoptics.com:443']
|
178
|
-
|
179
|
-
(allowed_uri.include? ENV.fetch('SW_APM_COLLECTOR', nil))
|
180
|
-
end
|
181
|
-
|
182
|
-
def java_collector?(uri)
|
183
|
-
java_collector_regex = /java-collector:\d+/
|
184
|
-
uri.match?(java_collector_regex)
|
185
|
-
end
|
186
|
-
|
187
|
-
def sanitize_collector_uri(uri)
|
188
|
-
return uri if uri.nil? || uri.empty?
|
189
|
-
return uri if java_collector?(uri)
|
190
|
-
|
191
|
-
begin
|
192
|
-
sanitized_uri = ::URI.parse("http://#{uri}").host
|
193
|
-
return sanitized_uri unless sanitized_uri.nil?
|
194
|
-
rescue StandardError => e
|
195
|
-
SolarWindsAPM.logger.error do
|
196
|
-
"[#{self.class}/#{__method__}] uri for collector #{uri} is malformat. Error: #{e.message}"
|
197
|
-
end
|
198
|
-
end
|
199
|
-
''
|
200
|
-
end
|
201
|
-
|
202
|
-
def determine_oboe_log_type
|
203
|
-
log_type = 0
|
204
|
-
log_type = 2 unless ENV['SW_APM_LOG_FILEPATH'].to_s.empty?
|
205
|
-
log_type = 4 if @debug_level == -1
|
206
|
-
log_type
|
207
|
-
end
|
208
|
-
|
209
|
-
def determine_lambda
|
210
|
-
if ENV['LAMBDA_TASK_ROOT'].to_s.empty? && ENV['AWS_LAMBDA_FUNCTION_NAME'].to_s.empty?
|
211
|
-
false
|
212
|
-
else
|
213
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] lambda environment - LAMBDA_TASK_ROOT: #{ENV.fetch('LAMBDA_TASK_ROOT', nil)}; AWS_LAMBDA_FUNCTION_NAME: #{ENV.fetch('AWS_LAMBDA_FUNCTION_NAME', nil)}" }
|
214
|
-
true
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
def determine_oboe_after_fork
|
219
|
-
ENV['SW_APM_ENABLE_AFTER_FORK'].to_s == 'true' ? 1 : 0
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
@@ -1,239 +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
|
-
# SolarWindsExporter
|
12
|
-
class SolarWindsExporter
|
13
|
-
SUCCESS = ::OpenTelemetry::SDK::Trace::Export::SUCCESS # ::OpenTelemetry #=> the OpenTelemetry at top level (to ignore SolarWindsAPM)
|
14
|
-
FAILURE = ::OpenTelemetry::SDK::Trace::Export::FAILURE
|
15
|
-
|
16
|
-
private_constant(:SUCCESS, :FAILURE)
|
17
|
-
|
18
|
-
def initialize(txn_manager: nil)
|
19
|
-
@shutdown = false
|
20
|
-
@txn_manager = txn_manager
|
21
|
-
@reporter = SolarWindsAPM::Reporter
|
22
|
-
@context = SolarWindsAPM::Context
|
23
|
-
@metadata = SolarWindsAPM::Metadata
|
24
|
-
@version_cache = {}
|
25
|
-
end
|
26
|
-
|
27
|
-
def export(span_data, timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
|
28
|
-
return FAILURE if @shutdown
|
29
|
-
|
30
|
-
status = SUCCESS
|
31
|
-
span_data.each do |data|
|
32
|
-
status = log_span_data(data)
|
33
|
-
end
|
34
|
-
|
35
|
-
status
|
36
|
-
end
|
37
|
-
|
38
|
-
def force_flush(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
|
39
|
-
SUCCESS
|
40
|
-
end
|
41
|
-
|
42
|
-
def shutdown(timeout: nil) # rubocop:disable Lint/UnusedMethodArgument
|
43
|
-
@shutdown = true
|
44
|
-
SUCCESS
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def log_span_data(span_data)
|
50
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] span_data: #{span_data.inspect}\n" }
|
51
|
-
|
52
|
-
md = build_meta_data(span_data)
|
53
|
-
event = nil
|
54
|
-
if span_data.parent_span_id == ::OpenTelemetry::Trace::INVALID_SPAN_ID
|
55
|
-
event = @context.createEntry(md, (span_data.start_timestamp.to_i / 1000).to_i)
|
56
|
-
add_info_transaction_name(span_data, event)
|
57
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Start a new trace." }
|
58
|
-
else
|
59
|
-
parent_md = build_meta_data(span_data, parent: true)
|
60
|
-
event = @context.createEntry(md, (span_data.start_timestamp.to_i / 1000).to_i, parent_md)
|
61
|
-
SolarWindsAPM.logger.debug do
|
62
|
-
"[#{self.class}/#{__method__}] Continue trace from parent metadata: #{parent_md.toString}."
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
layer_name = "#{span_data.kind}:#{span_data.name}"
|
67
|
-
event.addInfo('Layer', layer_name)
|
68
|
-
event.addInfo('sw.span_kind', span_data.kind.to_s)
|
69
|
-
event.addInfo('Language', 'Ruby')
|
70
|
-
|
71
|
-
add_instrumentation_scope(event, span_data)
|
72
|
-
add_instrumented_framework(event, span_data)
|
73
|
-
add_span_data_attributes(event, span_data.attributes) if span_data.attributes
|
74
|
-
|
75
|
-
event.addInfo(SolarWindsAPM::Constants::INTL_SWO_OTEL_STATUS, span_data.status.ok? ? 'OK' : 'ERROR')
|
76
|
-
unless span_data.status.description.empty?
|
77
|
-
event.addInfo(SolarWindsAPM::Constants::INTL_SWO_OTEL_STATUS_DESCRIPTION,
|
78
|
-
span_data.status.description)
|
79
|
-
end
|
80
|
-
|
81
|
-
@reporter.send_report(event, with_system_timestamp: false)
|
82
|
-
|
83
|
-
# info / exception event
|
84
|
-
span_data.events&.each do |span_data_event|
|
85
|
-
span_data_event.name == 'exception' ? report_exception_event(span_data_event) : report_info_event(span_data_event)
|
86
|
-
end
|
87
|
-
|
88
|
-
event = @context.createExit((span_data.end_timestamp.to_i / 1000).to_i)
|
89
|
-
event.addInfo('Layer', layer_name)
|
90
|
-
@reporter.send_report(event, with_system_timestamp: false)
|
91
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Exit a trace: #{event.metadataString}" }
|
92
|
-
SUCCESS
|
93
|
-
rescue StandardError => e
|
94
|
-
SolarWindsAPM.logger.info { "[#{self.class}/#{__method__}] exporter error: \n #{e.message} #{e.backtrace}\n" }
|
95
|
-
FAILURE
|
96
|
-
end
|
97
|
-
|
98
|
-
##
|
99
|
-
# extract the span_data.attributes (OpenTelemetry::SDK::Trace::SpanData.attributes)
|
100
|
-
def add_span_data_attributes(event, span_attributes)
|
101
|
-
target = 'http.target'
|
102
|
-
attributes = span_attributes.dup
|
103
|
-
attributes[target] = attributes[target].split('?').first if attributes[target] && SolarWindsAPM::Config[:log_args] == false
|
104
|
-
attributes.each { |k, v| event.addInfo(k, v) }
|
105
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] span_data attributes added: #{attributes.inspect}" }
|
106
|
-
end
|
107
|
-
|
108
|
-
##
|
109
|
-
# get instrumentation scope data: scope name and version.
|
110
|
-
# the version if the opentelemetry-instrumentation-* gem version
|
111
|
-
def add_instrumentation_scope(event, span_data)
|
112
|
-
scope_name = ''
|
113
|
-
scope_version = ''
|
114
|
-
if span_data.instrumentation_scope
|
115
|
-
scope_name = span_data.instrumentation_scope.name if span_data.instrumentation_scope.name
|
116
|
-
scope_version = span_data.instrumentation_scope.version if span_data.instrumentation_scope.version
|
117
|
-
end
|
118
|
-
event.addInfo(SolarWindsAPM::Constants::INTL_SWO_OTEL_SCOPE_NAME, scope_name)
|
119
|
-
event.addInfo(SolarWindsAPM::Constants::INTL_SWO_OTEL_SCOPE_VERSION, scope_version)
|
120
|
-
end
|
121
|
-
|
122
|
-
##
|
123
|
-
# add the gem library version to event
|
124
|
-
# p.s. gem library version is not same as the opentelemetry-instrumentation-* gem version
|
125
|
-
def add_instrumented_framework(event, span_data)
|
126
|
-
scope_name = span_data.instrumentation_scope.name
|
127
|
-
scope_name = scope_name.downcase if scope_name
|
128
|
-
return unless scope_name&.include? 'opentelemetry::instrumentation'
|
129
|
-
|
130
|
-
framework = scope_name.split('::')[2..]&.join('::')
|
131
|
-
return if framework.nil? || framework.empty?
|
132
|
-
|
133
|
-
framework = normalize_framework_name(framework)
|
134
|
-
framework_version = check_framework_version(framework)
|
135
|
-
SolarWindsAPM.logger.debug do
|
136
|
-
"[#{self.class}/#{__method__}] #{span_data.instrumentation_scope.name} with #{framework} and version #{framework_version}"
|
137
|
-
end
|
138
|
-
event.addInfo("Ruby.#{framework}.Version", framework_version) unless framework_version.nil?
|
139
|
-
end
|
140
|
-
|
141
|
-
##
|
142
|
-
# helper function that extract gem library version for func add_instrumented_framework
|
143
|
-
def check_framework_version(framework)
|
144
|
-
framework_version = nil
|
145
|
-
if @version_cache.key?(framework)
|
146
|
-
|
147
|
-
framework_version = @version_cache[framework]
|
148
|
-
else
|
149
|
-
|
150
|
-
begin
|
151
|
-
require framework
|
152
|
-
framework_version = Gem.loaded_specs[version_framework_name(framework)].version.to_s
|
153
|
-
rescue LoadError => e
|
154
|
-
SolarWindsAPM.logger.debug do
|
155
|
-
"[#{self.class}/#{__method__}] couldn't load #{framework} with error #{e.message}; skip"
|
156
|
-
end
|
157
|
-
rescue StandardError => e
|
158
|
-
SolarWindsAPM.logger.debug do
|
159
|
-
"[#{self.class}/#{__method__}] couldn't find #{framework} with error #{e.message}; skip"
|
160
|
-
end
|
161
|
-
ensure
|
162
|
-
@version_cache[framework] = framework_version
|
163
|
-
end
|
164
|
-
end
|
165
|
-
SolarWindsAPM.logger.debug do
|
166
|
-
"[#{self.class}/#{__method__}] current framework version cached: #{@version_cache.inspect}"
|
167
|
-
end
|
168
|
-
framework_version
|
169
|
-
end
|
170
|
-
|
171
|
-
##
|
172
|
-
# helper function that convert opentelemetry instrumentation name to gem library understandable
|
173
|
-
def normalize_framework_name(framework)
|
174
|
-
case framework
|
175
|
-
when 'net::http'
|
176
|
-
'net/http'
|
177
|
-
else
|
178
|
-
framework
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
##
|
183
|
-
# helper function that convert opentelemetry instrumentation name to gem library understandable
|
184
|
-
def version_framework_name(framework)
|
185
|
-
case framework
|
186
|
-
when 'net/http'
|
187
|
-
'net-http'
|
188
|
-
else
|
189
|
-
framework
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
##
|
194
|
-
# Add transaction name from cache to root span then removes from cache
|
195
|
-
def add_info_transaction_name(span_data, event)
|
196
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] transaction manager: #{@txn_manager.inspect}." }
|
197
|
-
trace_span_id = "#{span_data.hex_trace_id}-#{span_data.hex_span_id}"
|
198
|
-
txname = @txn_manager.get(trace_span_id) || ''
|
199
|
-
event.addInfo('TransactionName', txname)
|
200
|
-
@txn_manager.del(trace_span_id)
|
201
|
-
end
|
202
|
-
|
203
|
-
##
|
204
|
-
# report exception event
|
205
|
-
def report_exception_event(span_event)
|
206
|
-
event = @context.createEvent((span_event.timestamp.to_i / 1000).to_i)
|
207
|
-
event.addInfo('Label', 'error')
|
208
|
-
event.addInfo('Spec', 'error')
|
209
|
-
|
210
|
-
unless span_event.attributes.nil?
|
211
|
-
attributes = span_event.attributes.dup
|
212
|
-
attributes.delete('exception.type') if event.addInfo('ErrorClass', attributes['exception.type'])
|
213
|
-
attributes.delete('exception.message') if event.addInfo('ErrorMsg', attributes['exception.message'])
|
214
|
-
attributes.delete('exception.stacktrace') if event.addInfo('Backtrace', attributes['exception.stacktrace'])
|
215
|
-
attributes.each { |key, value| event.addInfo(key, value) }
|
216
|
-
end
|
217
|
-
|
218
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] exception event #{event.metadataString}" }
|
219
|
-
@reporter.send_report(event, with_system_timestamp: false)
|
220
|
-
end
|
221
|
-
|
222
|
-
##
|
223
|
-
# report non-exception/info event
|
224
|
-
def report_info_event(span_event)
|
225
|
-
event = @context.createEvent((span_event.timestamp.to_i / 1000).to_i)
|
226
|
-
event.addInfo('Label', 'info')
|
227
|
-
span_event.attributes&.each { |key, value| event.addInfo(key, value) }
|
228
|
-
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] info event #{event.metadataString}" }
|
229
|
-
@reporter.send_report(event, with_system_timestamp: false)
|
230
|
-
end
|
231
|
-
|
232
|
-
def build_meta_data(span_data, parent: false)
|
233
|
-
flag = span_data.trace_flags.sampled? ? 1 : 0
|
234
|
-
xtr = parent == false ? "00-#{span_data.hex_trace_id}-#{span_data.hex_span_id}-0#{flag}" : "00-#{span_data.hex_trace_id}-#{span_data.hex_parent_span_id}-0#{flag}"
|
235
|
-
@metadata.fromString(xtr)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|