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.
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