appoptics_apm 4.0.2

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 (226) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +43 -0
  3. data/.dockerignore +5 -0
  4. data/.gitignore +23 -0
  5. data/.rubocop.yml +5 -0
  6. data/.travis.yml +82 -0
  7. data/CHANGELOG.md +769 -0
  8. data/CONFIG.md +33 -0
  9. data/Dockerfile +41 -0
  10. data/Dockerfile_test +66 -0
  11. data/Gemfile +41 -0
  12. data/LICENSE +193 -0
  13. data/README.md +351 -0
  14. data/Rakefile +202 -0
  15. data/Vagrantfile +67 -0
  16. data/appoptics_apm.gemspec +55 -0
  17. data/build_gems.sh +15 -0
  18. data/docker-compose.yml +73 -0
  19. data/examples/DNT.md +35 -0
  20. data/examples/carrying_context.rb +220 -0
  21. data/examples/instrumenting_metal_controller.rb +8 -0
  22. data/examples/puma_on_heroku_config.rb +17 -0
  23. data/examples/tracing_async_threads.rb +124 -0
  24. data/examples/tracing_background_jobs.rb +53 -0
  25. data/examples/tracing_forked_processes.rb +99 -0
  26. data/examples/unicorn_on_heroku_config.rb +28 -0
  27. data/ext/oboe_metal/extconf.rb +54 -0
  28. data/ext/oboe_metal/lib/.keep +0 -0
  29. data/ext/oboe_metal/lib/liboboe-1.0.so.0.0.0 +0 -0
  30. data/ext/oboe_metal/noop/noop.c +7 -0
  31. data/ext/oboe_metal/src/VERSION +1 -0
  32. data/ext/oboe_metal/src/bson/bson.h +221 -0
  33. data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
  34. data/ext/oboe_metal/src/oboe.h +883 -0
  35. data/ext/oboe_metal/src/oboe.hpp +793 -0
  36. data/ext/oboe_metal/src/oboe_debug.h +50 -0
  37. data/ext/oboe_metal/src/oboe_wrap.cxx +6088 -0
  38. data/ext/oboe_metal/tests/test.rb +11 -0
  39. data/gemfiles/delayed_job.gemfile +36 -0
  40. data/gemfiles/frameworks.gemfile +44 -0
  41. data/gemfiles/instrumentation_mocked.gemfile +29 -0
  42. data/gemfiles/libraries.gemfile +85 -0
  43. data/gemfiles/rails23.gemfile +39 -0
  44. data/gemfiles/rails30.gemfile +42 -0
  45. data/gemfiles/rails31.gemfile +44 -0
  46. data/gemfiles/rails32.gemfile +54 -0
  47. data/gemfiles/rails40.gemfile +27 -0
  48. data/gemfiles/rails41.gemfile +27 -0
  49. data/gemfiles/rails42.gemfile +35 -0
  50. data/gemfiles/rails50.gemfile +44 -0
  51. data/gemfiles/rails51.gemfile +44 -0
  52. data/get_version.rb +5 -0
  53. data/init.rb +4 -0
  54. data/lib/appoptics_apm/api/layerinit.rb +39 -0
  55. data/lib/appoptics_apm/api/logging.rb +359 -0
  56. data/lib/appoptics_apm/api/memcache.rb +34 -0
  57. data/lib/appoptics_apm/api/profiling.rb +201 -0
  58. data/lib/appoptics_apm/api/tracing.rb +152 -0
  59. data/lib/appoptics_apm/api/util.rb +128 -0
  60. data/lib/appoptics_apm/api.rb +18 -0
  61. data/lib/appoptics_apm/base.rb +252 -0
  62. data/lib/appoptics_apm/config.rb +281 -0
  63. data/lib/appoptics_apm/frameworks/grape.rb +93 -0
  64. data/lib/appoptics_apm/frameworks/padrino/templates.rb +58 -0
  65. data/lib/appoptics_apm/frameworks/padrino.rb +52 -0
  66. data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +106 -0
  67. data/lib/appoptics_apm/frameworks/rails/inst/action_controller2.rb +61 -0
  68. data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +58 -0
  69. data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
  70. data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  71. data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  72. data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +58 -0
  73. data/lib/appoptics_apm/frameworks/rails/inst/action_view_2x.rb +56 -0
  74. data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +50 -0
  75. data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +27 -0
  76. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
  77. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +28 -0
  78. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +30 -0
  79. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +120 -0
  80. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +101 -0
  81. data/lib/appoptics_apm/frameworks/rails.rb +116 -0
  82. data/lib/appoptics_apm/frameworks/sinatra/templates.rb +56 -0
  83. data/lib/appoptics_apm/frameworks/sinatra.rb +71 -0
  84. data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
  85. data/lib/appoptics_apm/inst/bunny-consumer.rb +92 -0
  86. data/lib/appoptics_apm/inst/curb.rb +329 -0
  87. data/lib/appoptics_apm/inst/dalli.rb +85 -0
  88. data/lib/appoptics_apm/inst/delayed_job.rb +92 -0
  89. data/lib/appoptics_apm/inst/em-http-request.rb +105 -0
  90. data/lib/appoptics_apm/inst/excon.rb +130 -0
  91. data/lib/appoptics_apm/inst/faraday.rb +77 -0
  92. data/lib/appoptics_apm/inst/http.rb +83 -0
  93. data/lib/appoptics_apm/inst/httpclient.rb +176 -0
  94. data/lib/appoptics_apm/inst/memcache.rb +102 -0
  95. data/lib/appoptics_apm/inst/memcached.rb +94 -0
  96. data/lib/appoptics_apm/inst/mongo.rb +242 -0
  97. data/lib/appoptics_apm/inst/mongo2.rb +225 -0
  98. data/lib/appoptics_apm/inst/moped.rb +466 -0
  99. data/lib/appoptics_apm/inst/rack.rb +146 -0
  100. data/lib/appoptics_apm/inst/redis.rb +275 -0
  101. data/lib/appoptics_apm/inst/resque.rb +151 -0
  102. data/lib/appoptics_apm/inst/rest-client.rb +50 -0
  103. data/lib/appoptics_apm/inst/sequel.rb +178 -0
  104. data/lib/appoptics_apm/inst/sidekiq-client.rb +53 -0
  105. data/lib/appoptics_apm/inst/sidekiq-worker.rb +67 -0
  106. data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
  107. data/lib/appoptics_apm/inst/typhoeus.rb +113 -0
  108. data/lib/appoptics_apm/instrumentation.rb +22 -0
  109. data/lib/appoptics_apm/legacy_method_profiling.rb +97 -0
  110. data/lib/appoptics_apm/loading.rb +66 -0
  111. data/lib/appoptics_apm/logger.rb +41 -0
  112. data/lib/appoptics_apm/method_profiling.rb +33 -0
  113. data/lib/appoptics_apm/ruby.rb +35 -0
  114. data/lib/appoptics_apm/support.rb +135 -0
  115. data/lib/appoptics_apm/test.rb +94 -0
  116. data/lib/appoptics_apm/thread_local.rb +26 -0
  117. data/lib/appoptics_apm/util.rb +312 -0
  118. data/lib/appoptics_apm/version.rb +15 -0
  119. data/lib/appoptics_apm/xtrace.rb +103 -0
  120. data/lib/appoptics_apm.rb +72 -0
  121. data/lib/joboe_metal.rb +214 -0
  122. data/lib/oboe/README +2 -0
  123. data/lib/oboe/backward_compatibility.rb +80 -0
  124. data/lib/oboe/inst/rack.rb +11 -0
  125. data/lib/oboe.rb +7 -0
  126. data/lib/oboe_metal.rb +187 -0
  127. data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
  128. data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +222 -0
  129. data/ruby_setup.sh +47 -0
  130. data/run_docker_build_gem_upload_to_packagecloud.sh +20 -0
  131. data/run_tests_docker.rb +32 -0
  132. data/test/benchmark/README.md +65 -0
  133. data/test/benchmark/logging_bench.rb +54 -0
  134. data/test/benchmark/with_libraries_gemfile/bunny_bench.rb +69 -0
  135. data/test/benchmark/with_rails5x_gemfile/action_controller5x_bench.rb +43 -0
  136. data/test/frameworks/apps/grape_nested.rb +33 -0
  137. data/test/frameworks/apps/grape_simple.rb +80 -0
  138. data/test/frameworks/apps/padrino_simple.rb +80 -0
  139. data/test/frameworks/apps/sinatra_simple.rb +55 -0
  140. data/test/frameworks/grape_test.rb +286 -0
  141. data/test/frameworks/padrino_test.rb +222 -0
  142. data/test/frameworks/rails3x_test.rb +554 -0
  143. data/test/frameworks/rails4x_test.rb +570 -0
  144. data/test/frameworks/rails5x_api_test.rb +210 -0
  145. data/test/frameworks/rails5x_test.rb +376 -0
  146. data/test/frameworks/rails_shared_tests.rb +172 -0
  147. data/test/frameworks/sinatra_test.rb +140 -0
  148. data/test/instrumentation/bunny_client_test.rb +276 -0
  149. data/test/instrumentation/bunny_consumer_test.rb +204 -0
  150. data/test/instrumentation/curb_test.rb +398 -0
  151. data/test/instrumentation/dalli_test.rb +177 -0
  152. data/test/instrumentation/em_http_request_test.rb +89 -0
  153. data/test/instrumentation/excon_test.rb +231 -0
  154. data/test/instrumentation/faraday_test.rb +228 -0
  155. data/test/instrumentation/http_test.rb +143 -0
  156. data/test/instrumentation/httpclient_test.rb +320 -0
  157. data/test/instrumentation/memcache_test.rb +260 -0
  158. data/test/instrumentation/memcached_test.rb +229 -0
  159. data/test/instrumentation/mongo_v1_test.rb +479 -0
  160. data/test/instrumentation/mongo_v2_index_test.rb +124 -0
  161. data/test/instrumentation/mongo_v2_test.rb +584 -0
  162. data/test/instrumentation/mongo_v2_view_test.rb +435 -0
  163. data/test/instrumentation/moped_test.rb +517 -0
  164. data/test/instrumentation/rack_test.rb +165 -0
  165. data/test/instrumentation/redis_hashes_test.rb +268 -0
  166. data/test/instrumentation/redis_keys_test.rb +321 -0
  167. data/test/instrumentation/redis_lists_test.rb +310 -0
  168. data/test/instrumentation/redis_misc_test.rb +163 -0
  169. data/test/instrumentation/redis_sets_test.rb +296 -0
  170. data/test/instrumentation/redis_sortedsets_test.rb +328 -0
  171. data/test/instrumentation/redis_strings_test.rb +349 -0
  172. data/test/instrumentation/resque_test.rb +185 -0
  173. data/test/instrumentation/rest-client_test.rb +288 -0
  174. data/test/instrumentation/sequel_mysql2_test.rb +353 -0
  175. data/test/instrumentation/sequel_mysql_test.rb +334 -0
  176. data/test/instrumentation/sequel_pg_test.rb +336 -0
  177. data/test/instrumentation/sidekiq-client_test.rb +159 -0
  178. data/test/instrumentation/sidekiq-worker_test.rb +180 -0
  179. data/test/instrumentation/twitter-cassandra_test.rb +424 -0
  180. data/test/instrumentation/typhoeus_test.rb +284 -0
  181. data/test/jobs/delayed_job/db_worker_job.rb +29 -0
  182. data/test/jobs/delayed_job/error_worker_job.rb +10 -0
  183. data/test/jobs/delayed_job/remote_call_worker_job.rb +20 -0
  184. data/test/jobs/resque/db_worker_job.rb +29 -0
  185. data/test/jobs/resque/error_worker_job.rb +10 -0
  186. data/test/jobs/resque/remote_call_worker_job.rb +20 -0
  187. data/test/jobs/sidekiq/db_worker_job.rb +29 -0
  188. data/test/jobs/sidekiq/error_worker_job.rb +10 -0
  189. data/test/jobs/sidekiq/remote_call_worker_job.rb +20 -0
  190. data/test/minitest_helper.rb +276 -0
  191. data/test/mocked/curb_mocked_test.rb +311 -0
  192. data/test/mocked/excon_mocked_test.rb +166 -0
  193. data/test/mocked/faraday_mocked_test.rb +93 -0
  194. data/test/mocked/http_mocked_test.rb +129 -0
  195. data/test/mocked/httpclient_mocked_test.rb +245 -0
  196. data/test/mocked/rest_client_mocked_test.rb +103 -0
  197. data/test/mocked/typhoeus_mocked_test.rb +192 -0
  198. data/test/models/widget.rb +36 -0
  199. data/test/profiling/legacy_method_profiling_test.rb +201 -0
  200. data/test/profiling/method_profiling_test.rb +631 -0
  201. data/test/queues/delayed_job-client_test.rb +95 -0
  202. data/test/queues/delayed_job-worker_test.rb +91 -0
  203. data/test/reporter/reporter_test.rb +14 -0
  204. data/test/servers/delayed_job.rb +107 -0
  205. data/test/servers/rackapp_8101.rb +29 -0
  206. data/test/servers/rails3x_8140.rb +96 -0
  207. data/test/servers/rails4x_8140.rb +96 -0
  208. data/test/servers/rails5x_8140.rb +95 -0
  209. data/test/servers/rails5x_api_8150.rb +78 -0
  210. data/test/servers/sidekiq.rb +29 -0
  211. data/test/servers/sidekiq.yml +7 -0
  212. data/test/servers/sidekiq_initializer.rb +25 -0
  213. data/test/settings +0 -0
  214. data/test/support/auto_tracing_test.rb +50 -0
  215. data/test/support/backcompat_test.rb +276 -0
  216. data/test/support/config_test.rb +149 -0
  217. data/test/support/dnt_test.rb +98 -0
  218. data/test/support/init_report_test.rb +25 -0
  219. data/test/support/liboboe_settings_test.rb +110 -0
  220. data/test/support/logging_test.rb +130 -0
  221. data/test/support/noop_test.rb +88 -0
  222. data/test/support/sql_sanitize_test.rb +55 -0
  223. data/test/support/tracing_mode_test.rb +33 -0
  224. data/test/support/tvalias_test.rb +15 -0
  225. data/test/support/xtrace_test.rb +41 -0
  226. metadata +475 -0
@@ -0,0 +1,466 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ require 'json'
5
+
6
+ module AppOpticsAPM
7
+ module Inst
8
+ ##
9
+ # Moped
10
+ #
11
+ module Moped
12
+ FLAVOR = :mongodb
13
+
14
+ # Moped::Database
15
+ DB_OPS = [:command, :drop].freeze
16
+
17
+ # Moped::Indexes
18
+ INDEX_OPS = [:create, :drop].freeze
19
+
20
+ # Moped::Query
21
+ QUERY_OPS = [:count, :sort, :limit, :distinct, :update, :update_all, :upsert,
22
+ :explain, :modify, :remove, :remove_all].freeze
23
+
24
+ # Moped::Collection
25
+ COLLECTION_OPS = [:drop, :find, :indexes, :insert, :aggregate].freeze
26
+
27
+ ##
28
+ # remote_host
29
+ #
30
+ # This utility method converts the server into a host:port
31
+ # pair for reporting
32
+ #
33
+ def remote_host(server)
34
+ if ::Moped::VERSION < '2.0.0'
35
+ server
36
+ else
37
+ "#{server.address.host}:#{server.address.port}"
38
+ end
39
+ end
40
+ end
41
+
42
+ ##
43
+ # MopedDatabase
44
+ #
45
+ module MopedDatabase
46
+ include AppOpticsAPM::Inst::Moped
47
+
48
+ def self.included(klass)
49
+ AppOpticsAPM::Inst::Moped::DB_OPS.each do |m|
50
+ ::AppOpticsAPM::Util.method_alias(klass, m)
51
+ end
52
+ end
53
+
54
+ def extract_trace_details(op)
55
+ report_kvs = {}
56
+ report_kvs[:Flavor] = AppOpticsAPM::Inst::Moped::FLAVOR
57
+ # FIXME: We're only grabbing the first of potentially multiple servers here
58
+ report_kvs[:RemoteHost] = remote_host(session.cluster.seeds.first)
59
+ report_kvs[:Database] = name
60
+ report_kvs[:QueryOp] = op.to_s
61
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:moped][:collect_backtraces]
62
+ rescue StandardError => e
63
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
64
+ ensure
65
+ return report_kvs
66
+ end
67
+
68
+ def command_with_appoptics(command)
69
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.layer_op && command.key?(:mapreduce)
70
+ begin
71
+ report_kvs = extract_trace_details(:map_reduce)
72
+ report_kvs[:Map_Function] = command[:map]
73
+ report_kvs[:Reduce_Function] = command[:reduce]
74
+ rescue => e
75
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
76
+ end
77
+
78
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
79
+ command_without_appoptics(command)
80
+ end
81
+ else
82
+ command_without_appoptics(command)
83
+ end
84
+ end
85
+
86
+ def drop_with_appoptics
87
+ return drop_without_appoptics unless AppOpticsAPM.tracing?
88
+
89
+ report_kvs = extract_trace_details(:drop_database)
90
+
91
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
92
+ drop_without_appoptics
93
+ end
94
+ end
95
+ end
96
+
97
+ ##
98
+ # MopedIndexes
99
+ #
100
+ module MopedIndexes
101
+ include AppOpticsAPM::Inst::Moped
102
+
103
+ def self.included(klass)
104
+ AppOpticsAPM::Inst::Moped::INDEX_OPS.each do |m|
105
+ ::AppOpticsAPM::Util.method_alias(klass, m)
106
+ end
107
+ end
108
+
109
+ def extract_trace_details(op)
110
+ report_kvs = {}
111
+ report_kvs[:Flavor] = AppOpticsAPM::Inst::Moped::FLAVOR
112
+
113
+ # FIXME: We're only grabbing the first of potentially multiple servers here
114
+ first = database.session.cluster.seeds.first
115
+ if ::Moped::VERSION < '2.0.0'
116
+ report_kvs[:RemoteHost] = first
117
+ else
118
+ report_kvs[:RemoteHost] = "#{first.address.host}:#{first.address.port}"
119
+ end
120
+ report_kvs[:Database] = database.name
121
+ report_kvs[:QueryOp] = op.to_s
122
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:moped][:collect_backtraces]
123
+ rescue StandardError => e
124
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
125
+ ensure
126
+ return report_kvs
127
+ end
128
+
129
+ def create_with_appoptics(key, options = {})
130
+ return create_without_appoptics(key, options) unless AppOpticsAPM.tracing?
131
+
132
+ begin
133
+ # We report :create_index here to be consistent
134
+ # with other mongo implementations
135
+ report_kvs = extract_trace_details(:create_index)
136
+ report_kvs[:Key] = key.to_json
137
+ report_kvs[:Options] = options.to_json
138
+ rescue StandardError => e
139
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
140
+ end
141
+
142
+ AppOpticsAPM::API.trace(:mongo, report_kvs, :create_index) do
143
+ create_without_appoptics(key, options = {})
144
+ end
145
+ end
146
+
147
+ def drop_with_appoptics(key = nil)
148
+ return drop_without_appoptics(key) unless AppOpticsAPM.tracing?
149
+
150
+ begin
151
+ # We report :drop_indexes here to be consistent
152
+ # with other mongo implementations
153
+ report_kvs = extract_trace_details(:drop_indexes)
154
+ report_kvs[:Key] = key.nil? ? :all : key.to_json
155
+ rescue StandardError => e
156
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
157
+ end
158
+
159
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
160
+ drop_without_appoptics(key = nil)
161
+ end
162
+ end
163
+ end
164
+
165
+ ##
166
+ # MopedQuery
167
+ #
168
+ module MopedQuery
169
+ include AppOpticsAPM::Inst::Moped
170
+
171
+ def self.included(klass)
172
+ AppOpticsAPM::Inst::Moped::QUERY_OPS.each do |m|
173
+ ::AppOpticsAPM::Util.method_alias(klass, m)
174
+ end
175
+ end
176
+
177
+ def extract_trace_details(op)
178
+ report_kvs = {}
179
+ report_kvs[:Flavor] = AppOpticsAPM::Inst::Moped::FLAVOR
180
+ # FIXME: We're only grabbing the first of potentially multiple servers here
181
+ first = collection.database.session.cluster.seeds.first
182
+ report_kvs[:RemoteHost] = remote_host(first)
183
+ report_kvs[:Database] = collection.database.name
184
+ report_kvs[:Collection] = collection.name
185
+ report_kvs[:QueryOp] = op.to_s
186
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:moped][:collect_backtraces]
187
+ rescue StandardError => e
188
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
189
+ ensure
190
+ return report_kvs
191
+ end
192
+
193
+ def count_with_appoptics
194
+ return count_without_appoptics unless AppOpticsAPM.tracing?
195
+
196
+ begin
197
+ report_kvs = extract_trace_details(:count)
198
+ report_kvs[:Query] = selector.empty? ? :all : selector.to_json
199
+ rescue StandardError => e
200
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
201
+ end
202
+
203
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
204
+ count_without_appoptics
205
+ end
206
+ end
207
+
208
+ def sort_with_appoptics(sort)
209
+ return sort_without_appoptics(sort) unless AppOpticsAPM.tracing?
210
+
211
+ begin
212
+ report_kvs = extract_trace_details(:sort)
213
+ report_kvs[:Query] = selector.empty? ? :all : selector.to_json
214
+ report_kvs[:Order] = sort.to_s
215
+ rescue StandardError => e
216
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
217
+ end
218
+
219
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
220
+ sort_without_appoptics(sort)
221
+ end
222
+ end
223
+
224
+ def limit_with_appoptics(limit)
225
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:explain)
226
+ begin
227
+ report_kvs = extract_trace_details(:limit)
228
+ report_kvs[:Query] = selector.empty? ? :all : selector.to_json
229
+ report_kvs[:Limit] = limit.to_s
230
+ rescue StandardError => e
231
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
232
+ end
233
+
234
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
235
+ limit_without_appoptics(limit)
236
+ end
237
+ else
238
+ limit_without_appoptics(limit)
239
+ end
240
+ end
241
+
242
+ def distinct_with_appoptics(key)
243
+ return distinct_without_appoptics(key) unless AppOpticsAPM.tracing?
244
+
245
+ begin
246
+ report_kvs = extract_trace_details(:distinct)
247
+ report_kvs[:Query] = selector.empty? ? :all : selector.to_json
248
+ report_kvs[:Key] = key.to_s
249
+ rescue StandardError => e
250
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
251
+ end
252
+
253
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
254
+ distinct_without_appoptics(key)
255
+ end
256
+ end
257
+
258
+ def update_with_appoptics(change, flags = nil)
259
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?([:update_all, :upsert])
260
+ begin
261
+ report_kvs = extract_trace_details(:update)
262
+ report_kvs[:Flags] = flags.to_s if flags
263
+ report_kvs[:Update_Document] = change.to_json
264
+ rescue StandardError => e
265
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
266
+ end
267
+
268
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
269
+ update_without_appoptics(change, flags)
270
+ end
271
+ else
272
+ update_without_appoptics(change, flags)
273
+ end
274
+ end
275
+
276
+ def update_all_with_appoptics(change)
277
+ return update_all_without_appoptics(change) unless AppOpticsAPM.tracing?
278
+
279
+ begin
280
+ report_kvs = extract_trace_details(:update_all)
281
+ report_kvs[:Update_Document] = change.to_json
282
+ rescue StandardError => e
283
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
284
+ end
285
+
286
+ AppOpticsAPM::API.trace(:mongo, report_kvs, :update_all) do
287
+ update_all_without_appoptics(change)
288
+ end
289
+ end
290
+
291
+ def upsert_with_appoptics(change)
292
+ return upsert_without_appoptics(change) unless AppOpticsAPM.tracing?
293
+
294
+ begin
295
+ report_kvs = extract_trace_details(:upsert)
296
+ report_kvs[:Query] = selector.to_json
297
+ report_kvs[:Update_Document] = change.to_json
298
+ rescue StandardError => e
299
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
300
+ end
301
+
302
+ AppOpticsAPM::API.trace(:mongo, report_kvs, :upsert) do
303
+ upsert_without_appoptics(change)
304
+ end
305
+ end
306
+
307
+ def explain_with_appoptics
308
+ return explain_without_appoptics unless AppOpticsAPM.tracing?
309
+
310
+ begin
311
+ report_kvs = extract_trace_details(:explain)
312
+ report_kvs[:Query] = selector.empty? ? :all : selector.to_json
313
+ rescue StandardError => e
314
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
315
+ end
316
+
317
+ AppOpticsAPM::API.trace(:mongo, report_kvs, :explain) do
318
+ explain_without_appoptics
319
+ end
320
+ end
321
+
322
+ def modify_with_appoptics(change, options = {})
323
+ return modify_without_appoptics(change, options) unless AppOpticsAPM.tracing?
324
+
325
+ begin
326
+ report_kvs = extract_trace_details(:modify)
327
+ report_kvs[:Update_Document] = selector.empty? ? :all : selector.to_json
328
+ report_kvs[:Change] = change.to_json
329
+ report_kvs[:Options] = options.to_json
330
+ rescue StandardError => e
331
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
332
+ end
333
+
334
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
335
+ modify_without_appoptics(change, options)
336
+ end
337
+ end
338
+
339
+ def remove_with_appoptics
340
+ return remove_without_appoptics unless AppOpticsAPM.tracing?
341
+
342
+ begin
343
+ report_kvs = extract_trace_details(:remove)
344
+ report_kvs[:Query] = selector.to_json
345
+ rescue StandardError => e
346
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
347
+ end
348
+
349
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
350
+ remove_without_appoptics
351
+ end
352
+ end
353
+
354
+ def remove_all_with_appoptics
355
+ return remove_all_without_appoptics unless AppOpticsAPM.tracing?
356
+
357
+ begin
358
+ report_kvs = extract_trace_details(:remove_all)
359
+ report_kvs[:Query] = selector.to_json
360
+ rescue StandardError => e
361
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
362
+ end
363
+
364
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
365
+ remove_all_without_appoptics
366
+ end
367
+ end
368
+ end
369
+
370
+ ##
371
+ # MopedCollection
372
+ #
373
+ module MopedCollection
374
+ include AppOpticsAPM::Inst::Moped
375
+
376
+ def self.included(klass)
377
+ AppOpticsAPM::Inst::Moped::COLLECTION_OPS.each do |m|
378
+ ::AppOpticsAPM::Util.method_alias(klass, m)
379
+ end
380
+ end
381
+
382
+ def extract_trace_details(op)
383
+ report_kvs = {}
384
+ report_kvs[:Flavor] = AppOpticsAPM::Inst::Moped::FLAVOR
385
+ # FIXME: We're only grabbing the first of potentially multiple servers here
386
+ report_kvs[:RemoteHost] = remote_host(database.session.cluster.seeds.first)
387
+ report_kvs[:Database] = database.name
388
+ report_kvs[:Collection] = name
389
+ report_kvs[:QueryOp] = op.to_s
390
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:moped][:collect_backtraces]
391
+ rescue StandardError => e
392
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
393
+ ensure
394
+ return report_kvs
395
+ end
396
+
397
+ def drop_with_appoptics
398
+ return drop_without_appoptics unless AppOpticsAPM.tracing?
399
+
400
+ # We report :drop_collection here to be consistent
401
+ # with other mongo implementations
402
+ report_kvs = extract_trace_details(:drop_collection)
403
+
404
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
405
+ drop_without_appoptics
406
+ end
407
+ end
408
+
409
+ def find_with_appoptics(selector = {})
410
+ return find_without_appoptics(selector) unless AppOpticsAPM.tracing?
411
+
412
+ begin
413
+ report_kvs = extract_trace_details(:find)
414
+ report_kvs[:Query] = selector.empty? ? 'all' : selector.to_json
415
+ rescue StandardError => e
416
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
417
+ end
418
+
419
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
420
+ find_without_appoptics(selector)
421
+ end
422
+ end
423
+
424
+ def indexes_with_appoptics
425
+ return indexes_without_appoptics unless AppOpticsAPM.tracing?
426
+
427
+ report_kvs = extract_trace_details(:indexes)
428
+
429
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
430
+ indexes_without_appoptics
431
+ end
432
+ end
433
+
434
+ def insert_with_appoptics(documents, flags = nil)
435
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:create_index)
436
+ report_kvs = extract_trace_details(:insert)
437
+
438
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
439
+ insert_without_appoptics(documents, flags)
440
+ end
441
+ else
442
+ insert_without_appoptics(documents, flags)
443
+ end
444
+ end
445
+
446
+ def aggregate_with_appoptics(*pipeline)
447
+ return aggregate_without_appoptics(*pipeline) unless AppOpticsAPM.tracing?
448
+
449
+ report_kvs = extract_trace_details(:aggregate)
450
+ report_kvs[:Query] = pipeline
451
+
452
+ AppOpticsAPM::API.trace(:mongo, report_kvs) do
453
+ aggregate_without_appoptics(pipeline)
454
+ end
455
+ end
456
+ end
457
+ end
458
+ end
459
+
460
+ if defined?(::Moped) && AppOpticsAPM::Config[:moped][:enabled]
461
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting moped' if AppOpticsAPM::Config[:verbose]
462
+ ::AppOpticsAPM::Util.send_include(::Moped::Database, ::AppOpticsAPM::Inst::MopedDatabase)
463
+ ::AppOpticsAPM::Util.send_include(::Moped::Collection, ::AppOpticsAPM::Inst::MopedCollection)
464
+ ::AppOpticsAPM::Util.send_include(::Moped::Query, ::AppOpticsAPM::Inst::MopedQuery)
465
+ ::AppOpticsAPM::Util.send_include(::Moped::Indexes, ::AppOpticsAPM::Inst::MopedIndexes)
466
+ end
@@ -0,0 +1,146 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ require 'uri'
5
+ require 'cgi'
6
+
7
+ module AppOpticsAPM
8
+ ##
9
+ # AppOpticsAPM::Rack
10
+ #
11
+ # The AppOpticsAPM::Rack middleware used to sample a subset of incoming
12
+ # requests for instrumentation and reporting. Tracing context can
13
+ # be received here (via the X-Trace HTTP header) or initiated here
14
+ # based on configured tracing mode.
15
+ #
16
+ # After the rack layer passes on to the following layers (Rails, Sinatra,
17
+ # Padrino, Grape), then the instrumentation downstream will
18
+ # automatically detect whether this is a sampled request or not
19
+ # and act accordingly. (to instrument or not)
20
+ #
21
+ class Rack
22
+ attr_reader :app
23
+
24
+ def initialize(app)
25
+ @app = app
26
+ end
27
+
28
+ def collect(req, env)
29
+ report_kvs = {}
30
+
31
+ begin
32
+ report_kvs[:'HTTP-Host'] = req.host
33
+ report_kvs[:Port] = req.port
34
+ report_kvs[:Proto] = req.scheme
35
+ report_kvs[:Method] = req.request_method
36
+ report_kvs[:AJAX] = true if req.xhr?
37
+ report_kvs[:ClientIP] = req.ip
38
+
39
+ if AppOpticsAPM::Config[:rack][:log_args]
40
+ report_kvs[:'Query-String'] = ::CGI.unescape(req.query_string) unless req.query_string.empty?
41
+ end
42
+
43
+ # Report any request queue'ing headers. Report as 'Request-Start' or the summed Queue-Time
44
+ report_kvs[:'Request-Start'] = env['HTTP_X_REQUEST_START'] if env.key?('HTTP_X_REQUEST_START')
45
+ report_kvs[:'Request-Start'] = env['HTTP_X_QUEUE_START'] if env.key?('HTTP_X_QUEUE_START')
46
+ report_kvs[:'Queue-Time'] = env['HTTP_X_QUEUE_TIME'] if env.key?('HTTP_X_QUEUE_TIME')
47
+
48
+ report_kvs[:'Forwarded-For'] = env['HTTP_X_FORWARDED_FOR'] if env.key?('HTTP_X_FORWARDED_FOR')
49
+ report_kvs[:'Forwarded-Host'] = env['HTTP_X_FORWARDED_HOST'] if env.key?('HTTP_X_FORWARDED_HOST')
50
+ report_kvs[:'Forwarded-Proto'] = env['HTTP_X_FORWARDED_PROTO'] if env.key?('HTTP_X_FORWARDED_PROTO')
51
+ report_kvs[:'Forwarded-Port'] = env['HTTP_X_FORWARDED_PORT'] if env.key?('HTTP_X_FORWARDED_PORT')
52
+
53
+ report_kvs[:'Ruby.AppOpticsAPM.Version'] = ::AppOpticsAPM::Version::STRING
54
+ report_kvs[:ProcessID] = Process.pid
55
+ report_kvs[:ThreadID] = Thread.current.to_s[/0x\w*/]
56
+ rescue StandardError => e
57
+ # Discard any potential exceptions. Debug log and report whatever we can.
58
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Rack KV collection error: #{e.inspect}"
59
+ end
60
+ report_kvs
61
+ end
62
+
63
+ def call(env)
64
+ start = Time.now
65
+ status = 500
66
+ req = ::Rack::Request.new(env)
67
+ req_url = req.url # saving it here because rails3.2 overrides it when there is a 500 error
68
+
69
+ # In the case of nested Ruby apps such as Grape inside of Rails
70
+ # or Grape inside of Grape, each app has it's own instance
71
+ # of rack middleware. We avoid tracing rack more than once and
72
+ # instead start instrumenting from the first rack pass.
73
+ if AppOpticsAPM.tracing? && AppOpticsAPM.layer == :rack
74
+ AppOpticsAPM.logger.debug "[appoptics_apm/rack] Rack skipped!"
75
+ return @app.call(env)
76
+ end
77
+
78
+ begin
79
+ report_kvs = {}
80
+
81
+ report_kvs[:URL] = AppOpticsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
82
+
83
+ # Check for and validate X-Trace request header to pick up tracing context
84
+ xtrace = env.is_a?(Hash) ? env['HTTP_X_TRACE'] : nil
85
+ xtrace_header = AppOpticsAPM::XTrace.valid?(xtrace) ? xtrace : nil
86
+
87
+ # Under JRuby, JAppOpticsAPM may have already started a trace. Make note of this
88
+ # if so and don't clear context on log_end (see appoptics_apm/api/logging.rb)
89
+ AppOpticsAPM.has_incoming_context = AppOpticsAPM.tracing?
90
+ AppOpticsAPM.has_xtrace_header = xtrace_header
91
+ AppOpticsAPM.is_continued_trace = AppOpticsAPM.has_incoming_context || AppOpticsAPM.has_xtrace_header
92
+
93
+ xtrace = AppOpticsAPM::API.log_start(:rack, xtrace_header, report_kvs)
94
+
95
+ # We only trace a subset of requests based off of sample rate so if
96
+ # AppOpticsAPM::API.log_start really did start a trace, we act accordingly here.
97
+ if AppOpticsAPM.tracing?
98
+ report_kvs = collect(req, env)
99
+
100
+ # We log an info event with the HTTP KVs found in AppOpticsAPM::Rack.collect
101
+ # This is done here so in the case of stacks that try/catch/abort
102
+ # (looking at you Grape) we're sure the KVs get reported now as
103
+ # this code may not be returned to later.
104
+ AppOpticsAPM::API.log_info(:rack, report_kvs)
105
+
106
+ status, headers, response = @app.call(env)
107
+
108
+ xtrace = AppOpticsAPM::API.log_end(:rack, :Status => status)
109
+
110
+ else
111
+ status, headers, response = @app.call(env)
112
+ end
113
+
114
+ [status, headers, response]
115
+ rescue Exception => e
116
+ # it is ok to rescue Exception here because we are reraising it (we just need a chance to log_end)
117
+ if AppOpticsAPM.tracing?
118
+ AppOpticsAPM::API.log_exception(:rack, e)
119
+ xtrace = AppOpticsAPM::API.log_end(:rack, :Status => 500, 'TransactionName' => transaction_name(env))
120
+ end
121
+ raise
122
+ ensure
123
+ if headers && AppOpticsAPM::XTrace.valid?(xtrace)
124
+ unless defined?(JRUBY_VERSION) && AppOpticsAPM.is_continued_trace?
125
+ headers['X-Trace'] = xtrace if headers.is_a?(Hash)
126
+ end
127
+ end
128
+ end
129
+ ensure
130
+ unless ::AppOpticsAPM::Util.static_asset?(env['PATH_INFO'])
131
+ status = status.to_i
132
+ error = status.between?(500,599) ? 1 : 0
133
+ duration =(1000 * 1000 * (Time.now - start)).round(0)
134
+ AppOpticsAPM::Span.createHttpSpan(transaction_name(env), req_url, duration, status, req.request_method, error)
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def transaction_name(env)
141
+ if env['appoptics_apm.controller'] || env['appoptics_apm.action']
142
+ [env['appoptics_apm.controller'], env['appoptics_apm.action']].join('.')
143
+ end
144
+ end
145
+ end
146
+ end