solarwinds_apm 5.0.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.
- checksums.yaml +7 -0
- data/.dockerignore +5 -0
- data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
- data/.github/workflows/build_and_release_gem.yml +112 -0
- data/.github/workflows/build_for_packagecloud.yml +70 -0
- data/.github/workflows/docker-images.yml +47 -0
- data/.github/workflows/run_cpluplus_tests.yml +73 -0
- data/.github/workflows/run_tests.yml +155 -0
- data/.github/workflows/scripts/test_install.rb +23 -0
- data/.github/workflows/swig/swig-v4.0.2.tar.gz +0 -0
- data/.github/workflows/test_on_4_linux.yml +161 -0
- data/.gitignore +39 -0
- data/.rubocop.yml +29 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +769 -0
- data/CONFIG.md +31 -0
- data/Gemfile +14 -0
- data/LICENSE +202 -0
- data/README.md +383 -0
- data/bin/solarwinds_apm_config +15 -0
- data/examples/prepend.rb +13 -0
- data/examples/sdk_examples.rb +158 -0
- data/ext/oboe_metal/README.md +69 -0
- data/ext/oboe_metal/extconf.rb +141 -0
- data/ext/oboe_metal/extconf_local.rb +75 -0
- data/ext/oboe_metal/lib/.keep +0 -0
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.0.0.0.sha256 +1 -0
- data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.0.0.0.sha256 +1 -0
- data/ext/oboe_metal/noop/noop.c +8 -0
- data/ext/oboe_metal/src/README.md +6 -0
- data/ext/oboe_metal/src/VERSION +2 -0
- data/ext/oboe_metal/src/bson/bson.h +220 -0
- data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
- data/ext/oboe_metal/src/frames.cc +247 -0
- data/ext/oboe_metal/src/frames.h +40 -0
- data/ext/oboe_metal/src/init_solarwinds_apm.cc +21 -0
- data/ext/oboe_metal/src/logging.cc +95 -0
- data/ext/oboe_metal/src/logging.h +35 -0
- data/ext/oboe_metal/src/oboe.h +1169 -0
- data/ext/oboe_metal/src/oboe_api.cpp +658 -0
- data/ext/oboe_metal/src/oboe_api.hpp +433 -0
- data/ext/oboe_metal/src/oboe_debug.h +59 -0
- data/ext/oboe_metal/src/oboe_swig_wrap.cc +7562 -0
- data/ext/oboe_metal/src/profiling.cc +435 -0
- data/ext/oboe_metal/src/profiling.h +78 -0
- data/ext/oboe_metal/test/CMakeLists.txt +53 -0
- data/ext/oboe_metal/test/FindGMock.cmake +43 -0
- data/ext/oboe_metal/test/README.md +56 -0
- data/ext/oboe_metal/test/frames_test.cc +164 -0
- data/ext/oboe_metal/test/profiling_test.cc +93 -0
- data/ext/oboe_metal/test/ruby_inc_dir.rb +8 -0
- data/ext/oboe_metal/test/ruby_prefix.rb +8 -0
- data/ext/oboe_metal/test/ruby_test_helper.rb +67 -0
- data/ext/oboe_metal/test/test.h +11 -0
- data/ext/oboe_metal/test/test_main.cc +32 -0
- data/init.rb +4 -0
- data/lib/oboe.rb +7 -0
- data/lib/oboe_metal.rb +172 -0
- data/lib/rails/generators/solarwinds_apm/install_generator.rb +47 -0
- data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +424 -0
- data/lib/solarwinds_apm/api/layerinit.rb +41 -0
- data/lib/solarwinds_apm/api/logging.rb +356 -0
- data/lib/solarwinds_apm/api/memcache.rb +37 -0
- data/lib/solarwinds_apm/api/metrics.rb +63 -0
- data/lib/solarwinds_apm/api/util.rb +98 -0
- data/lib/solarwinds_apm/api.rb +21 -0
- data/lib/solarwinds_apm/base.rb +160 -0
- data/lib/solarwinds_apm/config.rb +301 -0
- data/lib/solarwinds_apm/frameworks/grape.rb +96 -0
- data/lib/solarwinds_apm/frameworks/padrino.rb +78 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/action_controller.rb +100 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/action_controller5.rb +50 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/action_view.rb +88 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/active_record.rb +26 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +22 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +103 -0
- data/lib/solarwinds_apm/frameworks/rails/inst/logger_formatters.rb +14 -0
- data/lib/solarwinds_apm/frameworks/rails.rb +100 -0
- data/lib/solarwinds_apm/frameworks/sinatra.rb +96 -0
- data/lib/solarwinds_apm/inst/bunny-client.rb +157 -0
- data/lib/solarwinds_apm/inst/bunny-consumer.rb +102 -0
- data/lib/solarwinds_apm/inst/curb.rb +288 -0
- data/lib/solarwinds_apm/inst/dalli.rb +89 -0
- data/lib/solarwinds_apm/inst/delayed_job.rb +100 -0
- data/lib/solarwinds_apm/inst/excon.rb +113 -0
- data/lib/solarwinds_apm/inst/faraday.rb +96 -0
- data/lib/solarwinds_apm/inst/graphql.rb +206 -0
- data/lib/solarwinds_apm/inst/grpc_client.rb +147 -0
- data/lib/solarwinds_apm/inst/grpc_server.rb +119 -0
- data/lib/solarwinds_apm/inst/httpclient.rb +181 -0
- data/lib/solarwinds_apm/inst/logger_formatter.rb +46 -0
- data/lib/solarwinds_apm/inst/logging_log_event.rb +24 -0
- data/lib/solarwinds_apm/inst/lumberjack_formatter.rb +9 -0
- data/lib/solarwinds_apm/inst/memcached.rb +86 -0
- data/lib/solarwinds_apm/inst/mongo.rb +246 -0
- data/lib/solarwinds_apm/inst/mongo2.rb +225 -0
- data/lib/solarwinds_apm/inst/moped.rb +466 -0
- data/lib/solarwinds_apm/inst/net_http.rb +60 -0
- data/lib/solarwinds_apm/inst/rack.rb +217 -0
- data/lib/solarwinds_apm/inst/rack_cache.rb +35 -0
- data/lib/solarwinds_apm/inst/redis.rb +273 -0
- data/lib/solarwinds_apm/inst/resque.rb +129 -0
- data/lib/solarwinds_apm/inst/rest-client.rb +43 -0
- data/lib/solarwinds_apm/inst/sequel.rb +241 -0
- data/lib/solarwinds_apm/inst/sidekiq-client.rb +63 -0
- data/lib/solarwinds_apm/inst/sidekiq-worker.rb +64 -0
- data/lib/solarwinds_apm/inst/typhoeus.rb +90 -0
- data/lib/solarwinds_apm/instrumentation.rb +22 -0
- data/lib/solarwinds_apm/loading.rb +65 -0
- data/lib/solarwinds_apm/logger.rb +14 -0
- data/lib/solarwinds_apm/noop/README.md +9 -0
- data/lib/solarwinds_apm/noop/context.rb +26 -0
- data/lib/solarwinds_apm/noop/metadata.rb +25 -0
- data/lib/solarwinds_apm/noop/profiling.rb +21 -0
- data/lib/solarwinds_apm/oboe_init_options.rb +191 -0
- data/lib/solarwinds_apm/ruby.rb +35 -0
- data/lib/solarwinds_apm/sdk/current_trace_info.rb +123 -0
- data/lib/solarwinds_apm/sdk/custom_metrics.rb +94 -0
- data/lib/solarwinds_apm/sdk/logging.rb +37 -0
- data/lib/solarwinds_apm/sdk/trace_context_headers.rb +69 -0
- data/lib/solarwinds_apm/sdk/tracing.rb +432 -0
- data/lib/solarwinds_apm/support/profiling.rb +22 -0
- data/lib/solarwinds_apm/support/trace_context.rb +53 -0
- data/lib/solarwinds_apm/support/trace_state.rb +69 -0
- data/lib/solarwinds_apm/support/trace_string.rb +89 -0
- data/lib/solarwinds_apm/support/transaction_metrics.rb +67 -0
- data/lib/solarwinds_apm/support/transaction_settings.rb +233 -0
- data/lib/solarwinds_apm/support/x_trace_options.rb +113 -0
- data/lib/solarwinds_apm/support.rb +12 -0
- data/lib/solarwinds_apm/support_report.rb +113 -0
- data/lib/solarwinds_apm/test.rb +165 -0
- data/lib/solarwinds_apm/thread_local.rb +26 -0
- data/lib/solarwinds_apm/util.rb +334 -0
- data/lib/solarwinds_apm/version.rb +17 -0
- data/lib/solarwinds_apm.rb +72 -0
- data/log/.keep +0 -0
- data/log/postgresql/.keep +0 -0
- data/solarwinds_apm.gemspec +52 -0
- data/yardoc_frontpage.md +24 -0
- metadata +228 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Copyright (c) 2019 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'singleton'
|
|
5
|
+
|
|
6
|
+
module SolarWindsAPM
|
|
7
|
+
|
|
8
|
+
class OboeInitOptions
|
|
9
|
+
include Singleton
|
|
10
|
+
|
|
11
|
+
attr_reader :reporter, :host, :service_name, :ec2_md_timeout, :grpc_proxy # exposing these mainly for testing
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
# optional hostname alias
|
|
15
|
+
@hostname_alias = ENV['SW_APM_HOSTNAME_ALIAS'] || SolarWindsAPM::Config[:hostname_alias] || ''
|
|
16
|
+
# level at which log messages will be written to log file (0-6)
|
|
17
|
+
@debug_level = (ENV['SW_APM_DEBUG_LEVEL'] || SolarWindsAPM::Config[:debug_level] || 3).to_i
|
|
18
|
+
# file name including path for log file
|
|
19
|
+
# TODO eventually find better way to combine ruby and oboe logs
|
|
20
|
+
@log_file_path = ENV['SW_APM_LOGFILE'] || ''
|
|
21
|
+
# maximum number of transaction names to track
|
|
22
|
+
@max_transactions = (ENV['SW_APM_MAX_TRANSACTIONS'] || -1).to_i
|
|
23
|
+
# maximum wait time for flushing data before terminating in milli seconds
|
|
24
|
+
@max_flush_wait_time = (ENV['SW_APM_FLUSH_MAX_WAIT_TIME'] || -1).to_i
|
|
25
|
+
# events flush timeout in seconds (threshold for batching messages before sending off)
|
|
26
|
+
@events_flush_interval = (ENV['SW_APM_EVENTS_FLUSH_INTERVAL'] || -1).to_i
|
|
27
|
+
# events flush batch size in KB (threshold for batching messages before sending off)
|
|
28
|
+
@event_flush_batch_size = (ENV['SW_APM_EVENTS_FLUSH_BATCH_SIZE'] || -1).to_i
|
|
29
|
+
|
|
30
|
+
# the reporter to be used (ssl, upd, file, null)
|
|
31
|
+
# collector endpoint (reporter=ssl), udp address (reporter=udp), or file path (reporter=file)
|
|
32
|
+
@reporter, @host = reporter_and_host
|
|
33
|
+
|
|
34
|
+
# the service key
|
|
35
|
+
@service_key = read_and_validate_service_key
|
|
36
|
+
# path to the SSL certificate (only for ssl)
|
|
37
|
+
@trusted_path = ENV['SW_APM_TRUSTEDPATH'] || ''
|
|
38
|
+
# size of the message buffer
|
|
39
|
+
@buffer_size = (ENV['SW_APM_BUFSIZE'] || -1).to_i
|
|
40
|
+
# flag indicating if trace metrics reporting should be enabled (default) or disabled
|
|
41
|
+
@trace_metrics = (ENV['SW_APM_TRACE_METRICS'] || -1).to_i
|
|
42
|
+
# the histogram precision (only for ssl)
|
|
43
|
+
@histogram_precision = (ENV['SW_APM_HISTOGRAM_PRECISION'] || -1).to_i
|
|
44
|
+
# custom token bucket capacity
|
|
45
|
+
@token_bucket_capacity = (ENV['SW_APM_TOKEN_BUCKET_CAPACITY'] || -1).to_i
|
|
46
|
+
# custom token bucket rate
|
|
47
|
+
@token_bucket_rate = (ENV['SW_APM_TOKEN_BUCKET_RATE'] || -1).to_i
|
|
48
|
+
# use single files in file reporter for each event
|
|
49
|
+
@file_single = (ENV['SW_APM_REPORTER_FILE_SINGLE'].to_s.downcase == 'true') ? 1 : 0
|
|
50
|
+
# timeout for ec2 metadata
|
|
51
|
+
@ec2_md_timeout = read_and_validate_ec2_md_timeout
|
|
52
|
+
@grpc_proxy = read_and_validate_proxy
|
|
53
|
+
# hardcoded arg for lambda (lambda not supported yet)
|
|
54
|
+
# hardcoded arg for grpc hack
|
|
55
|
+
# hardcoded arg for trace id format to use w3c format
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def re_init # for testing with changed ENV vars
|
|
59
|
+
initialize
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def array_for_oboe
|
|
63
|
+
[
|
|
64
|
+
@hostname_alias, # 0
|
|
65
|
+
@debug_level, # 1
|
|
66
|
+
@log_file_path, # 2
|
|
67
|
+
@max_transactions, # 3
|
|
68
|
+
@max_flush_wait_time, # 4
|
|
69
|
+
@events_flush_interval, # 5
|
|
70
|
+
@event_flush_batch_size, # 6
|
|
71
|
+
|
|
72
|
+
@reporter, # 7
|
|
73
|
+
@host, # 8
|
|
74
|
+
@service_key, # 9
|
|
75
|
+
@trusted_path, #10
|
|
76
|
+
@buffer_size, #11
|
|
77
|
+
@trace_metrics, #12
|
|
78
|
+
@histogram_precision, #13
|
|
79
|
+
@token_bucket_capacity, #14
|
|
80
|
+
@token_bucket_rate, #15
|
|
81
|
+
@file_single, #16
|
|
82
|
+
@ec2_md_timeout, #17
|
|
83
|
+
@grpc_proxy, #18
|
|
84
|
+
0, #19 arg for lambda (no lambda for ruby yet)
|
|
85
|
+
1, #20 arg for grpc hack, hardcoded to include hack
|
|
86
|
+
1 #21 arg for trace id format to use w3c format
|
|
87
|
+
]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def service_key_ok?
|
|
91
|
+
return !@service_key.empty? || @reporter != 'ssl'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def reporter_and_host
|
|
97
|
+
|
|
98
|
+
reporter = ENV['SW_APM_REPORTER'] || 'ssl'
|
|
99
|
+
# override with 'file', e.g. when running tests
|
|
100
|
+
# changed my mind => set the right reporter in the env when running tests !!!
|
|
101
|
+
# reporter = 'file' if ENV.key?('SW_APM_GEM_TEST')
|
|
102
|
+
|
|
103
|
+
host = ''
|
|
104
|
+
case reporter
|
|
105
|
+
when 'ssl', 'file'
|
|
106
|
+
host = ENV['SW_APM_COLLECTOR'] || ''
|
|
107
|
+
when 'udp'
|
|
108
|
+
host = ENV['SW_APM_COLLECTOR'] ||
|
|
109
|
+
"#{SolarWindsAPM::Config[:reporter_host]}:#{SolarWindsAPM::Config[:reporter_port]}"
|
|
110
|
+
# TODO decide what to do
|
|
111
|
+
# ____ SolarWindsAPM::Config[:reporter_host] and
|
|
112
|
+
# ____ SolarWindsAPM::Config[:reporter_port] were moved here from
|
|
113
|
+
# ____ oboe_metal.rb and are not documented anywhere
|
|
114
|
+
# ____ udp is for internal use only
|
|
115
|
+
when 'null'
|
|
116
|
+
host = ''
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
[reporter, host]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def read_and_validate_service_key
|
|
123
|
+
return '' unless @reporter == 'ssl'
|
|
124
|
+
|
|
125
|
+
service_key = ENV['SW_APM_SERVICE_KEY'] || SolarWindsAPM::Config[:service_key]
|
|
126
|
+
unless service_key
|
|
127
|
+
SolarWindsAPM.logger.error "[solarwinds_apm/oboe_options] SW_APM_SERVICE_KEY not configured."
|
|
128
|
+
return ''
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
match = service_key.match( /([^:]+)(:{0,1})(.*)/ )
|
|
132
|
+
token = match[1]
|
|
133
|
+
service_name = match[3]
|
|
134
|
+
|
|
135
|
+
return '' unless validate_token(token)
|
|
136
|
+
return '' unless validate_transform_service_name(service_name)
|
|
137
|
+
|
|
138
|
+
return "#{token}:#{service_name}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def validate_token(token)
|
|
142
|
+
if (token !~ /^[0-9a-zA-Z_-]{71}$/) && ENV['SW_APM_COLLECTOR'] !~ /java-collector:1222/
|
|
143
|
+
masked = "#{token[0..3]}...#{token[-4..-1]}"
|
|
144
|
+
SolarWindsAPM.logger.error "[solarwinds_apm/oboe_options] SW_APM_SERVICE_KEY problem. API Token in wrong format. Masked token: #{masked}"
|
|
145
|
+
return false
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
true
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def validate_transform_service_name(service_name)
|
|
152
|
+
service_name = 'test_ssl_collector' if ENV['SW_APM_COLLECTOR'] =~ /java-collector:1222/
|
|
153
|
+
if service_name.empty?
|
|
154
|
+
SolarWindsAPM.logger.error "[solarwinds_apm/oboe_options] SW_APM_SERVICE_KEY problem. Service Name is missing"
|
|
155
|
+
return false
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
name = service_name.dup
|
|
159
|
+
name.downcase!
|
|
160
|
+
name.gsub!(/[^a-z0-9.:_-]/, '')
|
|
161
|
+
name = name[0..254]
|
|
162
|
+
|
|
163
|
+
if name != service_name
|
|
164
|
+
SolarWindsAPM.logger.warn "[solarwinds_apm/oboe_options] SW_APM_SERVICE_KEY problem. Service Name transformed from #{service_name} to #{name}"
|
|
165
|
+
service_name = name
|
|
166
|
+
end
|
|
167
|
+
@service_name = service_name # instance variable used in testing
|
|
168
|
+
true
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def read_and_validate_ec2_md_timeout
|
|
172
|
+
timeout = ENV['SW_APM_EC2_METADATA_TIMEOUT'] || SolarWindsAPM::Config[:ec2_metadata_timeout]
|
|
173
|
+
return 1000 unless timeout.is_a?(Integer) || timeout =~ /^\d+$/
|
|
174
|
+
timeout = timeout.to_i
|
|
175
|
+
return timeout.between?(0, 3000) ? timeout : 1000
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def read_and_validate_proxy
|
|
179
|
+
proxy = ENV['SW_APM_PROXY'] || SolarWindsAPM::Config[:http_proxy] || ''
|
|
180
|
+
return proxy if proxy == ''
|
|
181
|
+
|
|
182
|
+
unless proxy =~ /http:\/\/.*:\d+$/
|
|
183
|
+
SolarWindsAPM.logger.error "[solarwinds_apm/oboe_options] SW_APM_PROXY/http_proxy doesn't start with 'http://', #{proxy}"
|
|
184
|
+
return '' # try without proxy, it may work, shouldn't crash but may not report
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
proxy
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module SolarWindsAPM
|
|
5
|
+
##
|
|
6
|
+
# This module provides a method to manually initialize the
|
|
7
|
+
# Ruby instrumentation. Normally this is done by detecting
|
|
8
|
+
# frameworks at load time and inserting initialization hooks.
|
|
9
|
+
module Ruby
|
|
10
|
+
class << self
|
|
11
|
+
def initialize
|
|
12
|
+
load
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# The core method to load Ruby instrumentation. Call this
|
|
17
|
+
# from raw Ruby scripts or in Ruby applications where a
|
|
18
|
+
# supported framework isn't being used. Supported frameworks
|
|
19
|
+
# will instead be detected at load time and initialization is
|
|
20
|
+
# automatic.
|
|
21
|
+
def load
|
|
22
|
+
# In case some apps call this manually, make sure
|
|
23
|
+
# that the gem is fully loaded and not in no-op
|
|
24
|
+
# mode (e.g. on unsupported platforms etc.)
|
|
25
|
+
if SolarWindsAPM.loaded
|
|
26
|
+
SolarWindsAPM::Inst.load_instrumentation
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if SolarWindsAPM.loaded && !SolarWindsAPM.framework?
|
|
34
|
+
SolarWindsAPM::Ruby.load
|
|
35
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) SolarWinds, LLC.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#++
|
|
5
|
+
|
|
6
|
+
module SolarWindsAPM
|
|
7
|
+
module SDK
|
|
8
|
+
|
|
9
|
+
module CurrentTraceInfo
|
|
10
|
+
# Creates an instance of {TraceInfo} with instance methods {TraceInfo#trace_id},
|
|
11
|
+
# {TraceInfo#span_id}, {TraceInfo#trace_flags}, {TraceInfo#for_log},
|
|
12
|
+
# and {TraceInfo#hash_for_log}.
|
|
13
|
+
#
|
|
14
|
+
# === Example:
|
|
15
|
+
#
|
|
16
|
+
# trace = SolarWindsAPM::SDK.current_trace_info
|
|
17
|
+
# trace.for_log # 'trace_id=7435a9fe510ae4533414d425dadf4e18 span_id=49e60702469db05f trace_flags=01' or '' depends on Config
|
|
18
|
+
# trace.hash_for_log # { trace_id: '7435a9fe510ae4533414d425dadf4e18',
|
|
19
|
+
# span_id: '49e60702469db05f',
|
|
20
|
+
# trace_flags: ''} or {} depends on Config
|
|
21
|
+
#
|
|
22
|
+
# Configure trace info injection with lograge:
|
|
23
|
+
#
|
|
24
|
+
# Lograge.custom_options = lambda do |event|
|
|
25
|
+
# SolarWindsAPM::SDK.current_trace_info.hash_for_log
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
|
|
29
|
+
def current_trace_info
|
|
30
|
+
TraceInfo.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @attr trace_id
|
|
34
|
+
# @attr span_id
|
|
35
|
+
# @attr trace_flags
|
|
36
|
+
class TraceInfo
|
|
37
|
+
attr_reader :tracestring, :trace_id, :span_id, :trace_flags, :do_log
|
|
38
|
+
|
|
39
|
+
SQL_REGEX=/\/\*\s*traceparent=.*\*\/\s*/.freeze
|
|
40
|
+
|
|
41
|
+
def initialize
|
|
42
|
+
tracestring = SolarWindsAPM::Context.toString
|
|
43
|
+
parts = SolarWindsAPM::TraceString.split(tracestring)
|
|
44
|
+
|
|
45
|
+
@tracestring = parts[:tracestring]
|
|
46
|
+
@trace_id = parts[:trace_id]
|
|
47
|
+
@span_id = parts[:span_id]
|
|
48
|
+
@trace_flags = parts[:flags]
|
|
49
|
+
|
|
50
|
+
@do_log = log? # true if the tracecontext should be added to logs
|
|
51
|
+
@do_sql = sql? # true if the tracecontext should be added to sql
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# for_log returns a string in the format
|
|
55
|
+
# 'trace_id=<trace_id> span_id=<span_id> trace_flags=<trace_flags>' or ''.
|
|
56
|
+
#
|
|
57
|
+
# An empty string is returned depending on the setting for
|
|
58
|
+
# <tt>SolarWindsAPM::Config[:log_traceId]</tt>, which can be :never,
|
|
59
|
+
# :sampled, :traced, or :always.
|
|
60
|
+
#
|
|
61
|
+
def for_log
|
|
62
|
+
@for_log ||= @do_log ? "trace_id=#{@trace_id} span_id=#{@span_id} trace_flags=#{@trace_flags}" : ''
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def hash_for_log
|
|
66
|
+
@hash_for_log ||= @do_log ? { trace_id: @trace_id,
|
|
67
|
+
span_id: @span_id,
|
|
68
|
+
trace_flags: @trace_flags } : {}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def for_sql
|
|
72
|
+
@for_sql ||= @do_sql ? "/*traceparent='#{@tracestring}'*/" : ''
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# add_traceparent_to_sql
|
|
77
|
+
#
|
|
78
|
+
# returns the sql with "/*traceparent='#{@tracestring}'*/" prepended
|
|
79
|
+
# and adds the QueryTag kv to kvs
|
|
80
|
+
#
|
|
81
|
+
def add_traceparent_to_sql(sql, kvs)
|
|
82
|
+
sql = sql.gsub(SQL_REGEX, '') # remove if it was added before
|
|
83
|
+
|
|
84
|
+
unless for_sql.empty?
|
|
85
|
+
kvs[:QueryTag] = for_sql
|
|
86
|
+
return "#{for_sql}#{sql}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
sql
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
# if true the trace info should be added to the log message
|
|
95
|
+
def log?
|
|
96
|
+
case SolarWindsAPM::Config[:log_traceId]
|
|
97
|
+
when :never, nil
|
|
98
|
+
false
|
|
99
|
+
when :always
|
|
100
|
+
# there is no way @tracestring is not ok
|
|
101
|
+
# it may be all 0s, but that is ok
|
|
102
|
+
# SolarWindsAPM::TraceString.ok?(@tracestring)
|
|
103
|
+
true
|
|
104
|
+
when :traced
|
|
105
|
+
SolarWindsAPM::TraceString.valid?(@tracestring)
|
|
106
|
+
when :sampled
|
|
107
|
+
SolarWindsAPM::TraceString.sampled?(@tracestring)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# if true the trace info should be added to the sql query
|
|
112
|
+
def sql?
|
|
113
|
+
SolarWindsAPM::Config[:tag_sql] &&
|
|
114
|
+
SolarWindsAPM::TraceString.sampled?(@tracestring)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
extend CurrentTraceInfo
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#++
|
|
5
|
+
|
|
6
|
+
module SolarWindsAPM
|
|
7
|
+
module SDK
|
|
8
|
+
|
|
9
|
+
module CustomMetrics
|
|
10
|
+
|
|
11
|
+
# Send counts
|
|
12
|
+
#
|
|
13
|
+
# Use this method to report the number of times an action occurs. The metric counts reported are summed and flushed every 60 seconds.
|
|
14
|
+
#
|
|
15
|
+
# === Arguments:
|
|
16
|
+
#
|
|
17
|
+
# * +name+ (String) Name to be used for the metric. Must be 255 or fewer characters and consist only of A-Za-z0-9.:-*
|
|
18
|
+
# * +count+ (Integer, optional, default = 1): Count of actions being reported
|
|
19
|
+
# * +with_hostname+ (Boolean, optional, default = false): Indicates if the host name should be included as a tag for the metric
|
|
20
|
+
# * +tags_kvs+ (Hash, optional): List of key/value pairs to describe the metric. The key must be <= 64 characters, the value must be <= 255 characters, allowed characters: A-Za-z0-9.:-_
|
|
21
|
+
#
|
|
22
|
+
# === Example:
|
|
23
|
+
#
|
|
24
|
+
# class WorkTracker
|
|
25
|
+
# def counting(name, tags = {})
|
|
26
|
+
# yield # yield to where work is done
|
|
27
|
+
# SolarWindsAPM::SDK.increment_metric(name, 1, false, tags)
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# === Returns:
|
|
32
|
+
# * 0 on success, error code on failure
|
|
33
|
+
#
|
|
34
|
+
def increment_metric(name, count = 1, with_hostname = false, tags_kvs = {})
|
|
35
|
+
return true unless SolarWindsAPM.loaded
|
|
36
|
+
with_hostname = with_hostname ? 1 : 0
|
|
37
|
+
tags, tags_count = make_tags(tags_kvs)
|
|
38
|
+
SolarWindsAPM::CustomMetrics.increment(name.to_s, count, with_hostname, nil, tags, tags_count) == 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Send values with counts
|
|
42
|
+
#
|
|
43
|
+
# Use this method to report a value for each or multiple counts. The metric values reported are aggregated and flushed every 60 seconds. The dashboard displays the average value per count.
|
|
44
|
+
#
|
|
45
|
+
# === Arguments:
|
|
46
|
+
#
|
|
47
|
+
# * +name+ (String) Name to be used for the metric. Must be 255 or fewer characters and consist only of A-Za-z0-9.:-*
|
|
48
|
+
# * +value+ (Numeric) Value to be added to the current sum
|
|
49
|
+
# * +count+ (Integer, optional, default = 1): Count of actions being reported
|
|
50
|
+
# * +with_hostname+ (Boolean, optional, default = false): Indicates if the host name should be included as a tag for the metric
|
|
51
|
+
# * +tags_kvs+ (Hash, optional): List of key/value pairs to describe the metric. The key must be <= 64 characters, the value must be <= 255 characters, allowed characters: A-Za-z0-9.:-_
|
|
52
|
+
#
|
|
53
|
+
# === Example:
|
|
54
|
+
#
|
|
55
|
+
# class WorkTracker
|
|
56
|
+
# def timing(name, tags = {})
|
|
57
|
+
# start = Time.now
|
|
58
|
+
# yield # yield to where work is done
|
|
59
|
+
# duration = Time.now - start
|
|
60
|
+
# SolarWindsAPM::SDK.summary_metric(name, duration, 1, false, tags)
|
|
61
|
+
# end
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# === Returns:
|
|
65
|
+
# * 0 on success, error code on failure
|
|
66
|
+
#
|
|
67
|
+
def summary_metric(name, value, count = 1, with_hostname = false, tags_kvs = {})
|
|
68
|
+
return true unless SolarWindsAPM.loaded
|
|
69
|
+
with_hostname = with_hostname ? 1 : 0
|
|
70
|
+
tags, tags_count = make_tags(tags_kvs)
|
|
71
|
+
SolarWindsAPM::CustomMetrics.summary(name.to_s, value, count, with_hostname, nil, tags, tags_count) == 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def make_tags(tags_kvs)
|
|
77
|
+
unless tags_kvs.is_a?(Hash)
|
|
78
|
+
SolarWindsAPM.logger.warn("[solarwinds_apm/metrics] CustomMetrics received tags_kvs that are not a Hash (found #{tags_kvs.class}), setting tags_kvs = {}")
|
|
79
|
+
tags_kvs = {}
|
|
80
|
+
end
|
|
81
|
+
count = tags_kvs.size
|
|
82
|
+
tags = SolarWindsAPM::MetricTags.new(count)
|
|
83
|
+
|
|
84
|
+
tags_kvs.each_with_index do |(k, v), i|
|
|
85
|
+
tags.add(i, k.to_s, v.to_s)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
[tags, count]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
extend CustomMetrics
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright (c) 2019 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
module SolarWindsAPM
|
|
6
|
+
module SDK
|
|
7
|
+
module Logging
|
|
8
|
+
|
|
9
|
+
# Log an information event in the current span
|
|
10
|
+
#
|
|
11
|
+
# a possible use-case is to collect extra information during the execution of a request
|
|
12
|
+
#
|
|
13
|
+
# === Arguments:
|
|
14
|
+
# * +kvs+ - (optional) hash containing key/value pairs that will be reported with this span.
|
|
15
|
+
#
|
|
16
|
+
def log_info(kvs)
|
|
17
|
+
SolarWindsAPM::API.log_info(SolarWindsAPM.layer, kvs)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Log an exception/error event in the current span
|
|
21
|
+
#
|
|
22
|
+
# this may be helpful to track problems when an exception is rescued
|
|
23
|
+
#
|
|
24
|
+
# === Arguments:
|
|
25
|
+
# * +exception+ - an exception, must respond to :message and :backtrace
|
|
26
|
+
# * +kvs+ - (optional) hash containing key/value pairs that will be reported with this span.
|
|
27
|
+
#
|
|
28
|
+
def log_exception(exception, kvs = {})
|
|
29
|
+
SolarWindsAPM::API.log_exception(SolarWindsAPM.layer, exception, kvs)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
extend Logging
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#sh Copyright (c) SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module SolarWindsAPM
|
|
5
|
+
module SDK
|
|
6
|
+
##
|
|
7
|
+
#
|
|
8
|
+
# Module to be included in classes with outbound calls
|
|
9
|
+
#
|
|
10
|
+
module TraceContextHeaders
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# Add w3c tracecontext to headers arg
|
|
14
|
+
#
|
|
15
|
+
# === Argument:
|
|
16
|
+
# * +:headers+ outbound headers, a Hash or other object that can have key/value assigned
|
|
17
|
+
#
|
|
18
|
+
# Internally it uses SolarWindsAPM.trace_context, which is a thread local
|
|
19
|
+
# variable containing verified and processed incoming w3c headers.
|
|
20
|
+
# It gets populated by requests processed by Rack or through the
|
|
21
|
+
# :headers arg in SolarWindsAPM::SDK.start_trace
|
|
22
|
+
#
|
|
23
|
+
# === Example:
|
|
24
|
+
# class OutboundCaller
|
|
25
|
+
# include SolarWindsAPM::SDK::TraceContextHeaders
|
|
26
|
+
#
|
|
27
|
+
# # create new headers
|
|
28
|
+
# def faraday_send
|
|
29
|
+
# conn = Faraday.new(:url => 'http://example.com')
|
|
30
|
+
# headers = add_tracecontext_headers
|
|
31
|
+
# conn.get('/', nil, headers)
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# # add to given headers
|
|
35
|
+
# def excon_send(headers)
|
|
36
|
+
# conn = Excon.new('http://example.com')
|
|
37
|
+
# add_tracecontext_headers(headers)
|
|
38
|
+
# conn.get(headers: headers)
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# === Returns:
|
|
43
|
+
# * The headers with w3c tracecontext added, also modifies the headers arg if given
|
|
44
|
+
#
|
|
45
|
+
def add_tracecontext_headers(headers = {})
|
|
46
|
+
begin
|
|
47
|
+
if SolarWindsAPM::Context.isValid
|
|
48
|
+
headers['traceparent'] = SolarWindsAPM::Context.toString
|
|
49
|
+
parent_id_flags = SolarWindsAPM::TraceString.span_id_flags(headers['traceparent'])
|
|
50
|
+
tracestate = SolarWindsAPM.trace_context&.tracestate
|
|
51
|
+
headers['tracestate'] = SolarWindsAPM::TraceState.add_sw_member(tracestate, parent_id_flags)
|
|
52
|
+
else
|
|
53
|
+
# make sure we propagate an incoming trace_context even if we don't trace
|
|
54
|
+
if SolarWindsAPM.trace_context
|
|
55
|
+
headers['traceparent'] = SolarWindsAPM.trace_context.traceparent
|
|
56
|
+
headers['tracestate'] = SolarWindsAPM.trace_context.tracestate
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
rescue => e
|
|
60
|
+
# we don't know what the class of headers is and the obj may not
|
|
61
|
+
# be able to accept a key/value assignment
|
|
62
|
+
# unfortunately I could not find a method to check for that
|
|
63
|
+
# therefore we're catching the error and don't change the headers
|
|
64
|
+
end
|
|
65
|
+
headers
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|