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,241 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module SolarWindsAPM
|
|
5
|
+
module Inst
|
|
6
|
+
##
|
|
7
|
+
# SolarWindsAPM::Inst::Sequel
|
|
8
|
+
#
|
|
9
|
+
# The common (shared) methods used by the SolarWindsAPM Sequel instrumentation
|
|
10
|
+
# across multiple modules/classes.
|
|
11
|
+
#
|
|
12
|
+
module Sequel
|
|
13
|
+
##
|
|
14
|
+
# assign_kvs
|
|
15
|
+
#
|
|
16
|
+
# Given SQL and the options hash, this method extracts the interesting
|
|
17
|
+
# bits for reporting to the SolarWinds dashboard.
|
|
18
|
+
#
|
|
19
|
+
# kvs is a hash and we are taking advantage of using it by reference to
|
|
20
|
+
# assign kvs to the exit event (important for trace injection)
|
|
21
|
+
#
|
|
22
|
+
def assign_kvs(sql, opts, kvs)
|
|
23
|
+
unless sql.is_a?(String)
|
|
24
|
+
kvs[:IsPreparedStatement] = true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if ::Sequel::VERSION > '4.36.0' && !sql.is_a?(String)
|
|
28
|
+
# TODO check if this is true for all sql
|
|
29
|
+
# In 4.37.0, sql was converted to a prepared statement object
|
|
30
|
+
sql = sql.prepared_sql unless sql.is_a?(Symbol)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if SolarWindsAPM::Config[:sanitize_sql]
|
|
34
|
+
# Sanitize SQL and don't report binds
|
|
35
|
+
if sql.is_a?(Symbol)
|
|
36
|
+
kvs[:Query] = sql
|
|
37
|
+
else
|
|
38
|
+
sql = SolarWindsAPM::Util.remove_traceparent(sql)
|
|
39
|
+
kvs[:Query] = SolarWindsAPM::Util.sanitize_sql(sql)
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
# Report raw SQL and any binds if they exist
|
|
43
|
+
kvs[:Query] = SolarWindsAPM::Util.remove_traceparent(sql.to_s)
|
|
44
|
+
kvs[:QueryArgs] = opts[:arguments] if opts.is_a?(Hash) && opts.key?(:arguments)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:sequel][:collect_backtraces]
|
|
48
|
+
|
|
49
|
+
if ::Sequel::VERSION < '3.41.0' && !(self.class.to_s =~ /Dataset$/)
|
|
50
|
+
db_opts = @opts
|
|
51
|
+
elsif @pool
|
|
52
|
+
db_opts = @pool.db.opts
|
|
53
|
+
else
|
|
54
|
+
db_opts = @db.opts
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
kvs[:Database] = db_opts[:database]
|
|
58
|
+
kvs[:RemoteHost] = db_opts[:host]
|
|
59
|
+
kvs[:RemotePort] = db_opts[:port] if db_opts.key?(:port)
|
|
60
|
+
kvs[:Flavor] = db_opts[:adapter]
|
|
61
|
+
rescue => e
|
|
62
|
+
SolarWindsAPM.logger.debug "[solarwinds_apm/debug Error capturing Sequel KVs: #{e.message}" if SolarWindsAPM::Config[:verbose]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# exec_with_sw_apm
|
|
67
|
+
#
|
|
68
|
+
# This method wraps and routes the call to the specified
|
|
69
|
+
# original method call
|
|
70
|
+
#
|
|
71
|
+
def exec_with_sw_apm(method, sql, opts = ::Sequel::OPTS, &block)
|
|
72
|
+
kvs = {}
|
|
73
|
+
SolarWindsAPM::SDK.trace(:sequel, kvs: kvs) do
|
|
74
|
+
new_sql = add_traceparent(sql, kvs)
|
|
75
|
+
assign_kvs(new_sql, opts, kvs) if SolarWindsAPM.tracing?
|
|
76
|
+
send(method, new_sql, opts, &block)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def add_traceparent(sql, kvs)
|
|
81
|
+
return sql unless SolarWindsAPM.tracing? && SolarWindsAPM::Config[:tag_sql]
|
|
82
|
+
|
|
83
|
+
case sql
|
|
84
|
+
when String
|
|
85
|
+
return SolarWindsAPM::SDK.current_trace_info.add_traceparent_to_sql(sql, kvs)
|
|
86
|
+
when Symbol
|
|
87
|
+
if defined?(prepared_statement) # for mysql2
|
|
88
|
+
ps = prepared_statement(sql)
|
|
89
|
+
new_ps = add_traceparent_to_ps(ps, kvs)
|
|
90
|
+
set_prepared_statement(sql, new_ps)
|
|
91
|
+
return sql # related query may have been modified
|
|
92
|
+
elsif self.is_a?(::Sequel::Dataset) # for postgresql
|
|
93
|
+
ps = self
|
|
94
|
+
new_ps = add_traceparent_to_ps(ps, kvs)
|
|
95
|
+
self.db.set_prepared_statement(sql, new_ps)
|
|
96
|
+
return sql
|
|
97
|
+
end
|
|
98
|
+
when ::Sequel::Dataset::ArgumentMapper # for mysql2
|
|
99
|
+
new_sql = add_traceparent_to_ps(sql, kvs)
|
|
100
|
+
return new_sql # related query may have been modified
|
|
101
|
+
end
|
|
102
|
+
sql # return original when none of the cases match
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# this method uses some non-api methods partially copied from
|
|
106
|
+
# `execute_prepared_statement` in `mysql2.rb`
|
|
107
|
+
# and `prepare` in `prepared_statements.rb` in the sequel gem
|
|
108
|
+
def add_traceparent_to_ps(ps, kvs)
|
|
109
|
+
sql = ps.prepared_sql
|
|
110
|
+
new_sql = SolarWindsAPM::SDK.current_trace_info.add_traceparent_to_sql(sql, kvs)
|
|
111
|
+
|
|
112
|
+
unless new_sql == sql
|
|
113
|
+
new_ps = ps.clone(:prepared_sql=>new_sql, :sql=>new_sql)
|
|
114
|
+
return new_ps
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
ps # no change, no trace context added
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
module SequelDatabase
|
|
122
|
+
include SolarWindsAPM::Inst::Sequel
|
|
123
|
+
|
|
124
|
+
def self.included(klass)
|
|
125
|
+
SolarWindsAPM::Util.method_alias(klass, :run, ::Sequel::Database)
|
|
126
|
+
SolarWindsAPM::Util.method_alias(klass, :execute_ddl, ::Sequel::Database)
|
|
127
|
+
SolarWindsAPM::Util.method_alias(klass, :execute_dui, ::Sequel::Database)
|
|
128
|
+
SolarWindsAPM::Util.method_alias(klass, :execute_insert, ::Sequel::Database)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def run_with_sw_apm(sql, opts = ::Sequel::OPTS)
|
|
132
|
+
kvs = {}
|
|
133
|
+
SolarWindsAPM::SDK.trace(:sequel, kvs: kvs) do
|
|
134
|
+
new_sql = add_traceparent(sql, kvs)
|
|
135
|
+
assign_kvs(new_sql, opts, kvs) if SolarWindsAPM.tracing?
|
|
136
|
+
run_without_sw_apm(new_sql, opts)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def execute_ddl_with_sw_apm(sql, opts = ::Sequel::OPTS, &block)
|
|
141
|
+
# If we're already tracing a sequel operation, then this call likely came
|
|
142
|
+
# from Sequel::Dataset. In this case, just pass it on.
|
|
143
|
+
return execute_ddl_without_sw_apm(sql, opts, &block) if SolarWindsAPM.tracing_layer?(:sequel)
|
|
144
|
+
|
|
145
|
+
exec_with_sw_apm(:execute_ddl_without_sw_apm, sql, opts, &block)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def execute_dui_with_sw_apm(sql, opts = ::Sequel::OPTS, &block)
|
|
149
|
+
# If we're already tracing a sequel operation, then this call likely came
|
|
150
|
+
# from Sequel::Dataset. In this case, just pass it on.
|
|
151
|
+
return execute_dui_without_sw_apm(sql, opts, &block) if SolarWindsAPM.tracing_layer?(:sequel)
|
|
152
|
+
|
|
153
|
+
exec_with_sw_apm(:execute_dui_without_sw_apm, sql, opts, &block)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def execute_insert_with_sw_apm(sql, opts = ::Sequel::OPTS, &block)
|
|
157
|
+
# If we're already tracing a sequel operation, then this call likely came
|
|
158
|
+
# from Sequel::Dataset. In this case, just pass it on.
|
|
159
|
+
return execute_insert_without_sw_apm(sql, opts, &block) if SolarWindsAPM.tracing_layer?(:sequel)
|
|
160
|
+
|
|
161
|
+
exec_with_sw_apm(:execute_insert_without_sw_apm, sql, opts, &block)
|
|
162
|
+
end
|
|
163
|
+
end # module SequelDatabase
|
|
164
|
+
|
|
165
|
+
module AdapterDatabase
|
|
166
|
+
include SolarWindsAPM::Inst::Sequel
|
|
167
|
+
|
|
168
|
+
def self.included(klass)
|
|
169
|
+
if defined?(::Sequel::MySQL::MysqlMysql2::DatabaseMethods)
|
|
170
|
+
SolarWindsAPM::Util.method_alias(klass, :execute, ::Sequel::MySQL::MysqlMysql2::DatabaseMethods)
|
|
171
|
+
end
|
|
172
|
+
if defined?(::Sequel::Postgres::Database)
|
|
173
|
+
SolarWindsAPM::Util.method_alias(klass, :execute, ::Sequel::Postgres::Database)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def execute_with_sw_apm(*args, &block)
|
|
178
|
+
# if this is called via a dataset it is already being traced
|
|
179
|
+
return execute_without_sw_apm(*args, &block) if SolarWindsAPM.tracing_layer?(:sequel)
|
|
180
|
+
|
|
181
|
+
kvs = {}
|
|
182
|
+
SolarWindsAPM::SDK.trace(:sequel, kvs: kvs) do
|
|
183
|
+
new_sql = add_traceparent(args[0], kvs)
|
|
184
|
+
args[0] = new_sql
|
|
185
|
+
assign_kvs(args[0], args[1], kvs) if SolarWindsAPM.tracing?
|
|
186
|
+
execute_without_sw_apm(*args, &block)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
module SequelDataset
|
|
192
|
+
include SolarWindsAPM::Inst::Sequel
|
|
193
|
+
|
|
194
|
+
def self.included(klass)
|
|
195
|
+
SolarWindsAPM::Util.method_alias(klass, :execute, ::Sequel::Dataset)
|
|
196
|
+
SolarWindsAPM::Util.method_alias(klass, :execute_ddl, ::Sequel::Dataset)
|
|
197
|
+
SolarWindsAPM::Util.method_alias(klass, :execute_dui, ::Sequel::Dataset)
|
|
198
|
+
SolarWindsAPM::Util.method_alias(klass, :execute_insert, ::Sequel::Dataset)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def execute_with_sw_apm(sql, opts = ::Sequel::OPTS, &block)
|
|
202
|
+
exec_with_sw_apm(:execute_without_sw_apm, sql, opts, &block)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def execute_ddl_with_sw_apm(sql, opts = ::Sequel::OPTS, &block)
|
|
206
|
+
exec_with_sw_apm(:execute_ddl_without_sw_apm, sql, opts, &block)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def execute_dui_with_sw_apm(sql, opts = ::Sequel::OPTS, &block)
|
|
210
|
+
exec_with_sw_apm(:execute_dui_without_sw_apm, sql, opts, &block)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def execute_insert_with_sw_apm(sql, opts = ::Sequel::OPTS, &block)
|
|
214
|
+
exec_with_sw_apm(:execute_insert_without_sw_apm, sql, opts, &block)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end # module SequelDataset
|
|
218
|
+
end # module Inst
|
|
219
|
+
end # module SolarWindsAPM
|
|
220
|
+
|
|
221
|
+
if SolarWindsAPM::Config[:sequel][:enabled]
|
|
222
|
+
if defined?(::Sequel) && ::Sequel::VERSION < '4.0.0'
|
|
223
|
+
# For versions before 4.0.0, Sequel::OPTS wasn't defined.
|
|
224
|
+
# Define it as an empty hash for backwards compatibility.
|
|
225
|
+
module ::Sequel
|
|
226
|
+
OPTS = {}
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
if defined?(::Sequel)
|
|
231
|
+
SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting sequel' if SolarWindsAPM::Config[:verbose]
|
|
232
|
+
SolarWindsAPM::Util.send_include(::Sequel::Database, SolarWindsAPM::Inst::SequelDatabase)
|
|
233
|
+
SolarWindsAPM::Util.send_include(::Sequel::Dataset, SolarWindsAPM::Inst::SequelDataset)
|
|
234
|
+
|
|
235
|
+
# TODO this is temporary, we need to instrument `require`, see NH-9711
|
|
236
|
+
require 'sequel/adapters/mysql2'
|
|
237
|
+
SolarWindsAPM::Util.send_include(::Sequel::MySQL::MysqlMysql2::DatabaseMethods, SolarWindsAPM::Inst::AdapterDatabase)
|
|
238
|
+
require 'sequel/adapters/postgres'
|
|
239
|
+
SolarWindsAPM::Util.send_include(::Sequel::Postgres::Database, SolarWindsAPM::Inst::AdapterDatabase)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module SolarWindsAPM
|
|
5
|
+
class SidekiqClient
|
|
6
|
+
include SolarWindsAPM::SDK::TraceContextHeaders
|
|
7
|
+
|
|
8
|
+
def collect_kvs(args)
|
|
9
|
+
begin
|
|
10
|
+
# Attempt to collect up pertinent info. If we hit something unexpected,
|
|
11
|
+
# keep calm and instrument on.
|
|
12
|
+
|
|
13
|
+
report_kvs = {}
|
|
14
|
+
worker_class, msg, queue, _ = args
|
|
15
|
+
|
|
16
|
+
report_kvs[:Spec] = :pushq
|
|
17
|
+
report_kvs[:Flavor] = :sidekiq
|
|
18
|
+
report_kvs[:Queue] = queue
|
|
19
|
+
report_kvs[:Retry] = msg['retry']
|
|
20
|
+
report_kvs[:JobName] = msg['wrapped'] || worker_class
|
|
21
|
+
report_kvs[:MsgID] = msg['jid']
|
|
22
|
+
report_kvs[:Args] = msg['args'].to_s[0..1024] if SolarWindsAPM::Config[:sidekiqclient][:log_args]
|
|
23
|
+
report_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:sidekiqclient][:collect_backtraces]
|
|
24
|
+
rescue => e
|
|
25
|
+
SolarWindsAPM.logger.warn "[solarwinds_apm/sidekiq] Non-fatal error capturing KVs: #{e.message}"
|
|
26
|
+
end
|
|
27
|
+
report_kvs
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def call(*args)
|
|
31
|
+
# args: 0: worker_class, 1: msg, 2: queue, 3: redis_pool
|
|
32
|
+
if SolarWindsAPM.tracing?
|
|
33
|
+
report_kvs = collect_kvs(args)
|
|
34
|
+
SolarWindsAPM::API.log_entry(:'sidekiq-client', report_kvs)
|
|
35
|
+
if args[1].is_a?(Hash)
|
|
36
|
+
# We've been doing this since 2015, but ...
|
|
37
|
+
# ... is it actually safe to inject our entries into the msg of the job?
|
|
38
|
+
# Opentelemetry does it too :), so I guess we're good
|
|
39
|
+
args[1]['SourceTrace'] = SolarWindsAPM::Context.toString
|
|
40
|
+
add_tracecontext_headers(args[1])
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
result = yield
|
|
45
|
+
rescue => e
|
|
46
|
+
SolarWindsAPM::API.log_exception(:'sidekiq-client', e, { :JobID => result['jid'] })
|
|
47
|
+
raise
|
|
48
|
+
ensure
|
|
49
|
+
SolarWindsAPM::API.log_exit(:'sidekiq-client', { :JobID => result['jid'] })
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if defined?(Sidekiq) && SolarWindsAPM::Config[:sidekiqclient][:enabled]
|
|
55
|
+
SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting sidekiq client' if SolarWindsAPM::Config[:verbose]
|
|
56
|
+
|
|
57
|
+
Sidekiq.configure_client do |config|
|
|
58
|
+
config.client_middleware do |chain|
|
|
59
|
+
SolarWindsAPM.logger.info '[solarwinds_apm/loading] Adding Sidekiq client middleware' if SolarWindsAPM::Config[:verbose]
|
|
60
|
+
chain.add SolarWindsAPM::SidekiqClient
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module SolarWindsAPM
|
|
5
|
+
class SidekiqWorker
|
|
6
|
+
def collect_kvs(args)
|
|
7
|
+
begin
|
|
8
|
+
# Attempt to collect up pertinent info. If we hit something unexpected,
|
|
9
|
+
# keep calm and instrument on.
|
|
10
|
+
report_kvs = {}
|
|
11
|
+
_worker, msg, queue = args
|
|
12
|
+
|
|
13
|
+
# Background Job Spec KVs
|
|
14
|
+
report_kvs[:Spec] = :job
|
|
15
|
+
report_kvs[:Flavor] = :sidekiq
|
|
16
|
+
report_kvs[:Queue] = queue
|
|
17
|
+
report_kvs[:Retry] = msg['retry']
|
|
18
|
+
report_kvs[:JobName] = msg['wrapped'] || msg['class']
|
|
19
|
+
report_kvs[:MsgID] = msg['jid']
|
|
20
|
+
report_kvs[:Args] = msg['args'].to_s[0..1024] if SolarWindsAPM::Config[:sidekiqworker][:log_args]
|
|
21
|
+
report_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:sidekiqworker][:collect_backtraces]
|
|
22
|
+
|
|
23
|
+
# Webserver Spec KVs
|
|
24
|
+
report_kvs[:'HTTP-Host'] = Socket.gethostname
|
|
25
|
+
report_kvs[:Controller] = "Sidekiq_#{queue}"
|
|
26
|
+
report_kvs[:Action] = msg['wrapped'] || msg['class']
|
|
27
|
+
report_kvs[:URL] = "/sidekiq/#{queue}/#{msg['wrapped'] || msg['class']}"
|
|
28
|
+
rescue => e
|
|
29
|
+
SolarWindsAPM.logger.warn "[solarwinds_apm/sidekiq] Non-fatal error capturing KVs: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
report_kvs
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def call(*args)
|
|
35
|
+
# args: 0: worker, 1: msg, 2: queue
|
|
36
|
+
report_kvs = collect_kvs(args)
|
|
37
|
+
|
|
38
|
+
# Continue the trace from the enqueue side
|
|
39
|
+
if args[1].is_a?(Hash) && SolarWindsAPM::TraceString.valid?(args[1]['SourceTrace'])
|
|
40
|
+
report_kvs[:SourceTrace] = args[1]['SourceTrace']
|
|
41
|
+
SolarWindsAPM::Context.fromString(args[1]['SourceTrace'])
|
|
42
|
+
args[1].delete('SourceTrace')
|
|
43
|
+
unless args[1]['traceparent'] && args[1]['tracestate']
|
|
44
|
+
add_tracecontext_headers(args[1])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
SolarWindsAPM::SDK.start_trace(:'sidekiq-worker', kvs: report_kvs, headers: args[1]) do
|
|
49
|
+
yield
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if defined?(Sidekiq) && SolarWindsAPM::Config[:sidekiqworker][:enabled]
|
|
56
|
+
SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting sidekiq worker' if SolarWindsAPM::Config[:verbose]
|
|
57
|
+
|
|
58
|
+
Sidekiq.configure_server do |config|
|
|
59
|
+
config.server_middleware do |chain|
|
|
60
|
+
SolarWindsAPM.logger.info '[solarwinds_apm/loading] Adding Sidekiq worker middleware' if SolarWindsAPM::Config[:verbose]
|
|
61
|
+
chain.add SolarWindsAPM::SidekiqWorker
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
class TyphoeusError < StandardError; end
|
|
4
|
+
|
|
5
|
+
module SolarWindsAPM
|
|
6
|
+
module Inst
|
|
7
|
+
module TyphoeusRequestOps
|
|
8
|
+
include SolarWindsAPM::SDK::TraceContextHeaders
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
unless SolarWindsAPM.tracing?
|
|
12
|
+
add_tracecontext_headers(options[:headers])
|
|
13
|
+
return super
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
SolarWindsAPM::API.log_entry(:typhoeus)
|
|
18
|
+
|
|
19
|
+
context = SolarWindsAPM::Context.toString
|
|
20
|
+
|
|
21
|
+
kvs = {}
|
|
22
|
+
kvs[:Spec] = 'rsc'
|
|
23
|
+
kvs[:IsService] = 1
|
|
24
|
+
kvs[:HTTPMethod] = SolarWindsAPM::Util.upcase(options[:method])
|
|
25
|
+
|
|
26
|
+
add_tracecontext_headers(options[:headers])
|
|
27
|
+
response = super
|
|
28
|
+
|
|
29
|
+
if response.code == 0
|
|
30
|
+
exception = TyphoeusError.new(response.return_message)
|
|
31
|
+
exception.set_backtrace(SolarWindsAPM::API.backtrace)
|
|
32
|
+
SolarWindsAPM::API.log_exception(:typhoeus, exception)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
kvs[:HTTPStatus] = response.code
|
|
36
|
+
kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:typhoeus][:collect_backtraces]
|
|
37
|
+
# Conditionally log query params
|
|
38
|
+
uri = URI(response.effective_url)
|
|
39
|
+
kvs[:RemoteURL] = SolarWindsAPM::Config[:typhoeus][:log_args] ? uri.to_s : uri.to_s.split('?').first
|
|
40
|
+
|
|
41
|
+
response
|
|
42
|
+
rescue => e
|
|
43
|
+
SolarWindsAPM::API.log_exception(:typhoeus, e)
|
|
44
|
+
raise e
|
|
45
|
+
ensure
|
|
46
|
+
SolarWindsAPM::API.log_exit(:typhoeus, kvs)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
module TyphoeusHydraRunnable
|
|
52
|
+
include SolarWindsAPM::SDK::TraceContextHeaders
|
|
53
|
+
|
|
54
|
+
def run
|
|
55
|
+
unless SolarWindsAPM.tracing?
|
|
56
|
+
queued_requests.map do |request|
|
|
57
|
+
add_tracecontext_headers(request.options[:headers])
|
|
58
|
+
end
|
|
59
|
+
return super
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
kvs = {}
|
|
63
|
+
|
|
64
|
+
kvs[:queued_requests] = queued_requests.count
|
|
65
|
+
kvs[:max_concurrency] = max_concurrency
|
|
66
|
+
kvs[:Async] = 1
|
|
67
|
+
kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:typhoeus][:collect_backtraces]
|
|
68
|
+
|
|
69
|
+
# FIXME: Until we figure out a strategy to deal with libcurl internal
|
|
70
|
+
# threading and Ethon's use of easy handles, here we just do a simple
|
|
71
|
+
# trace of the hydra run.
|
|
72
|
+
SolarWindsAPM::SDK.trace(:typhoeus_hydra, kvs: kvs) do
|
|
73
|
+
queued_requests.map do |request|
|
|
74
|
+
add_tracecontext_headers(request.options[:headers])
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if defined?(Typhoeus) && SolarWindsAPM::Config[:typhoeus][:enabled]
|
|
86
|
+
SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting typhoeus' if SolarWindsAPM::Config[:verbose]
|
|
87
|
+
|
|
88
|
+
Typhoeus::Request.prepend(SolarWindsAPM::Inst::TyphoeusRequestOps)
|
|
89
|
+
Typhoeus::Hydra.prepend(SolarWindsAPM::Inst::TyphoeusHydraRunnable)
|
|
90
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module SolarWindsAPM
|
|
5
|
+
##
|
|
6
|
+
# The Inst module holds all of the instrumentation extensions for various
|
|
7
|
+
# libraries such as Redis, Dalli and Resque.
|
|
8
|
+
module Inst
|
|
9
|
+
def self.load_instrumentation
|
|
10
|
+
# Load the general instrumentation
|
|
11
|
+
pattern = File.join(File.dirname(__FILE__), 'inst', '*.rb')
|
|
12
|
+
Dir.glob(pattern) do |f|
|
|
13
|
+
begin
|
|
14
|
+
require f
|
|
15
|
+
rescue => e
|
|
16
|
+
SolarWindsAPM.logger.error "[solarwinds_apm/loading] Error loading instrumentation file '#{f}' : #{e}"
|
|
17
|
+
SolarWindsAPM.logger.debug "[solarwinds_apm/loading] #{e.backtrace.first}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'digest/sha1'
|
|
5
|
+
|
|
6
|
+
module SolarWindsAPM
|
|
7
|
+
module Util
|
|
8
|
+
##
|
|
9
|
+
# This module was used solely for the deprecated RUM ID calculation
|
|
10
|
+
# but may be useful in the future.
|
|
11
|
+
#
|
|
12
|
+
module Base64URL
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def encode(bin)
|
|
16
|
+
c = [bin].pack('m0').gsub(/\=+\Z/, '').tr('+/', '-_').rstrip
|
|
17
|
+
m = c.size % 4
|
|
18
|
+
c += '=' * (4 - m) if m != 0
|
|
19
|
+
c
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def decode(bin)
|
|
23
|
+
m = bin.size % 4
|
|
24
|
+
bin += '=' * (4 - m) if m != 0
|
|
25
|
+
bin.tr('-_', '+/').unpack('m0').first
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# This module houses all of the loading functionality for the solarwinds_apm em.
|
|
32
|
+
|
|
33
|
+
# Note that this does not necessarily _have_ to include initialization routines
|
|
34
|
+
# (although it can).
|
|
35
|
+
#
|
|
36
|
+
# Actual initialization is often separated out as it can be dependent on on the state
|
|
37
|
+
# of the stack boot process. e.g. code requiring that initializers, frameworks or
|
|
38
|
+
# instrumented libraries are already loaded...
|
|
39
|
+
#
|
|
40
|
+
module Loading
|
|
41
|
+
##
|
|
42
|
+
# Load the solarwinds_apm tracing API
|
|
43
|
+
#
|
|
44
|
+
def self.require_api
|
|
45
|
+
pattern = File.join(File.dirname(__FILE__), 'api', '*.rb')
|
|
46
|
+
Dir.glob(pattern) do |f|
|
|
47
|
+
require f
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
require 'solarwinds_apm/api'
|
|
52
|
+
rescue LoadError => e
|
|
53
|
+
SolarWindsAPM.logger.fatal "[solarwinds_apm/error] Couldn't load api: #{e.message}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
SolarWindsAPM::Loading.require_api
|
|
60
|
+
|
|
61
|
+
# Auto-start the Reporter unless we are running Unicorn on Heroku
|
|
62
|
+
# In that case, we start the reporters after fork
|
|
63
|
+
unless SolarWindsAPM.heroku? && SolarWindsAPM.forking_webserver?
|
|
64
|
+
SolarWindsAPM::Reporter.start if SolarWindsAPM.loaded
|
|
65
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'logger'
|
|
5
|
+
|
|
6
|
+
module SolarWindsAPM
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :logger
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
SolarWindsAPM.logger = Logger.new(STDERR)
|
|
13
|
+
# set log level to INFO to be consistent with the c-lib, DEBUG would be default
|
|
14
|
+
SolarWindsAPM.logger.level = Logger::INFO
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Here we can define modules and classes for noop mode.
|
|
2
|
+
|
|
3
|
+
Instead of polluting code with SolarWindsAPM.loaded conditionals
|
|
4
|
+
|
|
5
|
+
we load these classes when in noop mode and they expose noop behavior.
|
|
6
|
+
|
|
7
|
+
so far only one class is needed:
|
|
8
|
+
|
|
9
|
+
- SolarWindsAPM::Context and its toString() method from oboe
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright (c) 2019 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
####
|
|
5
|
+
# noop version of SolarWindsAPM::Context
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
module SolarWindsAPM
|
|
10
|
+
module Context
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# noop version of :toString
|
|
14
|
+
# toString would return the current trace context as string
|
|
15
|
+
#
|
|
16
|
+
def self.toString
|
|
17
|
+
'00-00000000000000000000000000000000-0000000000000000-00'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# noop version of :clear
|
|
22
|
+
#
|
|
23
|
+
def self.clear
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copyright (c) 2019 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
####
|
|
5
|
+
# noop version of SolarWindsAPM::Metadata
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
module SolarWindsAPM
|
|
10
|
+
class Metadata
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# noop version of :makeRandom
|
|
14
|
+
#
|
|
15
|
+
# needs to return an object that responds to :isValid
|
|
16
|
+
#
|
|
17
|
+
def self.makeRandom
|
|
18
|
+
Metadata.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def isValid
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module SolarWindsAPM
|
|
2
|
+
|
|
3
|
+
# override the Ruby method, so that no code related to profiling gets executed
|
|
4
|
+
class Profiling
|
|
5
|
+
|
|
6
|
+
def self.run
|
|
7
|
+
yield
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# these put the c-functions into "noop"
|
|
12
|
+
module CProfiler
|
|
13
|
+
def self.set_interval(_)
|
|
14
|
+
# do nothing
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.get_tid
|
|
18
|
+
return 0
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|