solarwinds_apm 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +5 -0
  3. data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
  4. data/.github/workflows/build_and_release_gem.yml +112 -0
  5. data/.github/workflows/build_for_packagecloud.yml +70 -0
  6. data/.github/workflows/docker-images.yml +47 -0
  7. data/.github/workflows/run_cpluplus_tests.yml +73 -0
  8. data/.github/workflows/run_tests.yml +155 -0
  9. data/.github/workflows/scripts/test_install.rb +23 -0
  10. data/.github/workflows/swig/swig-v4.0.2.tar.gz +0 -0
  11. data/.github/workflows/test_on_4_linux.yml +161 -0
  12. data/.gitignore +39 -0
  13. data/.rubocop.yml +29 -0
  14. data/.yardopts +7 -0
  15. data/CHANGELOG.md +769 -0
  16. data/CONFIG.md +31 -0
  17. data/Gemfile +14 -0
  18. data/LICENSE +202 -0
  19. data/README.md +383 -0
  20. data/bin/solarwinds_apm_config +15 -0
  21. data/examples/prepend.rb +13 -0
  22. data/examples/sdk_examples.rb +158 -0
  23. data/ext/oboe_metal/README.md +69 -0
  24. data/ext/oboe_metal/extconf.rb +141 -0
  25. data/ext/oboe_metal/extconf_local.rb +75 -0
  26. data/ext/oboe_metal/lib/.keep +0 -0
  27. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.0.0.0.sha256 +1 -0
  28. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.0.0.0.sha256 +1 -0
  29. data/ext/oboe_metal/noop/noop.c +8 -0
  30. data/ext/oboe_metal/src/README.md +6 -0
  31. data/ext/oboe_metal/src/VERSION +2 -0
  32. data/ext/oboe_metal/src/bson/bson.h +220 -0
  33. data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
  34. data/ext/oboe_metal/src/frames.cc +247 -0
  35. data/ext/oboe_metal/src/frames.h +40 -0
  36. data/ext/oboe_metal/src/init_solarwinds_apm.cc +21 -0
  37. data/ext/oboe_metal/src/logging.cc +95 -0
  38. data/ext/oboe_metal/src/logging.h +35 -0
  39. data/ext/oboe_metal/src/oboe.h +1169 -0
  40. data/ext/oboe_metal/src/oboe_api.cpp +658 -0
  41. data/ext/oboe_metal/src/oboe_api.hpp +433 -0
  42. data/ext/oboe_metal/src/oboe_debug.h +59 -0
  43. data/ext/oboe_metal/src/oboe_swig_wrap.cc +7562 -0
  44. data/ext/oboe_metal/src/profiling.cc +435 -0
  45. data/ext/oboe_metal/src/profiling.h +78 -0
  46. data/ext/oboe_metal/test/CMakeLists.txt +53 -0
  47. data/ext/oboe_metal/test/FindGMock.cmake +43 -0
  48. data/ext/oboe_metal/test/README.md +56 -0
  49. data/ext/oboe_metal/test/frames_test.cc +164 -0
  50. data/ext/oboe_metal/test/profiling_test.cc +93 -0
  51. data/ext/oboe_metal/test/ruby_inc_dir.rb +8 -0
  52. data/ext/oboe_metal/test/ruby_prefix.rb +8 -0
  53. data/ext/oboe_metal/test/ruby_test_helper.rb +67 -0
  54. data/ext/oboe_metal/test/test.h +11 -0
  55. data/ext/oboe_metal/test/test_main.cc +32 -0
  56. data/init.rb +4 -0
  57. data/lib/oboe.rb +7 -0
  58. data/lib/oboe_metal.rb +172 -0
  59. data/lib/rails/generators/solarwinds_apm/install_generator.rb +47 -0
  60. data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +424 -0
  61. data/lib/solarwinds_apm/api/layerinit.rb +41 -0
  62. data/lib/solarwinds_apm/api/logging.rb +356 -0
  63. data/lib/solarwinds_apm/api/memcache.rb +37 -0
  64. data/lib/solarwinds_apm/api/metrics.rb +63 -0
  65. data/lib/solarwinds_apm/api/util.rb +98 -0
  66. data/lib/solarwinds_apm/api.rb +21 -0
  67. data/lib/solarwinds_apm/base.rb +160 -0
  68. data/lib/solarwinds_apm/config.rb +301 -0
  69. data/lib/solarwinds_apm/frameworks/grape.rb +96 -0
  70. data/lib/solarwinds_apm/frameworks/padrino.rb +78 -0
  71. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller.rb +100 -0
  72. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  73. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  74. data/lib/solarwinds_apm/frameworks/rails/inst/action_view.rb +88 -0
  75. data/lib/solarwinds_apm/frameworks/rails/inst/active_record.rb +26 -0
  76. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
  77. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +22 -0
  78. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +103 -0
  79. data/lib/solarwinds_apm/frameworks/rails/inst/logger_formatters.rb +14 -0
  80. data/lib/solarwinds_apm/frameworks/rails.rb +100 -0
  81. data/lib/solarwinds_apm/frameworks/sinatra.rb +96 -0
  82. data/lib/solarwinds_apm/inst/bunny-client.rb +157 -0
  83. data/lib/solarwinds_apm/inst/bunny-consumer.rb +102 -0
  84. data/lib/solarwinds_apm/inst/curb.rb +288 -0
  85. data/lib/solarwinds_apm/inst/dalli.rb +89 -0
  86. data/lib/solarwinds_apm/inst/delayed_job.rb +100 -0
  87. data/lib/solarwinds_apm/inst/excon.rb +113 -0
  88. data/lib/solarwinds_apm/inst/faraday.rb +96 -0
  89. data/lib/solarwinds_apm/inst/graphql.rb +206 -0
  90. data/lib/solarwinds_apm/inst/grpc_client.rb +147 -0
  91. data/lib/solarwinds_apm/inst/grpc_server.rb +119 -0
  92. data/lib/solarwinds_apm/inst/httpclient.rb +181 -0
  93. data/lib/solarwinds_apm/inst/logger_formatter.rb +46 -0
  94. data/lib/solarwinds_apm/inst/logging_log_event.rb +24 -0
  95. data/lib/solarwinds_apm/inst/lumberjack_formatter.rb +9 -0
  96. data/lib/solarwinds_apm/inst/memcached.rb +86 -0
  97. data/lib/solarwinds_apm/inst/mongo.rb +246 -0
  98. data/lib/solarwinds_apm/inst/mongo2.rb +225 -0
  99. data/lib/solarwinds_apm/inst/moped.rb +466 -0
  100. data/lib/solarwinds_apm/inst/net_http.rb +60 -0
  101. data/lib/solarwinds_apm/inst/rack.rb +217 -0
  102. data/lib/solarwinds_apm/inst/rack_cache.rb +35 -0
  103. data/lib/solarwinds_apm/inst/redis.rb +273 -0
  104. data/lib/solarwinds_apm/inst/resque.rb +129 -0
  105. data/lib/solarwinds_apm/inst/rest-client.rb +43 -0
  106. data/lib/solarwinds_apm/inst/sequel.rb +241 -0
  107. data/lib/solarwinds_apm/inst/sidekiq-client.rb +63 -0
  108. data/lib/solarwinds_apm/inst/sidekiq-worker.rb +64 -0
  109. data/lib/solarwinds_apm/inst/typhoeus.rb +90 -0
  110. data/lib/solarwinds_apm/instrumentation.rb +22 -0
  111. data/lib/solarwinds_apm/loading.rb +65 -0
  112. data/lib/solarwinds_apm/logger.rb +14 -0
  113. data/lib/solarwinds_apm/noop/README.md +9 -0
  114. data/lib/solarwinds_apm/noop/context.rb +26 -0
  115. data/lib/solarwinds_apm/noop/metadata.rb +25 -0
  116. data/lib/solarwinds_apm/noop/profiling.rb +21 -0
  117. data/lib/solarwinds_apm/oboe_init_options.rb +191 -0
  118. data/lib/solarwinds_apm/ruby.rb +35 -0
  119. data/lib/solarwinds_apm/sdk/current_trace_info.rb +123 -0
  120. data/lib/solarwinds_apm/sdk/custom_metrics.rb +94 -0
  121. data/lib/solarwinds_apm/sdk/logging.rb +37 -0
  122. data/lib/solarwinds_apm/sdk/trace_context_headers.rb +69 -0
  123. data/lib/solarwinds_apm/sdk/tracing.rb +432 -0
  124. data/lib/solarwinds_apm/support/profiling.rb +22 -0
  125. data/lib/solarwinds_apm/support/trace_context.rb +53 -0
  126. data/lib/solarwinds_apm/support/trace_state.rb +69 -0
  127. data/lib/solarwinds_apm/support/trace_string.rb +89 -0
  128. data/lib/solarwinds_apm/support/transaction_metrics.rb +67 -0
  129. data/lib/solarwinds_apm/support/transaction_settings.rb +233 -0
  130. data/lib/solarwinds_apm/support/x_trace_options.rb +113 -0
  131. data/lib/solarwinds_apm/support.rb +12 -0
  132. data/lib/solarwinds_apm/support_report.rb +113 -0
  133. data/lib/solarwinds_apm/test.rb +165 -0
  134. data/lib/solarwinds_apm/thread_local.rb +26 -0
  135. data/lib/solarwinds_apm/util.rb +334 -0
  136. data/lib/solarwinds_apm/version.rb +17 -0
  137. data/lib/solarwinds_apm.rb +72 -0
  138. data/log/.keep +0 -0
  139. data/log/postgresql/.keep +0 -0
  140. data/solarwinds_apm.gemspec +52 -0
  141. data/yardoc_frontpage.md +24 -0
  142. 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