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