appoptics_apm 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
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,359 @@
1
+ #--
2
+ # Copyright (c) 2016 SolarWinds, LLC.
3
+ # All rights reserved.
4
+ #++
5
+
6
+ # Make sure Set is loaded if possible.
7
+ begin
8
+ require 'set'
9
+ rescue LoadError
10
+ class Set; end # :nodoc:
11
+ end
12
+
13
+
14
+ module AppOpticsAPM
15
+ module API
16
+ ##
17
+ # This modules provides the X-Trace logging facilities.
18
+ #
19
+ # These are the lower level methods, please see AppOpticsAPM::API::Tracing
20
+ # for the higher level methods
21
+ module Logging
22
+ @@ints_or_nil = [Integer, Float, NilClass, String]
23
+ @@ints_or_nil << Fixnum unless RUBY_VERSION >= '2.4'
24
+
25
+ ##
26
+ # Public: Report an event in an active trace.
27
+ #
28
+ # ==== Arguments
29
+ #
30
+ # * +layer+ - The layer the reported event belongs to
31
+ # * +label+ - The label for the reported event. See SDK documentation for reserved labels and usage.
32
+ # * +opts+ - A hash containing key/value pairs that will be reported along with this event (optional).
33
+ # * +event+ - An event to be used instead of generating a new one (see also start_trace_with_target)
34
+ #
35
+ # ==== Example
36
+ #
37
+ # AppOpticsAPM::API.log('logical_layer', 'entry')
38
+ # AppOpticsAPM::API.log('logical_layer', 'info', { :list_length => 20 })
39
+ # AppOpticsAPM::API.log('logical_layer', 'exit')
40
+ #
41
+ # Returns nothing.
42
+ def log(layer, label, opts = {}, event=nil)
43
+ return if !AppOpticsAPM.tracing?
44
+
45
+ event ||= AppOpticsAPM::Context.createEvent
46
+ log_event(layer, label, event, opts)
47
+ end
48
+
49
+ ##
50
+ # Public: Report an exception.
51
+ #
52
+ # ==== Arguments
53
+ #
54
+ # * +layer+ - The layer the reported event belongs to
55
+ # * +exn+ - The exception to report
56
+ # * +opts+ - Custom params if you want to log extra information
57
+ #
58
+ # ==== Example
59
+ #
60
+ # begin
61
+ # my_iffy_method
62
+ # rescue Exception => e
63
+ # AppOpticsAPM::API.log_exception('rails', e, { user: user_id })
64
+ # raise
65
+ # end
66
+ #
67
+ # Returns nothing.
68
+ def log_exception(layer, exn, opts = {})
69
+ return if !AppOpticsAPM.tracing? || exn.instance_variable_get(:@oboe_logged)
70
+
71
+ unless exn
72
+ AppOpticsAPM.logger.debug '[appoptics_apm/debug] log_exception called with nil exception'
73
+ return
74
+ end
75
+
76
+ opts.merge!(:ErrorClass => exn.class.name,
77
+ :ErrorMsg => exn.message,
78
+ :Backtrace => exn.backtrace.join("\r\n")) if exn.backtrace
79
+
80
+ exn.instance_variable_set(:@oboe_logged, true)
81
+ log(layer, :error, opts)
82
+ end
83
+
84
+ ##
85
+ # Public: Decide whether or not to start a trace, and report an entry event
86
+ # appropriately.
87
+ #
88
+ # ==== Attributes
89
+ #
90
+ # * +layer+ - The layer the reported event belongs to
91
+ # * +xtrace+ - An xtrace metadata string, or nil. Used for cross-application tracing.
92
+ # * +opts+ - A hash containing key/value pairs that will be reported along with this event (optional).
93
+ #
94
+ # ==== Example
95
+ #
96
+ # AppOpticsAPM::API.log_start(:layer_name, nil, { :id => @user.id })
97
+ #
98
+ # Returns an xtrace metadata string
99
+ def log_start(layer, xtrace = nil, opts = {})
100
+ return if !AppOpticsAPM.loaded || (opts.key?(:URL) && ::AppOpticsAPM::Util.static_asset?(opts[:URL]))
101
+
102
+ #--
103
+ # Is the below necessary? Only on JRuby? Could there be an existing context but not x-trace header?
104
+ # See discussion at:
105
+ # https://github.com/librato/ruby-tracelytics/pull/6/files?diff=split#r131029135
106
+ #
107
+ # Used by JRuby/Java webservers such as Tomcat
108
+ # AppOpticsAPM::Context.fromString(xtrace) if AppOpticsAPM.pickup_context?(xtrace)
109
+
110
+ # if AppOpticsAPM.tracing?
111
+ # # Pre-existing context. Either we inherited context from an
112
+ # # incoming X-Trace request header or under JRuby, Joboe started
113
+ # # tracing before the JRuby code was called (e.g. Tomcat)
114
+ # AppOpticsAPM.is_continued_trace = true
115
+
116
+ # if AppOpticsAPM.has_xtrace_header
117
+ # opts[:TraceOrigin] = :continued_header
118
+ # elsif AppOpticsAPM.has_incoming_context
119
+ # opts[:TraceOrigin] = :continued_context
120
+ # else
121
+ # opts[:TraceOrigin] = :continued
122
+ # end
123
+
124
+ # return log_entry(layer, opts)
125
+ # end
126
+ #++
127
+
128
+ if AppOpticsAPM.sample?(opts.merge(:layer => layer, :xtrace => xtrace))
129
+ # Yes, we're sampling this request
130
+ # Probablistic tracing of a subset of requests based off of
131
+ # sample rate and sample source
132
+ opts[:SampleRate] = AppOpticsAPM.sample_rate
133
+ opts[:SampleSource] = AppOpticsAPM.sample_source
134
+ opts[:TraceOrigin] = :always_sampled
135
+
136
+ if xtrace_v2?(xtrace)
137
+ # continue valid incoming xtrace
138
+ # use it for current context, ensuring sample bit is set
139
+ AppOpticsAPM::XTrace.set_sampled(xtrace)
140
+
141
+ md = AppOpticsAPM::Metadata.fromString(xtrace)
142
+ AppOpticsAPM::Context.fromString(xtrace)
143
+ log_event(layer, :entry, md.createEvent, opts)
144
+ else
145
+ # discard invalid incoming xtrace
146
+ # create a new context, ensuring sample bit set
147
+ md = AppOpticsAPM::Metadata.makeRandom(true)
148
+ AppOpticsAPM::Context.set(md)
149
+ log_event(layer, :entry, AppOpticsAPM::Event.startTrace(md), opts)
150
+ end
151
+ else
152
+ # No, we're not sampling this request
153
+ # set the context but don't log the event
154
+ if xtrace_v2?(xtrace)
155
+ # continue valid incoming xtrace
156
+ # use it for current context, ensuring sample bit is not set
157
+ AppOpticsAPM::XTrace.unset_sampled(xtrace)
158
+ AppOpticsAPM::Context.fromString(xtrace)
159
+ else
160
+ # discard invalid incoming xtrace
161
+ # create a new context, ensuring sample bit not set
162
+ md = AppOpticsAPM::Metadata.makeRandom(false)
163
+ AppOpticsAPM::Context.fromString(md.toString)
164
+ end
165
+ end
166
+ AppOpticsAPM::Context.toString
167
+ end
168
+
169
+ ##
170
+ # Public: Report an exit event and potentially clear the tracing context.
171
+ #
172
+ # ==== Attributes
173
+ #
174
+ # * +layer+ - The layer the reported event belongs to
175
+ # * +opts+ - A hash containing key/value pairs that will be reported along with this event (optional).
176
+ #
177
+ # ==== Example
178
+ #
179
+ # AppOpticsAPM::API.log_end(:layer_name, { :id => @user.id })
180
+ #
181
+ # Returns an xtrace metadata string
182
+ def log_end(layer, opts = {})
183
+ return unless AppOpticsAPM.tracing?
184
+
185
+ log_event(layer, :exit, AppOpticsAPM::Context.createEvent, opts)
186
+ AppOpticsAPM::Context.toString
187
+ ensure
188
+ AppOpticsAPM::Context.clear unless AppOpticsAPM.has_incoming_context?
189
+ end
190
+
191
+ ##
192
+ # Public: Log an entry event
193
+ #
194
+ # A helper method to create and log an entry event
195
+ #
196
+ # ==== Attributes
197
+ #
198
+ # * +layer+ - The layer the reported event belongs to
199
+ # * +kvs+ - A hash containing key/value pairs that will be reported along with this event (optional).
200
+ # * +op+ - To identify the current operation being traced. Used to avoid double tracing recursive calls.
201
+ #
202
+ # ==== Example
203
+ #
204
+ # AppOpticsAPM::API.log_entry(:layer_name, { :id => @user.id })
205
+ #
206
+ # Returns an xtrace metadata string
207
+ def log_entry(layer, opts = {}, op = nil)
208
+ return unless AppOpticsAPM.tracing?
209
+
210
+ AppOpticsAPM.layer_op = op.to_sym if op
211
+ log_event(layer, :entry, AppOpticsAPM::Context.createEvent, opts)
212
+ end
213
+
214
+ ##
215
+ # Public: Log an info event
216
+ #
217
+ # A helper method to create and log an info event
218
+ #
219
+ # ==== Attributes
220
+ #
221
+ # * +layer+ - The layer the reported event belongs to
222
+ # * +opts+ - A hash containing key/value pairs that will be reported along with this event (optional).
223
+ #
224
+ # ==== Example
225
+ #
226
+ # AppOpticsAPM::API.log_info(:layer_name, { :id => @user.id })
227
+ #
228
+ # Returns an xtrace metadata string
229
+ def log_info(layer, kvs = {})
230
+ return unless AppOpticsAPM.tracing?
231
+
232
+ log_event(layer, :info, AppOpticsAPM::Context.createEvent, kvs)
233
+ end
234
+
235
+ ##
236
+ # Public: Log an exit event
237
+ #
238
+ # A helper method to create and log an exit event
239
+ #
240
+ # ==== Attributes
241
+ #
242
+ # * +layer+ - The layer the reported event belongs to
243
+ # * +opts+ - A hash containing key/value pairs that will be reported along with this event (optional).
244
+ # * +_op+ - deprecated, has been used to avoid double tracing recursive calls, but is irrelevant in +log_exit+
245
+ #
246
+ # ==== Example
247
+ #
248
+ # AppOpticsAPM::API.log_exit(:layer_name, { :id => @user.id })
249
+ #
250
+ # Returns an xtrace metadata string (TODO: does it?)
251
+ def log_exit(layer, opts = {}, _op = nil)
252
+ return unless AppOpticsAPM.tracing?
253
+
254
+ AppOpticsAPM.layer_op = nil
255
+ log_event(layer, :exit, AppOpticsAPM::Context.createEvent, opts)
256
+ end
257
+
258
+ ##
259
+ # Public: Log an exit event from multiple requests
260
+ #
261
+ # A helper method to create and log an info event
262
+ # If we return from a request that faned out multiple requests
263
+ # we can add the collected X-Traces to the exit event
264
+ #
265
+ # ==== Attributes
266
+ #
267
+ # * +layer+ - The layer the reported event belongs to
268
+ # * +traces+ - An array with X-Trace strings returned from the requests
269
+ #
270
+ def log_multi_exit(layer, traces)
271
+ return unless AppOpticsAPM.tracing?
272
+ task_id = AppOpticsAPM::XTrace.task_id(AppOpticsAPM::Context.toString)
273
+ event = AppOpticsAPM::Context.createEvent
274
+ traces.each do |trace|
275
+ event.addEdgeStr(trace) if AppOpticsAPM::XTrace.task_id(trace) == task_id
276
+ end
277
+ log_event(layer, :exit, event)
278
+ end
279
+
280
+ ##
281
+ #:nodoc:
282
+ # Internal: Reports agent init to the collector
283
+ #
284
+ # ==== Attributes
285
+ #
286
+ # * +layer+ - The layer the reported event belongs to
287
+ # * +opts+ - A hash containing key/value pairs that will be reported along with this event
288
+ def log_init(layer = :rack, opts = {})
289
+ context = AppOpticsAPM::Metadata.makeRandom
290
+ if !context.isValid
291
+ return
292
+ end
293
+
294
+ event = context.createEvent
295
+ event.addInfo(APPOPTICS_STR_LAYER, layer.to_s)
296
+ event.addInfo(APPOPTICS_STR_LABEL, 'single')
297
+ opts.each do |k, v|
298
+ event.addInfo(k, v.to_s)
299
+ end
300
+
301
+ AppOpticsAPM::Reporter.sendStatus(event, context)
302
+ end
303
+
304
+ private
305
+
306
+ ##
307
+ #:nodoc:
308
+ # Internal: Report an event.
309
+ #
310
+ # ==== Attributes
311
+ #
312
+ # * +layer+ - The layer the reported event belongs to
313
+ # * +label+ - The label for the reported event. See API documentation for reserved labels and usage.
314
+ # * +event+ - The pre-existing AppOpticsAPM context event. See AppOpticsAPM::Context.createEvent
315
+ # * +opts+ - A hash containing key/value pairs that will be reported along with this event (optional).
316
+ #
317
+ # ==== Example
318
+ #
319
+ # entry = AppOpticsAPM::Context.createEvent
320
+ # AppOpticsAPM::API.log_event(:layer_name, 'entry', entry_event, { :id => @user.id })
321
+ #
322
+ # exit_event = AppOpticsAPM::Context.createEvent
323
+ # exit_event.addEdge(entry.getMetadata)
324
+ # AppOpticsAPM::API.log_event(:layer_name, 'exit', exit_event, { :id => @user.id })
325
+ #
326
+ def log_event(layer, label, event, opts = {})
327
+ event.addInfo(APPOPTICS_STR_LAYER, layer.to_s.freeze) if layer
328
+ event.addInfo(APPOPTICS_STR_LABEL, label.to_s.freeze)
329
+
330
+ AppOpticsAPM.layer = layer.to_sym if label == :entry
331
+ AppOpticsAPM.layer = nil if label == :exit
332
+
333
+ opts.each do |k, v|
334
+ value = nil
335
+
336
+ next unless valid_key? k
337
+
338
+ if @@ints_or_nil.include?(v.class)
339
+ value = v
340
+ elsif v.class == Set
341
+ value = v.to_a.to_s
342
+ else
343
+ value = v.to_s if v.respond_to?(:to_s)
344
+ end
345
+
346
+ begin
347
+ event.addInfo(k.to_s, value)
348
+ rescue ArgumentError => e
349
+ AppOpticsAPM.logger.debug "[AppOpticsAPM/debug] Couldn't add event KV: #{k} => #{v.class}"
350
+ AppOpticsAPM.logger.debug "[AppOpticsAPM/debug] #{e.message}"
351
+ end
352
+ end if !opts.nil? && opts.any?
353
+
354
+ AppOpticsAPM::Reporter.sendReport(event)
355
+ end
356
+
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,34 @@
1
+ #--
2
+ # Copyright (c) 2016 SolarWinds, LLC.
3
+ # All rights reserved.
4
+ #++
5
+
6
+ module AppOpticsAPM
7
+ module API
8
+ ##
9
+ # Utility methods for the Memcache instrumentation
10
+ module Memcache
11
+ MEMCACHE_OPS = %w(add append cas decr decrement delete fetch get incr increment prepend replace set)
12
+
13
+ def memcache_hit?(result)
14
+ result.nil? ? 0 : 1
15
+ end
16
+
17
+ def remote_host(key)
18
+ return unless defined?(Lib.memcached_server_by_key) &&
19
+ defined?(@struct) && defined?(is_unix_socket?)
20
+
21
+ server_as_array = Lib.memcached_server_by_key(@struct, key.to_s)
22
+
23
+ return unless server_as_array.is_a?(Array)
24
+
25
+ server = server_as_array.first
26
+ if is_unix_socket?(server)
27
+ 'localhost'
28
+ elsif defined?(server.hostname)
29
+ server.hostname
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,201 @@
1
+ #--
2
+ # Copyright (c) 2016 SolarWinds, LLC.
3
+ # All rights reserved.
4
+ #++
5
+
6
+ module AppOpticsAPM
7
+ module API
8
+ ##
9
+ # Module that provides profiling of arbitrary blocks of code
10
+ module Profiling
11
+ ##
12
+ # Public: Profile a given block of code. Detect any exceptions thrown by
13
+ # the block and report errors.
14
+ #
15
+ # profile_name - A name used to identify the block being profiled.
16
+ # report_kvs - A hash containing key/value pairs that will be reported along
17
+ # with the event of this profile (optional).
18
+ # with_backtrace - Boolean to indicate whether a backtrace should
19
+ # be collected with this trace event.
20
+ #
21
+ # Example
22
+ #
23
+ # def computation(n)
24
+ # AppOpticsAPM::API.profile('fib', { :n => n }) do
25
+ # fib(n)
26
+ # end
27
+ # end
28
+ #
29
+ # Returns the result of the block.
30
+ #
31
+
32
+ def profile(profile_name, report_kvs = {}, with_backtrace = false)
33
+ return yield unless AppOpticsAPM.tracing?
34
+
35
+ report_kvs[:Language] ||= :ruby
36
+ report_kvs[:ProfileName] ||= profile_name
37
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if with_backtrace
38
+
39
+ AppOpticsAPM::API.log(nil, :profile_entry, report_kvs)
40
+
41
+ begin
42
+ yield
43
+ rescue => e
44
+ log_exception(nil, e)
45
+ raise
46
+ ensure
47
+ exit_kvs = {}
48
+ exit_kvs[:Language] = :ruby
49
+ exit_kvs[:ProfileName] = report_kvs[:ProfileName]
50
+
51
+ AppOpticsAPM::API.log(nil, :profile_exit, exit_kvs)
52
+ end
53
+ end
54
+
55
+ ##
56
+ # Public: Profile a method on a class or module. That method can be of any (accessible)
57
+ # type (instance, singleton, private, protected etc.).
58
+ #
59
+ # klass - the class or module that has the method to profile
60
+ # method - the method to profile. Can be singleton, instance, private etc...
61
+ # opts - a hash specifying the one or more of the following options:
62
+ # * :arguments - report the arguments passed to <tt>method</tt> on each profile (default: false)
63
+ # * :result - report the return value of <tt>method</tt> on each profile (default: false)
64
+ # * :backtrace - report the return value of <tt>method</tt> on each profile (default: false)
65
+ # * :name - alternate name for the profile reported in the dashboard (default: method name)
66
+ # extra_kvs - a hash containing any additional KVs you would like reported with the profile
67
+ #
68
+ # Example
69
+ #
70
+ # opts = {}
71
+ # opts[:backtrace] = true
72
+ # opts[:arguments] = false
73
+ # opts[:name] = :array_sort
74
+ #
75
+ # AppOpticsAPM::API.profile_method(Array, :sort, opts)
76
+ #
77
+ def profile_method(klass, method, opts = {}, extra_kvs = {})
78
+ # If we're on an unsupported platform (ahem Mac), just act
79
+ # like we did something to nicely play the no-op part.
80
+ return true unless AppOpticsAPM.loaded
81
+
82
+ if RUBY_VERSION < '1.9.3'
83
+ AppOpticsAPM.logger.warn '[appoptics_apm/error] profile_method: Use the legacy method profiling for Ruby versions before 1.9.3'
84
+ return false
85
+
86
+ elsif !klass.is_a?(Module)
87
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: Not sure what to do with #{klass}. Send a class or module."
88
+ return false
89
+
90
+ elsif !method.is_a?(Symbol)
91
+ if method.is_a?(String)
92
+ method = method.to_sym
93
+ else
94
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: Not sure what to do with #{method}. Send a string or symbol for method."
95
+ return false
96
+ end
97
+ end
98
+
99
+ instance_method = klass.instance_methods.include?(method) || klass.private_instance_methods.include?(method)
100
+ class_method = klass.singleton_methods.include?(method)
101
+
102
+ # Make sure the request klass::method exists
103
+ if !instance_method && !class_method
104
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: Can't instrument #{klass}.#{method} as it doesn't seem to exist."
105
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] #{__FILE__}:#{__LINE__}"
106
+ return false
107
+ end
108
+
109
+ # Strip '!' or '?' from method if present
110
+ safe_method_name = method.to_s.chop if method.to_s =~ /\?$|\!$/
111
+ safe_method_name ||= method
112
+
113
+ without_appoptics = "#{safe_method_name}_without_appoptics"
114
+ with_appoptics = "#{safe_method_name}_with_appoptics"
115
+
116
+ # Check if already profiled
117
+ if klass.instance_methods.include?(with_appoptics.to_sym) ||
118
+ klass.singleton_methods.include?(with_appoptics.to_sym)
119
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: #{klass}::#{method} already profiled."
120
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: #{__FILE__}:#{__LINE__}"
121
+ return false
122
+ end
123
+
124
+ source_location = []
125
+ if instance_method
126
+ ::AppOpticsAPM::Util.send_include(klass, ::AppOpticsAPM::MethodProfiling)
127
+ source_location = klass.instance_method(method).source_location
128
+ elsif class_method
129
+ ::AppOpticsAPM::Util.send_extend(klass, ::AppOpticsAPM::MethodProfiling)
130
+ source_location = klass.method(method).source_location
131
+ end
132
+
133
+ report_kvs = collect_profile_kvs(klass, method, opts, extra_kvs, source_location)
134
+ report_kvs[:MethodName] = safe_method_name
135
+
136
+ if instance_method
137
+ klass.class_eval do
138
+ define_method(with_appoptics) do |*args, &block|
139
+ profile_wrapper(without_appoptics, report_kvs, opts, *args, &block)
140
+ end
141
+
142
+ alias_method without_appoptics, method.to_s
143
+ alias_method method.to_s, with_appoptics
144
+ end
145
+ elsif class_method
146
+ klass.define_singleton_method(with_appoptics) do |*args, &block|
147
+ profile_wrapper(without_appoptics, report_kvs, opts, *args, &block)
148
+ end
149
+
150
+ klass.singleton_class.class_eval do
151
+ alias_method without_appoptics, method.to_s
152
+ alias_method method.to_s, with_appoptics
153
+ end
154
+ end
155
+ true
156
+ end
157
+
158
+ private
159
+
160
+ ##
161
+ # Private: Helper method to aggregate KVs to report
162
+ #
163
+ # klass - the class or module that has the method to profile
164
+ # method - the method to profile. Can be singleton, instance, private etc...
165
+ # opts - a hash specifying the one or more of the following options:
166
+ # * :arguments - report the arguments passed to <tt>method</tt> on each profile (default: false)
167
+ # * :result - report the return value of <tt>method</tt> on each profile (default: false)
168
+ # * :backtrace - report the return value of <tt>method</tt> on each profile (default: false)
169
+ # * :name - alternate name for the profile reported in the dashboard (default: method name)
170
+ # extra_kvs - a hash containing any additional KVs you would like reported with the profile
171
+ # source_location - array returned from klass.method(:name).source_location
172
+ #
173
+ def collect_profile_kvs(klass, method, opts, extra_kvs, source_location)
174
+ report_kvs = {}
175
+ report_kvs[:Language] ||= :ruby
176
+ report_kvs[:ProfileName] ||= opts[:name] ? opts[:name] : method
177
+
178
+ if klass.is_a?(Class)
179
+ report_kvs[:Class] = klass.to_s
180
+ else
181
+ report_kvs[:Module] = klass.to_s
182
+ end
183
+
184
+ # If this is a Rails Controller, report the KVs
185
+ if defined?(::AbstractController::Base) && klass.ancestors.include?(::AbstractController::Base)
186
+ report_kvs[:Controller] = klass.to_s
187
+ report_kvs[:Action] = method.to_s
188
+ end
189
+
190
+ # We won't have access to this info for native methods (those not defined in Ruby).
191
+ if source_location.is_a?(Array) && source_location.length == 2
192
+ report_kvs[:File] = source_location[0]
193
+ report_kvs[:LineNumber] = source_location[1]
194
+ end
195
+
196
+ # Merge in any extra_kvs requested
197
+ report_kvs.merge!(extra_kvs)
198
+ end
199
+ end
200
+ end
201
+ end