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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +5 -0
  3. data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
  4. data/.gitignore +29 -0
  5. data/.rubocop.yml +8 -0
  6. data/.travis.yml +121 -0
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +769 -0
  9. data/CONFIG.md +33 -0
  10. data/Gemfile +29 -0
  11. data/LICENSE +193 -0
  12. data/README.md +393 -0
  13. data/Rakefile +230 -0
  14. data/appoptics_apm.gemspec +61 -0
  15. data/bin/appoptics_apm_config +15 -0
  16. data/build_gem.sh +15 -0
  17. data/build_gem_upload_to_packagecloud.sh +20 -0
  18. data/examples/SDK/01_basic_tracing.rb +67 -0
  19. data/examples/carrying_context.rb +220 -0
  20. data/ext/oboe_metal/extconf.rb +114 -0
  21. data/ext/oboe_metal/lib/.keep +0 -0
  22. data/ext/oboe_metal/noop/noop.c +7 -0
  23. data/ext/oboe_metal/src/VERSION +1 -0
  24. data/init.rb +4 -0
  25. data/lib/appoptics_apm.rb +76 -0
  26. data/lib/appoptics_apm/api.rb +20 -0
  27. data/lib/appoptics_apm/api/layerinit.rb +41 -0
  28. data/lib/appoptics_apm/api/logging.rb +375 -0
  29. data/lib/appoptics_apm/api/memcache.rb +37 -0
  30. data/lib/appoptics_apm/api/metrics.rb +55 -0
  31. data/lib/appoptics_apm/api/profiling.rb +203 -0
  32. data/lib/appoptics_apm/api/tracing.rb +53 -0
  33. data/lib/appoptics_apm/api/util.rb +122 -0
  34. data/lib/appoptics_apm/base.rb +230 -0
  35. data/lib/appoptics_apm/config.rb +254 -0
  36. data/lib/appoptics_apm/frameworks/grape.rb +97 -0
  37. data/lib/appoptics_apm/frameworks/padrino.rb +108 -0
  38. data/lib/appoptics_apm/frameworks/rails.rb +94 -0
  39. data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +104 -0
  40. data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +55 -0
  41. data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
  42. data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  43. data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  44. data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +58 -0
  45. data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +50 -0
  46. data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +27 -0
  47. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
  48. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
  49. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +31 -0
  50. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +119 -0
  51. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +108 -0
  52. data/lib/appoptics_apm/frameworks/sinatra.rb +125 -0
  53. data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
  54. data/lib/appoptics_apm/inst/bunny-consumer.rb +89 -0
  55. data/lib/appoptics_apm/inst/curb.rb +330 -0
  56. data/lib/appoptics_apm/inst/dalli.rb +85 -0
  57. data/lib/appoptics_apm/inst/delayed_job.rb +92 -0
  58. data/lib/appoptics_apm/inst/em-http-request.rb +101 -0
  59. data/lib/appoptics_apm/inst/excon.rb +125 -0
  60. data/lib/appoptics_apm/inst/faraday.rb +94 -0
  61. data/lib/appoptics_apm/inst/grpc_client.rb +162 -0
  62. data/lib/appoptics_apm/inst/grpc_server.rb +120 -0
  63. data/lib/appoptics_apm/inst/http.rb +73 -0
  64. data/lib/appoptics_apm/inst/httpclient.rb +174 -0
  65. data/lib/appoptics_apm/inst/memcached.rb +86 -0
  66. data/lib/appoptics_apm/inst/mongo.rb +246 -0
  67. data/lib/appoptics_apm/inst/mongo2.rb +225 -0
  68. data/lib/appoptics_apm/inst/moped.rb +466 -0
  69. data/lib/appoptics_apm/inst/rack.rb +199 -0
  70. data/lib/appoptics_apm/inst/redis.rb +275 -0
  71. data/lib/appoptics_apm/inst/resque.rb +151 -0
  72. data/lib/appoptics_apm/inst/rest-client.rb +48 -0
  73. data/lib/appoptics_apm/inst/sequel.rb +178 -0
  74. data/lib/appoptics_apm/inst/sidekiq-client.rb +55 -0
  75. data/lib/appoptics_apm/inst/sidekiq-worker.rb +65 -0
  76. data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
  77. data/lib/appoptics_apm/inst/typhoeus.rb +108 -0
  78. data/lib/appoptics_apm/instrumentation.rb +22 -0
  79. data/lib/appoptics_apm/legacy_method_profiling.rb +90 -0
  80. data/lib/appoptics_apm/loading.rb +65 -0
  81. data/lib/appoptics_apm/logger.rb +42 -0
  82. data/lib/appoptics_apm/method_profiling.rb +33 -0
  83. data/lib/appoptics_apm/noop/README.md +9 -0
  84. data/lib/appoptics_apm/noop/context.rb +26 -0
  85. data/lib/appoptics_apm/noop/metadata.rb +22 -0
  86. data/lib/appoptics_apm/ruby.rb +35 -0
  87. data/lib/appoptics_apm/sdk/custom_metrics.rb +92 -0
  88. data/lib/appoptics_apm/sdk/tracing.rb +315 -0
  89. data/lib/appoptics_apm/support.rb +119 -0
  90. data/lib/appoptics_apm/test.rb +94 -0
  91. data/lib/appoptics_apm/thread_local.rb +26 -0
  92. data/lib/appoptics_apm/util.rb +319 -0
  93. data/lib/appoptics_apm/version.rb +15 -0
  94. data/lib/appoptics_apm/xtrace.rb +103 -0
  95. data/lib/joboe_metal.rb +212 -0
  96. data/lib/oboe.rb +7 -0
  97. data/lib/oboe/README +2 -0
  98. data/lib/oboe/backward_compatibility.rb +80 -0
  99. data/lib/oboe/inst/rack.rb +11 -0
  100. data/lib/oboe_metal.rb +198 -0
  101. data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
  102. data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +265 -0
  103. data/yardoc_frontpage.md +26 -0
  104. 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