appoptics_apm_mnfst 4.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|