appoptics_apm_mnfst 4.5.2

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