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,73 @@
1
+ version: "2"
2
+
3
+ #########################################################################################################
4
+ #
5
+ # to start the services:
6
+ # > docker-compose up -d
7
+ # ('ruby_appoptics' and 'wait' will exit)
8
+ #
9
+ # to run some ruby tests:
10
+ # > docker-compose run --service-ports ruby_appoptics /code/ruby-appoptics_apm/ruby_setup.sh 2.3.1 gemfiles/rails42.gemfile
11
+ #
12
+ ########################################################################################################
13
+
14
+ services:
15
+ ruby_appoptics_apm:
16
+ container_name: ruby_appoptics_apm
17
+ image: ruby_appoptics_apm
18
+ build:
19
+ context: .
20
+ dockerfile: ./Dockerfile_test
21
+ cpu_quota: 100000 # 1 cpu, assumes cpu_period of 1 second
22
+ mem_limit: 1G
23
+ volumes:
24
+ - .:/code/ruby-appoptics_apm
25
+ depends_on:
26
+ - wait
27
+ links:
28
+ - wait
29
+ environment:
30
+ - APPOPTICS_RABBITMQ_SERVER=rabbitmq
31
+ - DOCKER_MYSQL_PASS=admin
32
+ - MYSQL_ALLOW_EMPTY_PASSWORD=yes
33
+ - MYSQL_ROOT_PASSWORD=admin
34
+ - MYSQL_HOST=mysql
35
+ - APPOPTICS_MONGO_SERVER=mongo
36
+
37
+ rabbitmq:
38
+ container_name: rabbitmq
39
+ image: rabbitmq:3-management
40
+ ports:
41
+ - "8080:15672"
42
+ - "5672:5672"
43
+ - "5671:5671"
44
+
45
+ mysql:
46
+ container_name: mysql
47
+ image: mysql:5.7.13
48
+ environment:
49
+ - MYSQL_ALLOW_EMPTY_PASSWORD=yes
50
+ - MYSQL_ROOT_PASSWORD=admin
51
+ ports:
52
+ - "3306:3306"
53
+
54
+ mongo:
55
+ container_name: mongo
56
+ image: mongo
57
+ ports:
58
+ - "27017:27017"
59
+
60
+ wait:
61
+ container_name: wait
62
+ image: waisbrot/wait
63
+ depends_on:
64
+ - rabbitmq
65
+ - mysql
66
+ - mongo
67
+ links:
68
+ - rabbitmq
69
+ - mysql
70
+ - mongo
71
+ environment:
72
+ - TARGETS=mysql:3306;rabbitmq:5672;mongo:27017
73
+
data/examples/DNT.md ADDED
@@ -0,0 +1,35 @@
1
+ By default, the AppOpticsAPM gem will not trace routes with extensions
2
+ for common static files. Examples of such files may be images,
3
+ javascript, pdfs and text files.
4
+
5
+ This is done by using the regular expression stored in
6
+ `AppOpticsAPM::Config[:dnt_regexp]`:
7
+
8
+ .(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|ttf|woff|svg|less)$
9
+
10
+ This string is used as a regular expression and is tested against
11
+ candidate URLs to be instrumented.
12
+
13
+ To replace the pattern in use, you can update this regular expression
14
+ string. Here are some examples.
15
+
16
+ If you prefer that you want your javascript and CSS files instrumented,
17
+ you can update `AppOpticsAPM::Config[:dnt_regexp]` with an updated regexp
18
+ pattern (without the "js" and "css" entries):
19
+
20
+ .(jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|flv|swf|ttf|woff|svg|less)$
21
+
22
+ If you prefer to not instrument all javascript files except for one
23
+ named `show.js`, you could put this assignment into your initializer,
24
+ rackup file or application.rb (note that this example uses a standard
25
+ regexp [negative
26
+ look-behind](http://www.regular-expressions.info/lookaround.html) that
27
+ isn't supported in Ruby 1.8):
28
+
29
+ AppOpticsAPM::Config[:dnt_regexp] = "(\.js$)(?<!show.js)"
30
+
31
+ Since this pattern is used with the standard Ruby Regexp class, you can
32
+ use any Regexp supported pattern. See the documentation on Ruby Regexp
33
+ [here](https://www.omniref.com/ruby/2.2.0/symbols/Regexp?d=380181456&n=0#doc_uncollapsed=true&d=380181456&n=0)
34
+ or you can also view the oboe gem [source code documentation for this
35
+ feature](https://github.com/tracelytics/ruby-appoptics/blob/master/lib/appoptics/config.rb#L129).
@@ -0,0 +1,220 @@
1
+ ###############################################################
2
+ # A brief overview of AppOpticsAPM tracing context
3
+ ###############################################################
4
+ #
5
+ # Tracing context is the state held when AppOpticsAPM is instrumenting a
6
+ # transaction, block, request etc.. This context is advanced as
7
+ # new blocks are instrumented and this chain of context is used
8
+ # by AppOpticsAPM to later reassemble performance data to be displayed
9
+ # in the AppOptics dashboard.
10
+ #
11
+ # Tracing context is non-existent until established by calling
12
+ # `AppOpticsAPM::API.start_trace` or `AppOpticsAPM::API.log_start`. Those methods
13
+ # are part of the high-level and low-level API respectively.
14
+ #
15
+ # After a tracing context is established, that context can be
16
+ # continued by calling `AppOpticsAPM::API.trace` or `AppOpticsAPM::API.log_entry`.
17
+ # These methods will advance an existing context but not start
18
+ # new one.
19
+ #
20
+ # For example, when a web request comes into a stack, a tracing
21
+ # context is established using `AppOpticsAPM::API.log_start` as the request
22
+ # enters through the rack middleware via `::AppOpticsAPM::Rack`.
23
+ #
24
+ # That tracing context is then continued using `AppOpticsAPM::API.trace` or
25
+ # `AppOpticsAPM::API.log_entry` for each subsequent layer such as Rails,
26
+ # ActiveRecord, Redis, Memcache, ActionView, Mongo (etc...) until
27
+ # finally request processing is complete and the tracing context
28
+ # is cleared (AppOpticsAPM::Context.clear)
29
+ #
30
+
31
+ ###############################################################
32
+ # Carrying Context
33
+ ###############################################################
34
+ #
35
+ # The tracing context exists in the form of an X-Trace string and
36
+ # can be retrieved using 'AppOpticsAPM::Context.toString'
37
+ #
38
+ # xtrace = AppOpticsAPM::Context.toString
39
+ #
40
+ # => "1B4EDAB9E028CA3C81BCD57CC4644B4C4AE239C7B713F0BCB9FAD6D562"
41
+ #
42
+ # Tracing context can also be picked up from a pre-existing
43
+ # X-Trace string:
44
+ #
45
+ # xtrace = "1B4EDAB9E028CA3C81BCD57CC4644B4C4AE239C7B713F0BCB9FAD6D562"
46
+ #
47
+ # AppOpticsAPM::Context.fromString(xtrace)
48
+ #
49
+ # With these two methods, context can be passed across threads,
50
+ # processes (via fork) and in requests (such as external HTTP
51
+ # requests where the X-Trace is inserted in request headers).
52
+ #
53
+ #
54
+
55
+ ###############################################################
56
+ # Two Options for Spawned Tracing
57
+ ###############################################################
58
+ #
59
+ # When your application needs to instrument code that forks,
60
+ # spawns a thread or does something in-parallel, you have the
61
+ # option to either link those child traces to the parent or
62
+ # trace them as individuals (but with identifying information).
63
+ #
64
+ # Linking parent and child has it's benefits as in the
65
+ # AppOptics dashboard, you will see how a process may spawn
66
+ # a task in parallel and in a single view see the performance
67
+ # of both.
68
+ #
69
+ # The limitation of this is that this is only useful if your
70
+ # parent process spawns only a limited number of child traces.
71
+ #
72
+ # If your parent process is spawning many child tasks (e.g.
73
+ # twenty, hundreds, thousands or more) it's best to trace those
74
+ # child tasks as individuals and pass in identifier Key-Values
75
+ # (such as task ID, job ID etc..)
76
+ #
77
+ # In the examples below, I show implementations of both linked
78
+ # asynchronous traces and separated independent traces.
79
+
80
+ ###############################################################
81
+ # Thread - with separated traces
82
+ ###############################################################
83
+
84
+ AppOpticsAPM::API.log_start('parent')
85
+
86
+ # Get the work to be done
87
+ job = get_work
88
+
89
+ Thread.new do
90
+ # This is a new thread so there is no pre-existing context so
91
+ # we'll call `AppOpticsAPM::API.log_start` to start a new trace context.
92
+ AppOpticsAPM::API.log_start('worker_thread', :job_id => job.id)
93
+
94
+ # Do the work
95
+ do_the_work(job)
96
+
97
+ AppOpticsAPM::API.log_end('worker_thread')
98
+ end
99
+
100
+ AppOpticsAPM::API.log_end('parent')
101
+
102
+ ###############################################################
103
+ #
104
+ # This will generate two independent traces with the following
105
+ # topology.
106
+ #
107
+ # 'parent'
108
+ # ------------------------------------------------------------
109
+ #
110
+ # 'worker_thread'
111
+ # ------------------------------------------------------------
112
+ #
113
+
114
+ ###############################################################
115
+ # Thread - with linked asynchronous traces
116
+ ###############################################################
117
+
118
+ # Since the following example spawns a thread without waiting
119
+ # for it to return, we carry over the context and we mark the
120
+ # trace generated in that thread to be asynchronous using
121
+ # the `Async` flag.
122
+
123
+ AppOpticsAPM::API.log_start('parent')
124
+
125
+ # Save the context to be imported in spawned thread
126
+ tracing_context = AppOpticsAPM::Context.toString
127
+
128
+ # Get the work to be done
129
+ job = get_work
130
+
131
+ Thread.new do
132
+ # Restore context
133
+ AppOpticsAPM::Context.fromString(tracing_context)
134
+
135
+ AppOpticsAPM::API.log_entry('worker_thread')
136
+
137
+ # Do the work
138
+ do_the_work(job)
139
+
140
+ AppOpticsAPM::API.log_exit('worker_thread', :Async => 1)
141
+ end
142
+
143
+ AppOpticsAPM::API.log_end('parent')
144
+
145
+ ###############################################################
146
+ #
147
+ # This will generate a single trace with an asynchronous
148
+ # branch like the following
149
+ #
150
+ # 'parent'
151
+ # ------------------------------------------------------------
152
+ # \
153
+ # \
154
+ # ------------------------------------------------------
155
+ # 'worker_thread'
156
+ #
157
+
158
+ ###############################################################
159
+ # Process via fork - with separated traces
160
+ ###############################################################
161
+
162
+ AppOpticsAPM::API.start_trace('parent_process') do
163
+ # Get some work to process
164
+ job = get_job
165
+
166
+ # fork process to handle work
167
+ fork do
168
+ # Since fork does a complete process copy, the tracing_context still exists
169
+ # so we have to clear it and start again.
170
+ AppOpticsAPM::Context.clear
171
+
172
+ AppOpticsAPM::API.start_trace('worker_process', nil, :job_id => job.id) do
173
+ do_work(job)
174
+ end
175
+ end
176
+
177
+ end
178
+
179
+ ###############################################################
180
+ #
181
+ # This will generate two independent traces:
182
+ #
183
+ # 'parent_process'
184
+ # ------------------------------------------------------------
185
+ #
186
+ # 'worker_process'
187
+ # ------------------------------------------------------------
188
+ #
189
+ ###############################################################
190
+ # Process via fork - with linked asynchronous traces
191
+ ###############################################################
192
+
193
+ AppOpticsAPM::API.start_trace('parent_process') do
194
+ # Get some work to process
195
+ job = get_job
196
+
197
+ # fork process to handle work
198
+ fork do
199
+ # Since fork does a complete process copy, the tracing_context still exists
200
+ # although we'll have to mark these traces as asynchronous to denote
201
+ # that it has split off from the main program flow
202
+
203
+ AppOpticsAPM::API.trace('worker_process', :Async => 1) do
204
+ do_work(job)
205
+ end
206
+ end
207
+ end
208
+
209
+ ###############################################################
210
+ #
211
+ # This will generate a single trace with an asynchronous
212
+ # branch like the following
213
+ #
214
+ # 'parent_process'
215
+ # ------------------------------------------------------------
216
+ # \
217
+ # \
218
+ # ------------------------------------------------------
219
+ # 'worker_process'
220
+ #
@@ -0,0 +1,8 @@
1
+ class MetalController < ActionController::Metal
2
+ def index
3
+ self.response_body = 'Hello Metal!'
4
+ end
5
+
6
+ include AppOpticsAPMMethodProfiling
7
+ profile_method :index, 'metal-index'
8
+ end
@@ -0,0 +1,17 @@
1
+ workers Integer(ENV['WEB_CONCURRENCY'] || 2)
2
+ threads_count = Integer(ENV['MAX_THREADS'] || 5)
3
+ threads threads_count, threads_count
4
+
5
+ preload_app!
6
+
7
+ rackup DefaultRackup
8
+ port ENV['PORT'] || 3000
9
+ environment ENV['RACK_ENV'] || 'development'
10
+
11
+ on_worker_boot do
12
+ ::AppOpticsAPM.reconnect! if defined?(::AppOpticsAPM)
13
+ end
14
+
15
+ on_worker_shutdown do
16
+ ::AppOpticsAPM.disconnect! if defined?(::AppOpticsAPM)
17
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # This sample demonstrates how to instrument a main loop that
3
+ # retrieves work and spawn threads that do the actual work
4
+ #
5
+
6
+ require 'math'
7
+ require 'oboe'
8
+
9
+ AppOpticsAPM::Config[:tracing_mode] = :always
10
+ AppOpticsAPM::Config[:verbose] = true
11
+
12
+ # The parent process/loop which collects data
13
+ Kernel.loop do
14
+
15
+ # For each loop, we instrument the work retrieval. These traces
16
+ # will show up as layer 'get_the_work'.
17
+ AppOpticsAPM::API.start_trace('get_the_work') do
18
+ work = get_the_work
19
+
20
+ # Loop through work and pass to `do_the_work` method
21
+ # that spawns a thread each time
22
+ work.each do |j|
23
+
24
+ # In the new Thread block, the AppOpticsAPM tracing context isn't there
25
+ # so we carry it over manually and pass it to the `start_trace`
26
+ # method.
27
+
28
+ # In the AppOptics dashboard, this will show up as parent traces
29
+ # (layer 'get_the_work') with child traces (layer 'do_the_work').
30
+
31
+ tracing_context = AppOpticsAPM::Context.toString
32
+
33
+ Thread.new do
34
+ result = nil
35
+
36
+ AppOpticsAPM::API.start_trace('do_the_work', tracing_context, :Async => 1) do
37
+ result = do_the_work(j)
38
+ end
39
+
40
+ result
41
+ end
42
+ end
43
+ end
44
+ sleep 5
45
+ end
46
+
47
+
48
+ ##
49
+ # get_the_work
50
+ #
51
+ # Method to retrieve work to do
52
+ #
53
+ def get_the_work
54
+ # We'll just return random integers as a
55
+ # fake work load
56
+ w = []
57
+ w << rand(25)
58
+ w << rand(25)
59
+ w << rand(25)
60
+ end
61
+
62
+ ##
63
+ # do_the_work
64
+ #
65
+ # The work-horse method
66
+ #
67
+ def do_the_work(job_to_do)
68
+ i = job_to_do
69
+ i * Math::PI
70
+ end
71
+
72
+ ####################################################
73
+ # Notes
74
+ ####################################################
75
+
76
+ # The above code generates a trace for each loop of the parent data collection process.
77
+ # Those traces have the layer name of `get_the_work` and will show up in the AppOptics
78
+ # dashboard as such.
79
+ #
80
+ # Then as threads are spawned to process individual bits of work, we carry over the
81
+ # `tracing_context` and start a new asynchronous trace using `start_trace`. (An
82
+ # asynchronous trace is noted by passing the `Async` Hash key with a value of `1`).
83
+ #
84
+ # In the AppOptics dashboard, the two traces (parent and child; or one to many) will
85
+ # be linked and displayed together as a single trace.
86
+
87
+ ####################################################
88
+ # Caveats
89
+ ####################################################
90
+
91
+ # If the main loop is retrieving many jobs (work) to process on each loop then
92
+ # linking the traces may not be the best strategy as such large relationships
93
+ # are difficult to display correctly in the AppOptics dashboard and provide little
94
+ # added value.
95
+ #
96
+ # If there are more than 8 - 12 threads spawned from each loop, then you may want to consider
97
+ # NOT carrying over tracing context into the spawned threads.
98
+ #
99
+ # In this case, you can simply omit `tracing_context` and passing it to `start_trace` in
100
+ # the `Thread.new` block. (lines 32 + 37). Also remove the `{ Async => 1 }` Hash!
101
+ #
102
+ # This will produce two sets of traces with two the layer names 'get_the_work' +
103
+ # 'do_the_work'.
104
+ #
105
+ # In the AppOptics dashboard, you can then separate or unify these traces into
106
+ # independent applications. e.g. job processor, data retrieval, thread worker etc...
107
+ #
108
+ # An implementation of the work loop without carrying over tracing context would look
109
+ # like the following:
110
+ #
111
+ # work.each do |j|
112
+ # Thread.new do
113
+ # result = nil
114
+ #
115
+ # AppOpticsAPM::API.start_trace('do_the_work') do
116
+ # result = do_the_work(j)
117
+ # end
118
+ #
119
+ # result
120
+ # end
121
+ # end
122
+ #
123
+ # If anything isn't clear, please don't hesitate to reach us at support (support@appoptics.com).
124
+ #
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require
5
+
6
+ # Make sure oboe is at the bottom of your Gemfile.
7
+ # This is likely redundant but just in case.
8
+ require 'oboe'
9
+
10
+ # Tracing mode can be 'never' or 'always'
11
+ AppOpticsAPM::Config[:tracing_mode] = 'always'
12
+
13
+ #
14
+ # Update April 9, 2015 - this is done automagically now
15
+ # and doesn't have to be called manually
16
+ #
17
+ # Load library instrumentation to auto-capture stuff we know about...
18
+ # e.g. ActiveRecord, Cassandra, Dalli, Redis, memcache, mongo
19
+ # AppOpticsAPM::Ruby.load
20
+
21
+ # Some KVs to report to the dashboard
22
+ report_kvs = {}
23
+ report_kvs[:command_line_params] = ARGV.to_s
24
+ report_kvs[:user_id] = `whoami`
25
+
26
+ AppOpticsAPM::API.start_trace('my_background_job', nil, report_kvs) do
27
+ #
28
+ # Initialization code
29
+ #
30
+
31
+ tasks = get_all_tasks
32
+
33
+ tasks.each do |t|
34
+ # Optional: Here we embed another 'trace' to separate actual
35
+ # work for each task. In the APPOPTICS dashboard, this will show
36
+ # up as a large 'my_background_job' parent layer with many
37
+ # child 'task" layers.
38
+ AppOpticsAPM::API.trace('task', :task_id => t.id) do
39
+ t.perform
40
+ end
41
+ end
42
+ #
43
+ # cleanup code
44
+ #
45
+ end
46
+
47
+ # Note that we use 'start_trace' in the outer block and 'trace' for
48
+ # any sub-blocks of code we wish to instrument. The arguments for
49
+ # both methods vary slightly.
50
+ #
51
+ # TODO update location of the following doc
52
+ # Details in RubyDoc:
53
+ # https://www.omniref.com/ruby/gems/oboe/2.7.10.1/symbols/AppOpticsAPM::API::Tracing#tab=Methods
@@ -0,0 +1,99 @@
1
+ #
2
+ # This sample demonstrates how to instrument a main loop that
3
+ # retrieves work and calls fork to do the actual work
4
+ #
5
+
6
+ require 'math'
7
+ require 'oboe'
8
+
9
+ AppOpticsAPM::Config[:tracing_mode] = :always
10
+ AppOpticsAPM::Config[:verbose] = true
11
+
12
+ # The parent process/loop which collects data
13
+ Kernel.loop do
14
+ # For each loop, we instrument the work retrieval. These traces
15
+ # will show up as layer 'get_the_work'.
16
+ AppOpticsAPM::API.start_trace('get_the_work') do
17
+ work = get_the_work
18
+
19
+ # Loop through work and pass to `do_the_work` method
20
+ # that spawns a thread each time
21
+ work.each do |job|
22
+ fork do
23
+ # Since the context is copied from the parent process, we clear it
24
+ # and start a new trace via `AppOpticsAPM::API.start_trace`.
25
+ AppOpticsAPM::Context.clear
26
+ result = nil
27
+
28
+ AppOpticsAPM::API.start_trace('do_the_work', nil, :job_id => job.id) do
29
+ result = do_the_work(job)
30
+ end
31
+
32
+ result
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ ##
39
+ # get_the_work
40
+ #
41
+ # Method to retrieve work to do
42
+ #
43
+ def get_the_work
44
+ # We'll just return random integers as a
45
+ # fake work load
46
+ w = []
47
+ w << rand(25)
48
+ w << rand(25)
49
+ w << rand(25)
50
+ end
51
+
52
+ ##
53
+ # do_the_work
54
+ #
55
+ # The work-horse method
56
+ #
57
+ def do_the_work(job_to_do)
58
+ i = job_to_do
59
+ i * Math::PI
60
+ end
61
+
62
+ #########################################################################
63
+ # Notes
64
+ #########################################################################
65
+
66
+ # If your parent process only forks a small number of processes per loop (< 5..10),
67
+ # you may want to mark the child traces as asynchronous and have them directly
68
+ # linked to the parent tracing context.
69
+ #
70
+ # The benefit of this is that instead of having two independent traces (parent
71
+ # and child), you will have a single view of the parent trace showing the
72
+ # spawned child process and it's performance in the AppOptics dashboard.
73
+ #
74
+ # To do this:
75
+ # 1. Don't clear the context in the child process
76
+ # 2. Use `AppOpticsAPM::API.trace` instead
77
+ # 3. Pass the `Async` flag to mark this child as asynchronous
78
+ #
79
+ Kernel.loop do
80
+ AppOpticsAPM::API.start_trace('get_the_work') do
81
+
82
+ work = get_the_work
83
+
84
+ work.each do |job|
85
+ fork do
86
+ result = nil
87
+ # 1 Don't clear context
88
+ # 2 Use `AppOpticsAPM::API.trace` instead
89
+ # 3 Pass the Async flag
90
+ AppOpticsAPM::API.trace('do_the_work', {:job_id => job.id, :Async => 1 }) do
91
+ result = do_the_work(job)
92
+ end
93
+
94
+ result
95
+ end
96
+ end
97
+ end
98
+ sleep 5
99
+ end
@@ -0,0 +1,28 @@
1
+ worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
2
+ timeout 15
3
+ preload_app true
4
+
5
+ before_fork do |server, worker|
6
+ Signal.trap 'TERM' do
7
+ puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
8
+ Process.kill 'QUIT', Process.pid
9
+ end
10
+
11
+ defined?(ActiveRecord::Base) and
12
+ ActiveRecord::Base.connection.disconnect!
13
+
14
+ defined?(::AppOpticsAPM) and
15
+ ::AppOpticsAPM.disconnect!
16
+ end
17
+
18
+ after_fork do |server, worker|
19
+ Signal.trap 'TERM' do
20
+ puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
21
+ end
22
+
23
+ defined?(ActiveRecord::Base) and
24
+ ActiveRecord::Base.establish_connection
25
+
26
+ defined?(::AppOpticsAPM) and
27
+ ::AppOpticsAPM.reconnect!
28
+ end