appoptics_apm 4.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +43 -0
- data/.dockerignore +5 -0
- data/.gitignore +23 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +82 -0
- data/CHANGELOG.md +769 -0
- data/CONFIG.md +33 -0
- data/Dockerfile +41 -0
- data/Dockerfile_test +66 -0
- data/Gemfile +41 -0
- data/LICENSE +193 -0
- data/README.md +351 -0
- data/Rakefile +202 -0
- data/Vagrantfile +67 -0
- data/appoptics_apm.gemspec +55 -0
- data/build_gems.sh +15 -0
- data/docker-compose.yml +73 -0
- data/examples/DNT.md +35 -0
- data/examples/carrying_context.rb +220 -0
- data/examples/instrumenting_metal_controller.rb +8 -0
- data/examples/puma_on_heroku_config.rb +17 -0
- data/examples/tracing_async_threads.rb +124 -0
- data/examples/tracing_background_jobs.rb +53 -0
- data/examples/tracing_forked_processes.rb +99 -0
- data/examples/unicorn_on_heroku_config.rb +28 -0
- data/ext/oboe_metal/extconf.rb +54 -0
- data/ext/oboe_metal/lib/.keep +0 -0
- data/ext/oboe_metal/lib/liboboe-1.0.so.0.0.0 +0 -0
- data/ext/oboe_metal/noop/noop.c +7 -0
- data/ext/oboe_metal/src/VERSION +1 -0
- data/ext/oboe_metal/src/bson/bson.h +221 -0
- data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
- data/ext/oboe_metal/src/oboe.h +883 -0
- data/ext/oboe_metal/src/oboe.hpp +793 -0
- data/ext/oboe_metal/src/oboe_debug.h +50 -0
- data/ext/oboe_metal/src/oboe_wrap.cxx +6088 -0
- data/ext/oboe_metal/tests/test.rb +11 -0
- data/gemfiles/delayed_job.gemfile +36 -0
- data/gemfiles/frameworks.gemfile +44 -0
- data/gemfiles/instrumentation_mocked.gemfile +29 -0
- data/gemfiles/libraries.gemfile +85 -0
- data/gemfiles/rails23.gemfile +39 -0
- data/gemfiles/rails30.gemfile +42 -0
- data/gemfiles/rails31.gemfile +44 -0
- data/gemfiles/rails32.gemfile +54 -0
- data/gemfiles/rails40.gemfile +27 -0
- data/gemfiles/rails41.gemfile +27 -0
- data/gemfiles/rails42.gemfile +35 -0
- data/gemfiles/rails50.gemfile +44 -0
- data/gemfiles/rails51.gemfile +44 -0
- data/get_version.rb +5 -0
- data/init.rb +4 -0
- data/lib/appoptics_apm/api/layerinit.rb +39 -0
- data/lib/appoptics_apm/api/logging.rb +359 -0
- data/lib/appoptics_apm/api/memcache.rb +34 -0
- data/lib/appoptics_apm/api/profiling.rb +201 -0
- data/lib/appoptics_apm/api/tracing.rb +152 -0
- data/lib/appoptics_apm/api/util.rb +128 -0
- data/lib/appoptics_apm/api.rb +18 -0
- data/lib/appoptics_apm/base.rb +252 -0
- data/lib/appoptics_apm/config.rb +281 -0
- data/lib/appoptics_apm/frameworks/grape.rb +93 -0
- data/lib/appoptics_apm/frameworks/padrino/templates.rb +58 -0
- data/lib/appoptics_apm/frameworks/padrino.rb +52 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +106 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller2.rb +61 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +58 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +58 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_view_2x.rb +56 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +50 -0
- data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +27 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +28 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +30 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +120 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +101 -0
- data/lib/appoptics_apm/frameworks/rails.rb +116 -0
- data/lib/appoptics_apm/frameworks/sinatra/templates.rb +56 -0
- data/lib/appoptics_apm/frameworks/sinatra.rb +71 -0
- data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
- data/lib/appoptics_apm/inst/bunny-consumer.rb +92 -0
- data/lib/appoptics_apm/inst/curb.rb +329 -0
- data/lib/appoptics_apm/inst/dalli.rb +85 -0
- data/lib/appoptics_apm/inst/delayed_job.rb +92 -0
- data/lib/appoptics_apm/inst/em-http-request.rb +105 -0
- data/lib/appoptics_apm/inst/excon.rb +130 -0
- data/lib/appoptics_apm/inst/faraday.rb +77 -0
- data/lib/appoptics_apm/inst/http.rb +83 -0
- data/lib/appoptics_apm/inst/httpclient.rb +176 -0
- data/lib/appoptics_apm/inst/memcache.rb +102 -0
- data/lib/appoptics_apm/inst/memcached.rb +94 -0
- data/lib/appoptics_apm/inst/mongo.rb +242 -0
- data/lib/appoptics_apm/inst/mongo2.rb +225 -0
- data/lib/appoptics_apm/inst/moped.rb +466 -0
- data/lib/appoptics_apm/inst/rack.rb +146 -0
- data/lib/appoptics_apm/inst/redis.rb +275 -0
- data/lib/appoptics_apm/inst/resque.rb +151 -0
- data/lib/appoptics_apm/inst/rest-client.rb +50 -0
- data/lib/appoptics_apm/inst/sequel.rb +178 -0
- data/lib/appoptics_apm/inst/sidekiq-client.rb +53 -0
- data/lib/appoptics_apm/inst/sidekiq-worker.rb +67 -0
- data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
- data/lib/appoptics_apm/inst/typhoeus.rb +113 -0
- data/lib/appoptics_apm/instrumentation.rb +22 -0
- data/lib/appoptics_apm/legacy_method_profiling.rb +97 -0
- data/lib/appoptics_apm/loading.rb +66 -0
- data/lib/appoptics_apm/logger.rb +41 -0
- data/lib/appoptics_apm/method_profiling.rb +33 -0
- data/lib/appoptics_apm/ruby.rb +35 -0
- data/lib/appoptics_apm/support.rb +135 -0
- data/lib/appoptics_apm/test.rb +94 -0
- data/lib/appoptics_apm/thread_local.rb +26 -0
- data/lib/appoptics_apm/util.rb +312 -0
- data/lib/appoptics_apm/version.rb +15 -0
- data/lib/appoptics_apm/xtrace.rb +103 -0
- data/lib/appoptics_apm.rb +72 -0
- data/lib/joboe_metal.rb +214 -0
- data/lib/oboe/README +2 -0
- data/lib/oboe/backward_compatibility.rb +80 -0
- data/lib/oboe/inst/rack.rb +11 -0
- data/lib/oboe.rb +7 -0
- data/lib/oboe_metal.rb +187 -0
- data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
- data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +222 -0
- data/ruby_setup.sh +47 -0
- data/run_docker_build_gem_upload_to_packagecloud.sh +20 -0
- data/run_tests_docker.rb +32 -0
- data/test/benchmark/README.md +65 -0
- data/test/benchmark/logging_bench.rb +54 -0
- data/test/benchmark/with_libraries_gemfile/bunny_bench.rb +69 -0
- data/test/benchmark/with_rails5x_gemfile/action_controller5x_bench.rb +43 -0
- data/test/frameworks/apps/grape_nested.rb +33 -0
- data/test/frameworks/apps/grape_simple.rb +80 -0
- data/test/frameworks/apps/padrino_simple.rb +80 -0
- data/test/frameworks/apps/sinatra_simple.rb +55 -0
- data/test/frameworks/grape_test.rb +286 -0
- data/test/frameworks/padrino_test.rb +222 -0
- data/test/frameworks/rails3x_test.rb +554 -0
- data/test/frameworks/rails4x_test.rb +570 -0
- data/test/frameworks/rails5x_api_test.rb +210 -0
- data/test/frameworks/rails5x_test.rb +376 -0
- data/test/frameworks/rails_shared_tests.rb +172 -0
- data/test/frameworks/sinatra_test.rb +140 -0
- data/test/instrumentation/bunny_client_test.rb +276 -0
- data/test/instrumentation/bunny_consumer_test.rb +204 -0
- data/test/instrumentation/curb_test.rb +398 -0
- data/test/instrumentation/dalli_test.rb +177 -0
- data/test/instrumentation/em_http_request_test.rb +89 -0
- data/test/instrumentation/excon_test.rb +231 -0
- data/test/instrumentation/faraday_test.rb +228 -0
- data/test/instrumentation/http_test.rb +143 -0
- data/test/instrumentation/httpclient_test.rb +320 -0
- data/test/instrumentation/memcache_test.rb +260 -0
- data/test/instrumentation/memcached_test.rb +229 -0
- data/test/instrumentation/mongo_v1_test.rb +479 -0
- data/test/instrumentation/mongo_v2_index_test.rb +124 -0
- data/test/instrumentation/mongo_v2_test.rb +584 -0
- data/test/instrumentation/mongo_v2_view_test.rb +435 -0
- data/test/instrumentation/moped_test.rb +517 -0
- data/test/instrumentation/rack_test.rb +165 -0
- data/test/instrumentation/redis_hashes_test.rb +268 -0
- data/test/instrumentation/redis_keys_test.rb +321 -0
- data/test/instrumentation/redis_lists_test.rb +310 -0
- data/test/instrumentation/redis_misc_test.rb +163 -0
- data/test/instrumentation/redis_sets_test.rb +296 -0
- data/test/instrumentation/redis_sortedsets_test.rb +328 -0
- data/test/instrumentation/redis_strings_test.rb +349 -0
- data/test/instrumentation/resque_test.rb +185 -0
- data/test/instrumentation/rest-client_test.rb +288 -0
- data/test/instrumentation/sequel_mysql2_test.rb +353 -0
- data/test/instrumentation/sequel_mysql_test.rb +334 -0
- data/test/instrumentation/sequel_pg_test.rb +336 -0
- data/test/instrumentation/sidekiq-client_test.rb +159 -0
- data/test/instrumentation/sidekiq-worker_test.rb +180 -0
- data/test/instrumentation/twitter-cassandra_test.rb +424 -0
- data/test/instrumentation/typhoeus_test.rb +284 -0
- data/test/jobs/delayed_job/db_worker_job.rb +29 -0
- data/test/jobs/delayed_job/error_worker_job.rb +10 -0
- data/test/jobs/delayed_job/remote_call_worker_job.rb +20 -0
- data/test/jobs/resque/db_worker_job.rb +29 -0
- data/test/jobs/resque/error_worker_job.rb +10 -0
- data/test/jobs/resque/remote_call_worker_job.rb +20 -0
- data/test/jobs/sidekiq/db_worker_job.rb +29 -0
- data/test/jobs/sidekiq/error_worker_job.rb +10 -0
- data/test/jobs/sidekiq/remote_call_worker_job.rb +20 -0
- data/test/minitest_helper.rb +276 -0
- data/test/mocked/curb_mocked_test.rb +311 -0
- data/test/mocked/excon_mocked_test.rb +166 -0
- data/test/mocked/faraday_mocked_test.rb +93 -0
- data/test/mocked/http_mocked_test.rb +129 -0
- data/test/mocked/httpclient_mocked_test.rb +245 -0
- data/test/mocked/rest_client_mocked_test.rb +103 -0
- data/test/mocked/typhoeus_mocked_test.rb +192 -0
- data/test/models/widget.rb +36 -0
- data/test/profiling/legacy_method_profiling_test.rb +201 -0
- data/test/profiling/method_profiling_test.rb +631 -0
- data/test/queues/delayed_job-client_test.rb +95 -0
- data/test/queues/delayed_job-worker_test.rb +91 -0
- data/test/reporter/reporter_test.rb +14 -0
- data/test/servers/delayed_job.rb +107 -0
- data/test/servers/rackapp_8101.rb +29 -0
- data/test/servers/rails3x_8140.rb +96 -0
- data/test/servers/rails4x_8140.rb +96 -0
- data/test/servers/rails5x_8140.rb +95 -0
- data/test/servers/rails5x_api_8150.rb +78 -0
- data/test/servers/sidekiq.rb +29 -0
- data/test/servers/sidekiq.yml +7 -0
- data/test/servers/sidekiq_initializer.rb +25 -0
- data/test/settings +0 -0
- data/test/support/auto_tracing_test.rb +50 -0
- data/test/support/backcompat_test.rb +276 -0
- data/test/support/config_test.rb +149 -0
- data/test/support/dnt_test.rb +98 -0
- data/test/support/init_report_test.rb +25 -0
- data/test/support/liboboe_settings_test.rb +110 -0
- data/test/support/logging_test.rb +130 -0
- data/test/support/noop_test.rb +88 -0
- data/test/support/sql_sanitize_test.rb +55 -0
- data/test/support/tracing_mode_test.rb +33 -0
- data/test/support/tvalias_test.rb +15 -0
- data/test/support/xtrace_test.rb +41 -0
- 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
|