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