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,37 @@
1
+ #--
2
+ # Copyright (c) 2016 SolarWinds, LLC.
3
+ # All rights reserved.
4
+ #++
5
+
6
+ # TODO remove Memcache from API and into some Util module to be included in Modules that need
7
+ # ____ these methods
8
+ module AppOpticsAPM
9
+ module API
10
+ ##
11
+ # Utility methods for the Memcache instrumentation
12
+ # currently used by dalli and memcached
13
+ module Memcache #:nodoc:
14
+ MEMCACHE_OPS = %w(add append cas decr decrement delete fetch get incr increment prepend replace set)
15
+
16
+ def memcache_hit?(result)
17
+ result.nil? ? 0 : 1
18
+ end
19
+
20
+ def remote_host(key)
21
+ return unless defined?(Lib.memcached_server_by_key) &&
22
+ defined?(@struct) && defined?(is_unix_socket?)
23
+
24
+ server_as_array = Lib.memcached_server_by_key(@struct, key.to_s)
25
+
26
+ return unless server_as_array.is_a?(Array)
27
+
28
+ server = server_as_array.first
29
+ if is_unix_socket?(server)
30
+ 'localhost'
31
+ elsif defined?(server.hostname)
32
+ server.hostname
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,55 @@
1
+ module AppOpticsAPM
2
+ module API
3
+ module Metrics
4
+
5
+ ##
6
+ # Internal: method to send duration for a transaction
7
+ # it checks if it can send metrics with the current transaction name
8
+ # or a default transaction name and sets the transaction name accordingly
9
+ #
10
+ # === Arguments:
11
+ #
12
+ # * +span+ the name of the current span (used to construct a transaction name if none is defined)
13
+ # * +kvs+ A hash containing key/value pairs, only the value of :TransactionName will be relevant
14
+ #
15
+ # === Returns:
16
+ # The result of the block.
17
+ #
18
+ # === Assigns:
19
+ # The transaction_name to kvs[:TransactionName]
20
+
21
+ def send_metrics(span, kvs)
22
+ start = Time.now
23
+ yield
24
+ ensure
25
+ duration = (1000 * 1000 * (Time.now - start)).to_i
26
+ transaction_name = determine_transaction_name(span, kvs)
27
+ kvs[:TransactionName] = AppOpticsAPM::Span.createSpan(transaction_name, nil, duration)
28
+ AppOpticsAPM.transaction_name = nil
29
+ end
30
+
31
+ private
32
+
33
+ ##
34
+ # Determine the transaction name to be set on the trace.
35
+ #
36
+ # === Argument:
37
+ # * +span+ the name of the current span (used to construct a transaction name if none is defined)
38
+ # * +kvs+ (hash, optional) the hash that may have values for 'Controller' and 'Action'
39
+ #
40
+ # === Returns:
41
+ # (string) the determined transaction name
42
+ #
43
+ def determine_transaction_name(span, kvs = {})
44
+ if AppOpticsAPM.transaction_name
45
+ AppOpticsAPM.transaction_name
46
+ elsif kvs['Controller'] && kvs['Action']
47
+ [kvs['Controller'], kvs['Action']].join('.')
48
+ else
49
+ "custom-#{span}"
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,203 @@
1
+ #--
2
+ # Copyright (c) 2016 SolarWinds, LLC.
3
+ # All rights reserved.
4
+ #++
5
+
6
+ module AppOpticsAPM
7
+ module API
8
+ ##
9
+ # Module to create profiling traces for blocks of code or methods
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
+ # ==== Arguments
16
+ #
17
+ # * +profile_name+ - A name used to identify the block being profiled.
18
+ # * +report_kvs+ - A hash containing key/value pairs that will be reported along
19
+ # with the event of this profile (optional).
20
+ # * +with_backtrace+ - Boolean to indicate whether a backtrace should
21
+ # be collected with this trace event.
22
+ #
23
+ # ==== Example
24
+ #
25
+ # def computation(n)
26
+ # AppOpticsAPM::API.profile('fib', { :n => n }) do
27
+ # fib(n)
28
+ # end
29
+ # end
30
+ #
31
+ # Returns the result of the block.
32
+ #
33
+
34
+ def profile(profile_name, report_kvs = {}, with_backtrace = false)
35
+ return yield unless AppOpticsAPM.tracing?
36
+
37
+ begin
38
+ report_kvs[:Language] ||= :ruby
39
+ report_kvs[:ProfileName] ||= profile_name
40
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if with_backtrace
41
+
42
+ AppOpticsAPM::API.log(nil, :profile_entry, report_kvs)
43
+
44
+ begin
45
+ yield
46
+ rescue => e
47
+ log_exception(nil, e)
48
+ raise
49
+ ensure
50
+ exit_kvs = {}
51
+ exit_kvs[:Language] = :ruby
52
+ exit_kvs[:ProfileName] = report_kvs[:ProfileName]
53
+
54
+ AppOpticsAPM::API.log(nil, :profile_exit, exit_kvs)
55
+ end
56
+ end
57
+ end
58
+
59
+ ##
60
+ # Public: Add profiling to a method on a class or module. That method can be of any (accessible)
61
+ # type (instance, singleton, private, protected etc.).
62
+ #
63
+ # ==== Arguments
64
+ #
65
+ # * +klass+ - the class or module that has the method to profile
66
+ # * +method+ - the method to profile. Can be singleton, instance, private etc...
67
+ # * +opts+ - a hash specifying the one or more of the following options:
68
+ # * +:arguments+ - report the arguments passed to <tt>method</tt> on each profile (default: false)
69
+ # * +:result+ - report the return value of <tt>method</tt> on each profile (default: false)
70
+ # * +:backtrace+ - report the return value of <tt>method</tt> on each profile (default: false)
71
+ # * +:name+ - alternate name for the profile reported in the dashboard (default: method name)
72
+ # * +extra_kvs+ - a hash containing any additional key/value pairs you would like reported with the profile
73
+ #
74
+ # ==== Example
75
+ #
76
+ # opts = {}
77
+ # opts[:backtrace] = true
78
+ # opts[:arguments] = false
79
+ # opts[:name] = :array_sort
80
+ #
81
+ # AppOpticsAPM::API.profile_method(Array, :sort, opts)
82
+ #
83
+ def profile_method(klass, method, opts = {}, extra_kvs = {})
84
+ # If we're on an unsupported platform (ahem Mac), just act
85
+ # like we did something to nicely play the no-op part.
86
+ return true unless AppOpticsAPM.loaded
87
+
88
+ if !klass.is_a?(Module)
89
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: Not sure what to do with #{klass}. Send a class or module."
90
+ return false
91
+ end
92
+
93
+ if method.is_a?(String)
94
+ method = method.to_sym
95
+ elsif !method.is_a?(Symbol)
96
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: Not sure what to do with #{method}. Send a string or symbol for method."
97
+ return false
98
+ end
99
+
100
+ instance_method = klass.instance_methods.include?(method) || klass.private_instance_methods.include?(method)
101
+ class_method = klass.singleton_methods.include?(method)
102
+
103
+ # Make sure the request klass::method exists
104
+ if !instance_method && !class_method
105
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: Can't instrument #{klass}.#{method} as it doesn't seem to exist."
106
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] #{__FILE__}:#{__LINE__}"
107
+ return false
108
+ end
109
+
110
+ # Strip '!' or '?' from method if present
111
+ safe_method_name = method.to_s.chop if method.to_s =~ /\?$|\!$/
112
+ safe_method_name ||= method
113
+
114
+ without_appoptics = "#{safe_method_name}_without_appoptics"
115
+ with_appoptics = "#{safe_method_name}_with_appoptics"
116
+
117
+ # Check if already profiled
118
+ if klass.instance_methods.include?(with_appoptics.to_sym) ||
119
+ klass.singleton_methods.include?(with_appoptics.to_sym)
120
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: #{klass}::#{method} already profiled."
121
+ AppOpticsAPM.logger.warn "[appoptics_apm/error] profile_method: #{__FILE__}:#{__LINE__}"
122
+ return false
123
+ end
124
+
125
+ source_location = []
126
+ if instance_method
127
+ AppOpticsAPM::Util.send_include(klass, AppOpticsAPM::MethodProfiling)
128
+ source_location = klass.instance_method(method).source_location
129
+ elsif class_method
130
+ AppOpticsAPM::Util.send_extend(klass, AppOpticsAPM::MethodProfiling)
131
+ source_location = klass.method(method).source_location
132
+ end
133
+
134
+ report_kvs = collect_profile_kvs(klass, method, opts, extra_kvs, source_location)
135
+ report_kvs[:MethodName] = safe_method_name
136
+
137
+ if instance_method
138
+ klass.class_eval do
139
+ define_method(with_appoptics) do |*args, &block|
140
+ profile_wrapper(without_appoptics, report_kvs, opts, *args, &block)
141
+ end
142
+
143
+ alias_method without_appoptics, method.to_s
144
+ alias_method method.to_s, with_appoptics
145
+ end
146
+ elsif class_method
147
+ klass.define_singleton_method(with_appoptics) do |*args, &block|
148
+ profile_wrapper(without_appoptics, report_kvs, opts, *args, &block)
149
+ end
150
+
151
+ klass.singleton_class.class_eval do
152
+ alias_method without_appoptics, method.to_s
153
+ alias_method method.to_s, with_appoptics
154
+ end
155
+ end
156
+ true
157
+ end
158
+
159
+ private
160
+
161
+ ##
162
+ # Private: Helper method to aggregate KVs to report
163
+ #
164
+ # klass - the class or module that has the method to profile
165
+ # method - the method to profile. Can be singleton, instance, private etc...
166
+ # opts - a hash specifying the one or more of the following options:
167
+ # * :arguments - report the arguments passed to <tt>method</tt> on each profile (default: false)
168
+ # * :result - report the return value of <tt>method</tt> on each profile (default: false)
169
+ # * :backtrace - report the return value of <tt>method</tt> on each profile (default: false)
170
+ # * :name - alternate name for the profile reported in the dashboard (default: method name)
171
+ # extra_kvs - a hash containing any additional KVs you would like reported with the profile
172
+ # source_location - array returned from klass.method(:name).source_location
173
+ #
174
+ def collect_profile_kvs(klass, method, opts, extra_kvs, source_location)
175
+ report_kvs = {}
176
+ report_kvs[:Language] ||= :ruby
177
+ report_kvs[:ProfileName] ||= opts[:name] ? opts[:name] : method
178
+
179
+ klass.is_a?(Class) ? report_kvs[:Class] = klass.to_s : report_kvs[:Module] = klass.to_s
180
+
181
+ # If this is a Rails Controller, report the KVs
182
+ if defined?(::AbstractController::Base) && klass.ancestors.include?(::AbstractController::Base)
183
+ report_kvs[:Controller] = klass.to_s
184
+ report_kvs[:Action] = method.to_s
185
+ end
186
+
187
+ # We won't have access to this info for native methods (those not defined in Ruby).
188
+ if source_location.is_a?(Array) && source_location.length == 2
189
+ report_kvs[:File] = source_location[0]
190
+ report_kvs[:LineNumber] = source_location[1]
191
+ end
192
+
193
+ # Merge in any extra_kvs requested
194
+ report_kvs.merge!(extra_kvs)
195
+ end
196
+
197
+
198
+ # need to set the context to public, otherwise the following `extends` will be private in api.rb
199
+ public
200
+
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,53 @@
1
+ #--
2
+ # Copyright (c) 2016 SolarWinds, LLC.
3
+ # All rights reserved.
4
+ #++
5
+
6
+ module AppOpticsAPM
7
+ module API
8
+ ##
9
+ # Provides the higher-level tracing interface for the API
10
+ #
11
+ # The tracing methods have been moved to AppOpticsAPM::SDK and AppOpticsAPM::API extends all methods from the SDK
12
+ # except for start_trace.
13
+ # AppOpticsAPM::API.start_trace is kept for backwards compatibility because it returns an array
14
+ # whereas AppOpticsAPM::SDK.start_trace will only return the result of the block.
15
+ #
16
+
17
+ module Tracing
18
+
19
+ # Public: Collect metrics and start tracing a given block of code. A
20
+ # trace will be started depending on configuration and probability.
21
+ # Detect any exceptions thrown by the block and report errors.
22
+ #
23
+ # When start_trace returns control to the calling context, the trace will be
24
+ # completed and the tracing context will be cleared.
25
+ #
26
+ # ==== Arguments
27
+ #
28
+ # * +span+ - name for the span to be used as label in the trace view
29
+ # * +xtrace+ - (optional) incoming X-Trace identifier to be continued
30
+ # * +opts+ - (optional) hash containing key/value pairs that will be reported along
31
+ # with the first event of this span
32
+ #
33
+ # ==== Example
34
+ #
35
+ # def handle_request(request, response)
36
+ # # ... code that modifies request and response ...
37
+ # end
38
+ #
39
+ # def handle_request_with_appoptics(request, response)
40
+ # start_trace('custom_span', nil, :TransactionName => 'handle_request') do
41
+ # handle_request(request, response)
42
+ # end
43
+ # end
44
+ #
45
+ # Returns an array with the result of the block and the last xtrace used
46
+ def start_trace(span, xtrace = nil, opts = {})
47
+ target = {}
48
+ [start_trace_with_target(span, xtrace, target, opts) { yield }, target['X-Trace']]
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,122 @@
1
+ #--
2
+ # Copyright (c) 2016 SolarWinds, LLC.
3
+ # All rights reserved.
4
+ #++
5
+
6
+ require 'pp'
7
+
8
+ module AppOpticsAPM
9
+ module API
10
+ ##
11
+ # General utility methods for the gem
12
+ module Util #:nodoc:
13
+ BACKTRACE_CUTOFF = 200
14
+
15
+ # Internal: Check whether the provided key is reserved or not. Reserved
16
+ # keys are either keys that are handled by liboboe calls or the appoptics_apm gem.
17
+ #
18
+ # key - the key to check.
19
+ #
20
+ # Return a boolean indicating whether or not key is reserved.
21
+ def valid_key?(key)
22
+ ![:Label, :Layer, :Edge, :Timestamp, :Timestamp_u].include?(key.to_sym)
23
+ end
24
+
25
+ # Internal: Get the current backtrace.
26
+ #
27
+ # ignore - Number of frames to ignore at the top of the backtrace. Use
28
+ # when you know how many layers deep in the key call is being
29
+ # made.
30
+ #
31
+ # Returns a string with each frame of the backtrace separated by '\r\n'.
32
+ #
33
+ def backtrace(ignore = 0)
34
+ bt = Kernel.caller
35
+ bt.slice!(0, ignore)
36
+ trim_backtrace(bt).join("\r\n")
37
+ end
38
+
39
+ # Internal: Trim a backtrace to a manageable size
40
+ #
41
+ # backtrace - the backtrace (an array of stack frames/from Kernel.caller)
42
+ #
43
+ # Returns a trimmed backtrace
44
+ def trim_backtrace(backtrace)
45
+ return backtrace unless backtrace.is_a?(Array) && backtrace.size > BACKTRACE_CUTOFF
46
+
47
+ # Trim backtraces by getting the first 180 and last 20 lines
48
+ backtrace[0, 180] + ['...[snip]...'] + backtrace[backtrace.size - 20, 20]
49
+ end
50
+
51
+ # Internal: Check if a host is blacklisted from tracing
52
+ #
53
+ # addr_port - the addr_port from Net::HTTP although this method
54
+ # can be used from any component in reality
55
+ #
56
+ # Returns a boolean on blacklisted state
57
+ def blacklisted?(addr_port)
58
+ return false unless AppOpticsAPM::Config.blacklist
59
+
60
+ # Ensure that the blacklist is an array
61
+ unless AppOpticsAPM::Config.blacklist.is_a?(Array)
62
+ val = AppOpticsAPM::Config[:blacklist]
63
+ AppOpticsAPM::Config[:blacklist] = [val.to_s]
64
+ end
65
+
66
+ AppOpticsAPM::Config.blacklist.each do |h|
67
+ return true if addr_port.to_s.match(h.to_s)
68
+ end
69
+
70
+ false
71
+ end
72
+
73
+ # Internal: Pretty print a list of arguments for reporting
74
+ #
75
+ # args - the list of arguments to work on
76
+ #
77
+ # Returns a pretty string representation of arguments
78
+ def pps(*args)
79
+ old_out = $stdout
80
+ begin
81
+ s = StringIO.new
82
+ $stdout = s
83
+ pp(*args)
84
+ ensure
85
+ $stdout = old_out
86
+ end
87
+ s.string
88
+ end
89
+
90
+ # Internal: Determine a string to report representing klass
91
+ #
92
+ # args - an instance of a Class, a Class or a Module
93
+ #
94
+ # Returns a string representation of klass
95
+ def get_class_name(klass)
96
+ kv = {}
97
+
98
+ if klass.to_s =~ /::/
99
+ klass.class.to_s.rpartition('::').last
100
+ else
101
+ if klass.is_a?(Class) && klass.is_a?(Module)
102
+ # Class
103
+ kv['Class'] = klass.to_s
104
+
105
+ elsif !klass.is_a?(Class) && !klass.is_a?(Module)
106
+ # Class instance
107
+ kv['Class'] = klass.class.to_s
108
+
109
+ else
110
+ # Module
111
+ kv['Module'] = klass.to_s
112
+ end
113
+ end
114
+ kv
115
+ end
116
+
117
+ def xtrace_v2?(xtr)
118
+ return xtr && xtr.start_with?('2B')
119
+ end
120
+ end
121
+ end
122
+ end