appoptics_apm_mnfst 4.5.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/.dockerignore +5 -0
- data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
- data/.gitignore +29 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +121 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +769 -0
- data/CONFIG.md +33 -0
- data/Gemfile +29 -0
- data/LICENSE +193 -0
- data/README.md +393 -0
- data/Rakefile +230 -0
- data/appoptics_apm.gemspec +61 -0
- data/bin/appoptics_apm_config +15 -0
- data/build_gem.sh +15 -0
- data/build_gem_upload_to_packagecloud.sh +20 -0
- data/examples/SDK/01_basic_tracing.rb +67 -0
- data/examples/carrying_context.rb +220 -0
- data/ext/oboe_metal/extconf.rb +114 -0
- data/ext/oboe_metal/lib/.keep +0 -0
- data/ext/oboe_metal/noop/noop.c +7 -0
- data/ext/oboe_metal/src/VERSION +1 -0
- data/init.rb +4 -0
- data/lib/appoptics_apm.rb +76 -0
- data/lib/appoptics_apm/api.rb +20 -0
- data/lib/appoptics_apm/api/layerinit.rb +41 -0
- data/lib/appoptics_apm/api/logging.rb +375 -0
- data/lib/appoptics_apm/api/memcache.rb +37 -0
- data/lib/appoptics_apm/api/metrics.rb +55 -0
- data/lib/appoptics_apm/api/profiling.rb +203 -0
- data/lib/appoptics_apm/api/tracing.rb +53 -0
- data/lib/appoptics_apm/api/util.rb +122 -0
- data/lib/appoptics_apm/base.rb +230 -0
- data/lib/appoptics_apm/config.rb +254 -0
- data/lib/appoptics_apm/frameworks/grape.rb +97 -0
- data/lib/appoptics_apm/frameworks/padrino.rb +108 -0
- data/lib/appoptics_apm/frameworks/rails.rb +94 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +104 -0
- data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +55 -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_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 +29 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +31 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +119 -0
- data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +108 -0
- data/lib/appoptics_apm/frameworks/sinatra.rb +125 -0
- data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
- data/lib/appoptics_apm/inst/bunny-consumer.rb +89 -0
- data/lib/appoptics_apm/inst/curb.rb +330 -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 +101 -0
- data/lib/appoptics_apm/inst/excon.rb +125 -0
- data/lib/appoptics_apm/inst/faraday.rb +94 -0
- data/lib/appoptics_apm/inst/grpc_client.rb +162 -0
- data/lib/appoptics_apm/inst/grpc_server.rb +120 -0
- data/lib/appoptics_apm/inst/http.rb +73 -0
- data/lib/appoptics_apm/inst/httpclient.rb +174 -0
- data/lib/appoptics_apm/inst/memcached.rb +86 -0
- data/lib/appoptics_apm/inst/mongo.rb +246 -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 +199 -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 +48 -0
- data/lib/appoptics_apm/inst/sequel.rb +178 -0
- data/lib/appoptics_apm/inst/sidekiq-client.rb +55 -0
- data/lib/appoptics_apm/inst/sidekiq-worker.rb +65 -0
- data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
- data/lib/appoptics_apm/inst/typhoeus.rb +108 -0
- data/lib/appoptics_apm/instrumentation.rb +22 -0
- data/lib/appoptics_apm/legacy_method_profiling.rb +90 -0
- data/lib/appoptics_apm/loading.rb +65 -0
- data/lib/appoptics_apm/logger.rb +42 -0
- data/lib/appoptics_apm/method_profiling.rb +33 -0
- data/lib/appoptics_apm/noop/README.md +9 -0
- data/lib/appoptics_apm/noop/context.rb +26 -0
- data/lib/appoptics_apm/noop/metadata.rb +22 -0
- data/lib/appoptics_apm/ruby.rb +35 -0
- data/lib/appoptics_apm/sdk/custom_metrics.rb +92 -0
- data/lib/appoptics_apm/sdk/tracing.rb +315 -0
- data/lib/appoptics_apm/support.rb +119 -0
- data/lib/appoptics_apm/test.rb +94 -0
- data/lib/appoptics_apm/thread_local.rb +26 -0
- data/lib/appoptics_apm/util.rb +319 -0
- data/lib/appoptics_apm/version.rb +15 -0
- data/lib/appoptics_apm/xtrace.rb +103 -0
- data/lib/joboe_metal.rb +212 -0
- data/lib/oboe.rb +7 -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_metal.rb +198 -0
- data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
- data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +265 -0
- data/yardoc_frontpage.md +26 -0
- metadata +266 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'digest/sha1'
|
|
5
|
+
|
|
6
|
+
module AppOpticsAPM
|
|
7
|
+
module Util
|
|
8
|
+
##
|
|
9
|
+
# This module was used solely for the deprecated RUM ID calculation
|
|
10
|
+
# but may be useful in the future.
|
|
11
|
+
#
|
|
12
|
+
module Base64URL
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def encode(bin)
|
|
16
|
+
c = [bin].pack('m0').gsub(/\=+\Z/, '').tr('+/', '-_').rstrip
|
|
17
|
+
m = c.size % 4
|
|
18
|
+
c += '=' * (4 - m) if m != 0
|
|
19
|
+
c
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def decode(bin)
|
|
23
|
+
m = bin.size % 4
|
|
24
|
+
bin += '=' * (4 - m) if m != 0
|
|
25
|
+
bin.tr('-_', '+/').unpack('m0').first
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# This module houses all of the loading functionality for the appoptics_apm em.
|
|
32
|
+
|
|
33
|
+
# Note that this does not necessarily _have_ to include initialization routines
|
|
34
|
+
# (although it can).
|
|
35
|
+
#
|
|
36
|
+
# Actual initialization is often separated out as it can be dependent on on the state
|
|
37
|
+
# of the stack boot process. e.g. code requiring that initializers, frameworks or
|
|
38
|
+
# instrumented libraries are already loaded...
|
|
39
|
+
#
|
|
40
|
+
module Loading
|
|
41
|
+
##
|
|
42
|
+
# Load the appoptics_apm tracing API
|
|
43
|
+
#
|
|
44
|
+
def self.require_api
|
|
45
|
+
pattern = File.join(File.dirname(__FILE__), 'api', '*.rb')
|
|
46
|
+
Dir.glob(pattern) do |f|
|
|
47
|
+
require f
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
require 'appoptics_apm/api'
|
|
52
|
+
rescue LoadError => e
|
|
53
|
+
AppOpticsAPM.logger.fatal "[appoptics_apm/error] Couldn't load api: #{e.message}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
AppOpticsAPM::Loading.require_api
|
|
60
|
+
|
|
61
|
+
# Auto-start the Reporter unless we are running Unicorn on Heroku
|
|
62
|
+
# In that case, we start the reporters after fork
|
|
63
|
+
unless AppOpticsAPM.heroku? && AppOpticsAPM.forking_webserver?
|
|
64
|
+
AppOpticsAPM::Reporter.start if AppOpticsAPM.loaded
|
|
65
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
require 'logger'
|
|
5
|
+
|
|
6
|
+
module AppOpticsAPM
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :logger
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Logger
|
|
12
|
+
# Fatal message
|
|
13
|
+
def fatal(string, exception = nil)
|
|
14
|
+
AppOpticsAPM.logger.fatal(string) if AppOpticsAPM.logger
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Error message
|
|
18
|
+
def error(msg, exception = nil)
|
|
19
|
+
AppOpticsAPM.logger.error(string) if AppOpticsAPM.logger
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Warn message
|
|
23
|
+
def warn(msg, exception = nil)
|
|
24
|
+
AppOpticsAPM.logger.warn(string) if AppOpticsAPM.logger
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Info message
|
|
28
|
+
def info(msg, exception = nil)
|
|
29
|
+
AppOpticsAPM.logger.info(string) if AppOpticsAPM.logger
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Debug message
|
|
33
|
+
def debug(msg, exception = nil)
|
|
34
|
+
AppOpticsAPM.logger.debug(string) if AppOpticsAPM.logger
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
AppOpticsAPM.logger = Logger.new(STDERR)
|
|
41
|
+
# set log level to INFO to be consistent with the c-lib, DEBUG would be default
|
|
42
|
+
AppOpticsAPM.logger.level = Logger::INFO
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
module AppOpticsAPM
|
|
3
|
+
module MethodProfiling
|
|
4
|
+
def profile_wrapper(method, report_kvs, opts, *args, &block)
|
|
5
|
+
report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace(2) if opts[:backtrace]
|
|
6
|
+
report_kvs[:Arguments] = args if opts[:arguments]
|
|
7
|
+
|
|
8
|
+
# if this is a rails controller we want to set the transaction for the outbound metrics
|
|
9
|
+
if defined?(request) && defined?(request.env)
|
|
10
|
+
report_kvs['Controller'] = self.class.name
|
|
11
|
+
report_kvs['Action'] = self.action_name
|
|
12
|
+
request.env['appoptics_apm.controller'] = report_kvs['Controller']
|
|
13
|
+
request.env['appoptics_apm.action'] = report_kvs['Action']
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
AppOpticsAPM::API.log(nil, :profile_entry, report_kvs)
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
rv = self.send(method, *args, &block)
|
|
20
|
+
report_kvs[:ReturnValue] = rv if opts[:result]
|
|
21
|
+
rv
|
|
22
|
+
rescue => e
|
|
23
|
+
AppOpticsAPM::API.log_exception(nil, e)
|
|
24
|
+
raise
|
|
25
|
+
ensure
|
|
26
|
+
report_kvs.delete(:Backtrace)
|
|
27
|
+
report_kvs.delete(:Controller)
|
|
28
|
+
report_kvs.delete(:Action)
|
|
29
|
+
AppOpticsAPM::API.log(nil, :profile_exit, report_kvs)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Here we can define modules and classes for noop mode.
|
|
2
|
+
|
|
3
|
+
Instead of polluting code with AppOpticsAPM.loaded conditionals
|
|
4
|
+
|
|
5
|
+
we load these classes when in noop mode and they expose noop behavior.
|
|
6
|
+
|
|
7
|
+
so far only one class is needed:
|
|
8
|
+
|
|
9
|
+
- AppOpticsAPM::Context and its toString() method from oboe
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
####
|
|
2
|
+
# noop version of AppOpticsAPM::Context
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
module AppOpticsAPM
|
|
7
|
+
module Context
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# noop version of :toString
|
|
11
|
+
# toString would return the current context (xtrace) as string
|
|
12
|
+
#
|
|
13
|
+
# the noop version returns an empty string
|
|
14
|
+
#
|
|
15
|
+
def self.toString
|
|
16
|
+
''
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# noop version of :clear
|
|
21
|
+
#
|
|
22
|
+
def self.clear
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
####
|
|
2
|
+
# noop version of AppOpticsAPM::Metadata
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
module AppOpticsAPM
|
|
7
|
+
class Metadata
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# noop version of :makeRandom
|
|
11
|
+
#
|
|
12
|
+
# needs to return an object that responds to :isValid
|
|
13
|
+
#
|
|
14
|
+
def self.makeRandom
|
|
15
|
+
Metadata.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def isValid
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module AppOpticsAPM
|
|
5
|
+
##
|
|
6
|
+
# This module provides a method to manually initialize the
|
|
7
|
+
# Ruby instrumentation. Normally this is done by detecting
|
|
8
|
+
# frameworks at load time and inserting initialization hooks.
|
|
9
|
+
module Ruby
|
|
10
|
+
class << self
|
|
11
|
+
def initialize
|
|
12
|
+
load
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# The core method to load Ruby instrumentation. Call this
|
|
17
|
+
# from raw Ruby scripts or in Ruby applications where a
|
|
18
|
+
# supported framework isn't being used. Supported frameworks
|
|
19
|
+
# will instead be detected at load time and initialization is
|
|
20
|
+
# automatic.
|
|
21
|
+
def load
|
|
22
|
+
# In case some apps call this manually, make sure
|
|
23
|
+
# that the gem is fully loaded and not in no-op
|
|
24
|
+
# mode (e.g. on unsupported platforms etc.)
|
|
25
|
+
if AppOpticsAPM.loaded
|
|
26
|
+
AppOpticsAPM::Inst.load_instrumentation
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if AppOpticsAPM.loaded && !AppOpticsAPM.framework?
|
|
34
|
+
AppOpticsAPM::Ruby.load
|
|
35
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#++
|
|
5
|
+
|
|
6
|
+
module AppOpticsAPM
|
|
7
|
+
module SDK
|
|
8
|
+
|
|
9
|
+
module CustomMetrics
|
|
10
|
+
|
|
11
|
+
# Send counts
|
|
12
|
+
#
|
|
13
|
+
# Use this method to report the number of times an action occurs. The metric counts reported are summed and flushed every 60 seconds.
|
|
14
|
+
#
|
|
15
|
+
# === Arguments:
|
|
16
|
+
#
|
|
17
|
+
# * +name+ (String) Name to be used for the metric. Must be 255 or fewer characters and consist only of A-Za-z0-9.:-*
|
|
18
|
+
# * +count+ (Integer, optional, default = 1): Count of actions being reported
|
|
19
|
+
# * +with_hostname+ (Boolean, optional, default = false): Indicates if the host name should be included as a tag for the metric
|
|
20
|
+
# * +tags_kvs+ (Hash, optional): List of key/value pairs to describe the metric. The key must be <= 64 characters, the value must be <= 255 characters, allowed characters: A-Za-z0-9.:-_
|
|
21
|
+
#
|
|
22
|
+
# === Example:
|
|
23
|
+
#
|
|
24
|
+
# class WorkTracker
|
|
25
|
+
# def counting(name, tags = {})
|
|
26
|
+
# yield # yield to where work is done
|
|
27
|
+
# AppOpticsAPM::SDK.increment_metric(name, 1, false, tags)
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# === Returns:
|
|
32
|
+
# * true on success, false on failure
|
|
33
|
+
#
|
|
34
|
+
def increment_metric(name, count = 1, with_hostname = false, tags_kvs = {})
|
|
35
|
+
with_hostname = with_hostname ? 1 : 0
|
|
36
|
+
tags, tags_count = make_tags(tags_kvs)
|
|
37
|
+
AppOpticsAPM::CustomMetrics.increment(name.to_s, count, with_hostname, nil, tags, tags_count) == 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Send values with counts
|
|
41
|
+
#
|
|
42
|
+
# Use this method to report a value for each or multiple counts. The metric values reported are aggregated and flushed every 60 seconds. The dashboard displays the average value per count.
|
|
43
|
+
#
|
|
44
|
+
# === Arguments:
|
|
45
|
+
#
|
|
46
|
+
# * +name+ (String) Name to be used for the metric. Must be 255 or fewer characters and consist only of A-Za-z0-9.:-*
|
|
47
|
+
# * +value+ (Numeric) Value to be added to the current sum
|
|
48
|
+
# * +count+ (Integer, optional, default = 1): Count of actions being reported
|
|
49
|
+
# * +with_hostname+ (Boolean, optional, default = false): Indicates if the host name should be included as a tag for the metric
|
|
50
|
+
# * +tags_kvs+ (Hash, optional): List of key/value pairs to describe the metric. The key must be <= 64 characters, the value must be <= 255 characters, allowed characters: A-Za-z0-9.:-_
|
|
51
|
+
#
|
|
52
|
+
# === Example:
|
|
53
|
+
#
|
|
54
|
+
# class WorkTracker
|
|
55
|
+
# def timing(name, tags = {})
|
|
56
|
+
# start = Time.now
|
|
57
|
+
# yield # yield to where work is done
|
|
58
|
+
# duration = Time.now - start
|
|
59
|
+
# AppOpticsAPM::SDK.summary_metric(name, duration, 1, false, tags)
|
|
60
|
+
# end
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# === Returns:
|
|
64
|
+
# * true on success, false on failure
|
|
65
|
+
#
|
|
66
|
+
def summary_metric(name, value, count = 1, with_hostname = false, tags_kvs = {})
|
|
67
|
+
with_hostname = with_hostname ? 1 : 0
|
|
68
|
+
tags, tags_count = make_tags(tags_kvs)
|
|
69
|
+
AppOpticsAPM::CustomMetrics.summary(name.to_s, value, count, with_hostname, nil, tags, tags_count) == 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def make_tags(tags_kvs)
|
|
75
|
+
unless tags_kvs.is_a?(Hash)
|
|
76
|
+
AppOpticsAPM.logger.warn("[appoptics_apm/metrics] CustomMetrics received tags_kvs that are not a Hash (found #{tags_kvs.class}), setting tags_kvs = {}")
|
|
77
|
+
tags_kvs = {}
|
|
78
|
+
end
|
|
79
|
+
count = tags_kvs.size
|
|
80
|
+
tags = AppOpticsAPM::MetricTags.new(count)
|
|
81
|
+
|
|
82
|
+
tags_kvs.each_with_index do |(k, v), i|
|
|
83
|
+
tags.add(i, k.to_s, v.to_s)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
[tags, count]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
extend CustomMetrics
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#++
|
|
5
|
+
|
|
6
|
+
module AppOpticsAPM
|
|
7
|
+
module SDK
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# Traces are best created with an <tt>AppOpticsAPM::SDK.start_trace</tt> block and
|
|
11
|
+
# <tt>AppOpticsAPM::SDK.trace</tt> blocks around calls to be traced.
|
|
12
|
+
# These two methods guarantee proper nesting of traces, handling of the tracing context, as well as avoiding
|
|
13
|
+
# broken traces in case of exceptions.
|
|
14
|
+
#
|
|
15
|
+
# Some optional keys that can be used in the +opts+ hash:
|
|
16
|
+
# * +:Controller+
|
|
17
|
+
# * +:Action+
|
|
18
|
+
# * +:HTTP-Host+
|
|
19
|
+
# * +:URL+
|
|
20
|
+
# * +:Method+
|
|
21
|
+
#
|
|
22
|
+
# as well as custom keys. The information will show up in the raw data view of a span.
|
|
23
|
+
#
|
|
24
|
+
# Invalid keys: +:Label+, +:Layer+, +:Edge+, +:Timestamp+, +:Timestamp_u+, +:TransactionName+ (allowed in start_trace)
|
|
25
|
+
#
|
|
26
|
+
# The methods are exposed as singleton methods for AppOpticsAPM::SDK.
|
|
27
|
+
#
|
|
28
|
+
# === Usage:
|
|
29
|
+
# * +AppOpticsAPM::SDK.appoptics_ready?+
|
|
30
|
+
# * +AppOpticsAPM::SDK.get_transaction_name+
|
|
31
|
+
# * +AppOpticsAPM::SDK.set_transaction_name+
|
|
32
|
+
# * +AppOpticsAPM::SDK.start_trace+
|
|
33
|
+
# * +AppOpticsAPM::SDK.start_trace_with_target+
|
|
34
|
+
# * +AppOpticsAPM::SDK.trace+
|
|
35
|
+
# * +AppOpticsAPM::SDK.tracing?+
|
|
36
|
+
#
|
|
37
|
+
# === Example:
|
|
38
|
+
# class MonthlyCouponEmailJob
|
|
39
|
+
# def perform(*args)
|
|
40
|
+
#
|
|
41
|
+
# # KVs to report to the dashboard
|
|
42
|
+
# report_kvs = {}
|
|
43
|
+
# report_kvs[:Spec] = :job
|
|
44
|
+
# report_kvs[:Controller] = :MonthlyEmailJob
|
|
45
|
+
# report_kvs[:Action] = :CouponEmailer
|
|
46
|
+
#
|
|
47
|
+
# # Start tracing this job with start_trace
|
|
48
|
+
# AppOpticsAPM::SDK.start_trace('starling', nil, report_kvs) do
|
|
49
|
+
# monthly = MonthlyEmail.new(:CouponEmailer)
|
|
50
|
+
#
|
|
51
|
+
# # Trace a sub-component of this trace
|
|
52
|
+
# AppOpticsAPM::SDK.trace(self.class.name) do
|
|
53
|
+
#
|
|
54
|
+
# # The work to be done
|
|
55
|
+
# users = User.all
|
|
56
|
+
# users.each do |u|
|
|
57
|
+
# monthly.send(u.email)
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# end
|
|
61
|
+
# end
|
|
62
|
+
# end
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
module Tracing
|
|
66
|
+
|
|
67
|
+
# Trace a given block of code.
|
|
68
|
+
#
|
|
69
|
+
# Also detects any exceptions thrown by the block and report errors.
|
|
70
|
+
#
|
|
71
|
+
# === Arguments:
|
|
72
|
+
# * +:span+ - The span the block of code belongs to.
|
|
73
|
+
# * +:opts+ - (optional) A hash containing key/value pairs that will be reported along with the first event of this span.
|
|
74
|
+
# * +:protect_op+ - (optional) The operation being traced. Used to avoid double tracing operations that call each other.
|
|
75
|
+
#
|
|
76
|
+
# === Example:
|
|
77
|
+
#
|
|
78
|
+
# def computation_with_appoptics(n)
|
|
79
|
+
# AppOpticsAPM::SDK.trace('computation', { :number => n }, :computation) do
|
|
80
|
+
# return n if n == 0
|
|
81
|
+
# n + computation_with_appoptics(n-1)
|
|
82
|
+
# end
|
|
83
|
+
# end
|
|
84
|
+
#
|
|
85
|
+
# result = computation_with_appoptics(100)
|
|
86
|
+
#
|
|
87
|
+
# === Returns:
|
|
88
|
+
# * The result of the block.
|
|
89
|
+
#
|
|
90
|
+
def trace(span, opts = {}, protect_op = nil)
|
|
91
|
+
return yield if !AppOpticsAPM.loaded || !AppOpticsAPM.tracing? || AppOpticsAPM.tracing_layer_op?(protect_op)
|
|
92
|
+
|
|
93
|
+
opts.delete(:TransactionName)
|
|
94
|
+
opts.delete('TransactionName')
|
|
95
|
+
|
|
96
|
+
AppOpticsAPM::API.log_entry(span, opts, protect_op)
|
|
97
|
+
opts[:Backtrace] && opts.delete(:Backtrace) # to avoid sending backtrace twice (faster to check presence here)
|
|
98
|
+
begin
|
|
99
|
+
yield
|
|
100
|
+
rescue Exception => e
|
|
101
|
+
AppOpticsAPM::API.log_exception(span, e)
|
|
102
|
+
raise
|
|
103
|
+
ensure
|
|
104
|
+
AppOpticsAPM::API.log_exit(span, opts, protect_op)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# Collect metrics and start tracing a given block of code.
|
|
110
|
+
#
|
|
111
|
+
# This will start a trace depending on configuration and probability, detect any exceptions
|
|
112
|
+
# thrown by the block, and report errors.
|
|
113
|
+
#
|
|
114
|
+
# When start_trace returns control to the calling context, the trace will be
|
|
115
|
+
# completed and the tracing context will be cleared.
|
|
116
|
+
#
|
|
117
|
+
# === Arguments:
|
|
118
|
+
#
|
|
119
|
+
# * +span+ - Name for the span to be used as label in the trace view.
|
|
120
|
+
# * +xtrace+ - (optional) incoming X-Trace identifier to be continued.
|
|
121
|
+
# * +opts+ - (optional) hash containing key/value pairs that will be reported with this span.
|
|
122
|
+
# The value of :TransactionName will set the transaction_name.
|
|
123
|
+
#
|
|
124
|
+
# === Example:
|
|
125
|
+
#
|
|
126
|
+
# def handle_request(request, response)
|
|
127
|
+
# # ... code that processes request and response ...
|
|
128
|
+
# end
|
|
129
|
+
#
|
|
130
|
+
# def handle_request_with_appoptics(request, response)
|
|
131
|
+
# AppOpticsAPM::SDK.start_trace('custom_trace', nil, :TransactionName => 'handle_request') do
|
|
132
|
+
# handle_request(request, response)
|
|
133
|
+
# end
|
|
134
|
+
# end
|
|
135
|
+
#
|
|
136
|
+
# === Returns:
|
|
137
|
+
# * The result of the block.
|
|
138
|
+
#
|
|
139
|
+
def start_trace(span, xtrace = nil, opts = {})
|
|
140
|
+
start_trace_with_target(span, xtrace, {}, opts) { yield }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Collect metrics, trace a given block of code, and assign trace info to target.
|
|
144
|
+
#
|
|
145
|
+
# This will start a trace depending on configuration and probability, detect any exceptions
|
|
146
|
+
# thrown by the block, report errors, and assign an X-Trace to the target.
|
|
147
|
+
#
|
|
148
|
+
# The motivating use case for this is HTTP streaming in rails3. We need
|
|
149
|
+
# access to the exit event's trace id so we can set the header before any
|
|
150
|
+
# work is done, and before any headers are sent back to the client.
|
|
151
|
+
#
|
|
152
|
+
# === Arguments:
|
|
153
|
+
# * +span+ - The span the block of code belongs to.
|
|
154
|
+
# * +xtrace+ - (optional) incoming X-Trace identifier to be continued.
|
|
155
|
+
# * +target+ - (optional) has to respond to #[]=, The target object in which to place the trace information.
|
|
156
|
+
# * +opts+ - (optional) hash containing key/value pairs that will be reported with this span.
|
|
157
|
+
#
|
|
158
|
+
# === Example:
|
|
159
|
+
#
|
|
160
|
+
# def handle_request(request, response)
|
|
161
|
+
# # ... code that processes request and response ...
|
|
162
|
+
# end
|
|
163
|
+
#
|
|
164
|
+
# def handle_request_with_appoptics(request, response)
|
|
165
|
+
# AppOpticsAPM::SDK.start_trace_with_target('rails', request['X-Trace'], response) do
|
|
166
|
+
# handle_request(request, response)
|
|
167
|
+
# end
|
|
168
|
+
# end
|
|
169
|
+
#
|
|
170
|
+
# === Returns:
|
|
171
|
+
# * The result of the block.
|
|
172
|
+
#
|
|
173
|
+
def start_trace_with_target(span, xtrace, target, opts = {})
|
|
174
|
+
return yield unless AppOpticsAPM.loaded
|
|
175
|
+
|
|
176
|
+
if AppOpticsAPM::Context.isValid # not an entry span!
|
|
177
|
+
result = trace(span, opts) { yield }
|
|
178
|
+
target['X-Trace'] = AppOpticsAPM::Context.toString
|
|
179
|
+
return result
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# :TransactionName and 'TransactionName' need to be removed from opts
|
|
183
|
+
AppOpticsAPM.transaction_name = opts.delete('TransactionName') || opts.delete(:TransactionName)
|
|
184
|
+
|
|
185
|
+
AppOpticsAPM::API.log_start(span, xtrace, opts)
|
|
186
|
+
opts[:Backtrace] && opts.delete(:Backtrace) # to avoid sending backtrace twice (faster to check presence here)
|
|
187
|
+
|
|
188
|
+
# AppOpticsAPM::Event.startTrace creates an Event without an Edge
|
|
189
|
+
exit_evt = AppOpticsAPM::Event.startTrace(AppOpticsAPM::Context.get)
|
|
190
|
+
result = begin
|
|
191
|
+
AppOpticsAPM::API.send_metrics(span, opts) do
|
|
192
|
+
target['X-Trace'] = AppOpticsAPM::EventUtil.metadataString(exit_evt)
|
|
193
|
+
yield
|
|
194
|
+
end
|
|
195
|
+
rescue Exception => e
|
|
196
|
+
AppOpticsAPM::API.log_exception(span, e)
|
|
197
|
+
exit_evt.addEdge(AppOpticsAPM::Context.get)
|
|
198
|
+
xtrace = AppOpticsAPM::API.log_end(span, opts, exit_evt)
|
|
199
|
+
e.instance_variable_set(:@xtrace, xtrace)
|
|
200
|
+
raise
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
exit_evt.addEdge(AppOpticsAPM::Context.get)
|
|
204
|
+
AppOpticsAPM::API.log_end(span, opts, exit_evt)
|
|
205
|
+
|
|
206
|
+
result
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Provide a custom transaction name
|
|
210
|
+
#
|
|
211
|
+
# The AppOpticsAPM gem tries to create meaningful transaction names from controller+action
|
|
212
|
+
# or something similar depending on the framework used. However, you may want to override the
|
|
213
|
+
# transaction name to better describe your instrumented operation.
|
|
214
|
+
#
|
|
215
|
+
# Take note that on the dashboard the transaction name is converted to lowercase, and might be
|
|
216
|
+
# truncated with invalid characters replaced. Method calls with an empty string or a non-string
|
|
217
|
+
# argument won't change the current transaction name.
|
|
218
|
+
#
|
|
219
|
+
# The configuration +AppOpticsAPM.Config+['transaction_name']+['prepend_domain']+ can be set to
|
|
220
|
+
# true to have the domain name prepended to the transaction name when an event or a metric are
|
|
221
|
+
# logged. This is a global setting.
|
|
222
|
+
#
|
|
223
|
+
# === Argument:
|
|
224
|
+
#
|
|
225
|
+
# * +name+ - A non-empty string with the custom transaction name
|
|
226
|
+
#
|
|
227
|
+
# === Example:
|
|
228
|
+
#
|
|
229
|
+
# class DogfoodsController < ApplicationController
|
|
230
|
+
#
|
|
231
|
+
# def create
|
|
232
|
+
# @dogfood = Dogfood.new(params.permit(:brand, :name))
|
|
233
|
+
# @dogfood.save
|
|
234
|
+
#
|
|
235
|
+
# AppOpticsAPM::SDK.set_transaction_name("dogfoodscontroller.create_for_#{params[:brand]}")
|
|
236
|
+
#
|
|
237
|
+
# redirect_to @dogfood
|
|
238
|
+
# end
|
|
239
|
+
#
|
|
240
|
+
# end
|
|
241
|
+
#
|
|
242
|
+
# === Returns:
|
|
243
|
+
# * (String or nil) the current transaction name
|
|
244
|
+
#
|
|
245
|
+
def set_transaction_name(name)
|
|
246
|
+
if name.is_a?(String) && name.strip != ''
|
|
247
|
+
AppOpticsAPM.transaction_name = name
|
|
248
|
+
else
|
|
249
|
+
AppOpticsAPM.logger.debug "[appoptics_apm/api] Could not set transaction name, provided name is empty or not a String."
|
|
250
|
+
end
|
|
251
|
+
AppOpticsAPM.transaction_name
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# Get the currently set custom transaction name.
|
|
256
|
+
#
|
|
257
|
+
# This is provided for testing
|
|
258
|
+
#
|
|
259
|
+
# === Returns:
|
|
260
|
+
# * (String or nil) the current transaction name (without domain prepended)
|
|
261
|
+
#
|
|
262
|
+
def get_transaction_name
|
|
263
|
+
AppOpticsAPM.transaction_name
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Determine if this transaction is being traced.
|
|
267
|
+
#
|
|
268
|
+
# Tracing puts some extra load on a system, therefor not all transaction are traced.
|
|
269
|
+
# The +tracing?+ method helps to determine this so that extra work can be avoided when not tracing.
|
|
270
|
+
#
|
|
271
|
+
# === Example:
|
|
272
|
+
#
|
|
273
|
+
# kvs = expensive_info_gathering_method if AppOpticsAPM::SDK.tracing?
|
|
274
|
+
# AppOpticsAPM::SDK.trace('some_span', kvs) do
|
|
275
|
+
# # this may not create a trace every time it runs
|
|
276
|
+
# db_request
|
|
277
|
+
# end
|
|
278
|
+
#
|
|
279
|
+
def tracing?
|
|
280
|
+
AppOpticsAPM.tracing?
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Wait for AppOptics to be ready to send traces.
|
|
284
|
+
#
|
|
285
|
+
# This may be useful in short lived background processes when it is important to capture
|
|
286
|
+
# information during the whole time the process is running. Usually AppOptics doesn't block an
|
|
287
|
+
# application while it is starting up.
|
|
288
|
+
#
|
|
289
|
+
# === Argument:
|
|
290
|
+
#
|
|
291
|
+
# * +wait_milliseconds+ (int, default 3000) the maximum time to wait in milliseconds
|
|
292
|
+
#
|
|
293
|
+
# === Example:
|
|
294
|
+
#
|
|
295
|
+
# unless AppopticsAPM::SDK.appoptics_ready?(10_000)
|
|
296
|
+
# Logger.info "AppOptics not ready after 10 seconds, no metrics will be sent"
|
|
297
|
+
# end
|
|
298
|
+
#
|
|
299
|
+
def appoptics_ready?(wait_milliseconds = 3000)
|
|
300
|
+
return false unless AppOpticsAPM.loaded
|
|
301
|
+
# These codes are returned by isReady:
|
|
302
|
+
# OBOE_SERVER_RESPONSE_UNKNOWN 0
|
|
303
|
+
# OBOE_SERVER_RESPONSE_OK 1
|
|
304
|
+
# OBOE_SERVER_RESPONSE_TRY_LATER 2
|
|
305
|
+
# OBOE_SERVER_RESPONSE_LIMIT_EXCEEDED 3
|
|
306
|
+
# OBOE_SERVER_RESPONSE_INVALID_API_KEY 4
|
|
307
|
+
# OBOE_SERVER_RESPONSE_CONNECT_ERROR 5
|
|
308
|
+
AppopticsAPM::Context.isReady(wait_milliseconds) == 1
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
extend Tracing
|
|
313
|
+
|
|
314
|
+
end
|
|
315
|
+
end
|