traceview 3.0.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +58 -0
- data/Appraisals +10 -0
- data/CHANGELOG.md +490 -0
- data/CONFIG.md +16 -0
- data/Gemfile +95 -0
- data/LICENSE +199 -0
- data/README.md +380 -0
- data/Rakefile +109 -0
- data/examples/DNT.md +35 -0
- data/examples/carrying_context.rb +225 -0
- data/examples/instrumenting_metal_controller.rb +8 -0
- data/examples/puma_on_heroku_config.rb +17 -0
- data/examples/tracing_async_threads.rb +125 -0
- data/examples/tracing_background_jobs.rb +52 -0
- data/examples/tracing_forked_processes.rb +100 -0
- data/examples/unicorn_on_heroku_config.rb +28 -0
- data/ext/oboe_metal/extconf.rb +61 -0
- data/ext/oboe_metal/noop/noop.c +7 -0
- data/ext/oboe_metal/src/bson/bson.h +221 -0
- data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
- data/ext/oboe_metal/src/oboe.h +275 -0
- data/ext/oboe_metal/src/oboe.hpp +352 -0
- data/ext/oboe_metal/src/oboe_wrap.cxx +3886 -0
- data/ext/oboe_metal/tests/test.rb +11 -0
- data/gemfiles/mongo.gemfile +33 -0
- data/gemfiles/moped.gemfile +33 -0
- data/get_version.rb +5 -0
- data/init.rb +4 -0
- data/lib/joboe_metal.rb +206 -0
- data/lib/oboe/README +2 -0
- data/lib/oboe/backward_compatibility.rb +59 -0
- data/lib/oboe/inst/rack.rb +11 -0
- data/lib/oboe.rb +7 -0
- data/lib/oboe_metal.rb +151 -0
- data/lib/rails/generators/traceview/install_generator.rb +76 -0
- data/lib/rails/generators/traceview/templates/traceview_initializer.rb +159 -0
- data/lib/traceview/api/layerinit.rb +51 -0
- data/lib/traceview/api/logging.rb +209 -0
- data/lib/traceview/api/memcache.rb +31 -0
- data/lib/traceview/api/profiling.rb +50 -0
- data/lib/traceview/api/tracing.rb +135 -0
- data/lib/traceview/api/util.rb +121 -0
- data/lib/traceview/api.rb +18 -0
- data/lib/traceview/base.rb +225 -0
- data/lib/traceview/config.rb +238 -0
- data/lib/traceview/frameworks/grape.rb +97 -0
- data/lib/traceview/frameworks/padrino/templates.rb +58 -0
- data/lib/traceview/frameworks/padrino.rb +64 -0
- data/lib/traceview/frameworks/rails/helpers/rum/rum_ajax_header.js.erb +5 -0
- data/lib/traceview/frameworks/rails/helpers/rum/rum_footer.js.erb +1 -0
- data/lib/traceview/frameworks/rails/helpers/rum/rum_header.js.erb +3 -0
- data/lib/traceview/frameworks/rails/inst/action_controller.rb +216 -0
- data/lib/traceview/frameworks/rails/inst/action_view.rb +56 -0
- data/lib/traceview/frameworks/rails/inst/action_view_2x.rb +54 -0
- data/lib/traceview/frameworks/rails/inst/action_view_30.rb +48 -0
- data/lib/traceview/frameworks/rails/inst/active_record.rb +24 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql2.rb +28 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/oracle.rb +18 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/postgresql.rb +30 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/utils.rb +117 -0
- data/lib/traceview/frameworks/rails.rb +145 -0
- data/lib/traceview/frameworks/sinatra/templates.rb +56 -0
- data/lib/traceview/frameworks/sinatra.rb +95 -0
- data/lib/traceview/inst/cassandra.rb +279 -0
- data/lib/traceview/inst/dalli.rb +86 -0
- data/lib/traceview/inst/em-http-request.rb +99 -0
- data/lib/traceview/inst/excon.rb +111 -0
- data/lib/traceview/inst/faraday.rb +73 -0
- data/lib/traceview/inst/http.rb +87 -0
- data/lib/traceview/inst/httpclient.rb +173 -0
- data/lib/traceview/inst/memcache.rb +102 -0
- data/lib/traceview/inst/memcached.rb +94 -0
- data/lib/traceview/inst/mongo.rb +238 -0
- data/lib/traceview/inst/moped.rb +474 -0
- data/lib/traceview/inst/rack.rb +122 -0
- data/lib/traceview/inst/redis.rb +271 -0
- data/lib/traceview/inst/resque.rb +192 -0
- data/lib/traceview/inst/rest-client.rb +38 -0
- data/lib/traceview/inst/sequel.rb +162 -0
- data/lib/traceview/inst/typhoeus.rb +102 -0
- data/lib/traceview/instrumentation.rb +21 -0
- data/lib/traceview/loading.rb +94 -0
- data/lib/traceview/logger.rb +41 -0
- data/lib/traceview/method_profiling.rb +84 -0
- data/lib/traceview/ruby.rb +36 -0
- data/lib/traceview/support.rb +113 -0
- data/lib/traceview/thread_local.rb +26 -0
- data/lib/traceview/util.rb +250 -0
- data/lib/traceview/version.rb +16 -0
- data/lib/traceview/xtrace.rb +90 -0
- data/lib/traceview.rb +62 -0
- data/test/frameworks/apps/grape_nested.rb +30 -0
- data/test/frameworks/apps/grape_simple.rb +24 -0
- data/test/frameworks/apps/padrino_simple.rb +45 -0
- data/test/frameworks/apps/sinatra_simple.rb +24 -0
- data/test/frameworks/grape_test.rb +142 -0
- data/test/frameworks/padrino_test.rb +30 -0
- data/test/frameworks/sinatra_test.rb +30 -0
- data/test/instrumentation/cassandra_test.rb +380 -0
- data/test/instrumentation/dalli_test.rb +171 -0
- data/test/instrumentation/em_http_request_test.rb +86 -0
- data/test/instrumentation/excon_test.rb +207 -0
- data/test/instrumentation/faraday_test.rb +235 -0
- data/test/instrumentation/http_test.rb +140 -0
- data/test/instrumentation/httpclient_test.rb +296 -0
- data/test/instrumentation/memcache_test.rb +251 -0
- data/test/instrumentation/memcached_test.rb +226 -0
- data/test/instrumentation/mongo_test.rb +462 -0
- data/test/instrumentation/moped_test.rb +496 -0
- data/test/instrumentation/rack_test.rb +116 -0
- data/test/instrumentation/redis_hashes_test.rb +265 -0
- data/test/instrumentation/redis_keys_test.rb +318 -0
- data/test/instrumentation/redis_lists_test.rb +310 -0
- data/test/instrumentation/redis_misc_test.rb +160 -0
- data/test/instrumentation/redis_sets_test.rb +293 -0
- data/test/instrumentation/redis_sortedsets_test.rb +325 -0
- data/test/instrumentation/redis_strings_test.rb +333 -0
- data/test/instrumentation/resque_test.rb +62 -0
- data/test/instrumentation/rest-client_test.rb +294 -0
- data/test/instrumentation/sequel_mysql2_test.rb +326 -0
- data/test/instrumentation/sequel_mysql_test.rb +326 -0
- data/test/instrumentation/sequel_pg_test.rb +330 -0
- data/test/instrumentation/typhoeus_test.rb +285 -0
- data/test/minitest_helper.rb +187 -0
- data/test/profiling/method_test.rb +198 -0
- data/test/servers/rackapp_8101.rb +22 -0
- data/test/support/backcompat_test.rb +269 -0
- data/test/support/config_test.rb +128 -0
- data/test/support/dnt_test.rb +73 -0
- data/test/support/liboboe_settings_test.rb +104 -0
- data/test/support/xtrace_test.rb +35 -0
- data/traceview.gemspec +29 -0
- metadata +248 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Copyright (c) 2013 AppNeta, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module TraceView
|
|
5
|
+
##
|
|
6
|
+
# Provides utility methods for use while in the business
|
|
7
|
+
# of instrumenting code
|
|
8
|
+
module Util
|
|
9
|
+
class << self
|
|
10
|
+
def contextual_name(cls)
|
|
11
|
+
# Attempt to infer a contextual name if not indicated
|
|
12
|
+
#
|
|
13
|
+
# For example:
|
|
14
|
+
# ::ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.to_s.split(/::/).last
|
|
15
|
+
# => "AbstractMysqlAdapter"
|
|
16
|
+
#
|
|
17
|
+
cls.to_s.split(/::/).last
|
|
18
|
+
rescue
|
|
19
|
+
cls
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# method_alias
|
|
24
|
+
#
|
|
25
|
+
# Centralized utility method to alias a method on an arbitrary
|
|
26
|
+
# class or module.
|
|
27
|
+
#
|
|
28
|
+
def method_alias(cls, method, name = nil)
|
|
29
|
+
name ||= contextual_name(cls)
|
|
30
|
+
|
|
31
|
+
if cls.method_defined?(method.to_sym) || cls.private_method_defined?(method.to_sym)
|
|
32
|
+
|
|
33
|
+
# Strip '!' or '?' from method if present
|
|
34
|
+
safe_method_name = method.to_s.chop if method.to_s =~ /\?$|\!$/
|
|
35
|
+
safe_method_name ||= method
|
|
36
|
+
|
|
37
|
+
without_traceview = "#{safe_method_name}_without_traceview"
|
|
38
|
+
with_traceview = "#{safe_method_name}_with_traceview"
|
|
39
|
+
|
|
40
|
+
# Only alias if we haven't done so already
|
|
41
|
+
unless cls.method_defined?(without_traceview.to_sym) ||
|
|
42
|
+
cls.private_method_defined?(without_traceview.to_sym)
|
|
43
|
+
|
|
44
|
+
cls.class_eval do
|
|
45
|
+
alias_method without_traceview, "#{method}"
|
|
46
|
+
alias_method "#{method}", with_traceview
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
else
|
|
50
|
+
TraceView.logger.warn "[traceview/loading] Couldn't properly instrument #{name}.#{method}. Partial traces may occur."
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# class_method_alias
|
|
56
|
+
#
|
|
57
|
+
# Centralized utility method to alias a class method on an arbitrary
|
|
58
|
+
# class or module
|
|
59
|
+
#
|
|
60
|
+
def class_method_alias(cls, method, name = nil)
|
|
61
|
+
name ||= contextual_name(cls)
|
|
62
|
+
|
|
63
|
+
if cls.singleton_methods.include? method.to_sym
|
|
64
|
+
|
|
65
|
+
# Strip '!' or '?' from method if present
|
|
66
|
+
safe_method_name = method.to_s.chop if method.to_s =~ /\?$|\!$/
|
|
67
|
+
safe_method_name ||= method
|
|
68
|
+
|
|
69
|
+
without_traceview = "#{safe_method_name}_without_traceview"
|
|
70
|
+
with_traceview = "#{safe_method_name}_with_traceview"
|
|
71
|
+
|
|
72
|
+
# Only alias if we haven't done so already
|
|
73
|
+
unless cls.singleton_methods.include? without_traceview.to_sym
|
|
74
|
+
cls.singleton_class.send(:alias_method, without_traceview, "#{method}")
|
|
75
|
+
cls.singleton_class.send(:alias_method, "#{method}", with_traceview)
|
|
76
|
+
end
|
|
77
|
+
else TraceView.logger.warn "[traceview/loading] Couldn't properly instrument #{name}. Partial traces may occur."
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
# send_extend
|
|
83
|
+
#
|
|
84
|
+
# Centralized utility method to send an extend call for an
|
|
85
|
+
# arbitrary class
|
|
86
|
+
def send_extend(target_cls, cls)
|
|
87
|
+
target_cls.send(:extend, cls) if defined?(target_cls)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# send_include
|
|
92
|
+
#
|
|
93
|
+
# Centralized utility method to send a include call for an
|
|
94
|
+
# arbitrary class
|
|
95
|
+
def send_include(target_cls, cls)
|
|
96
|
+
target_cls.send(:include, cls) if defined?(target_cls)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# static_asset?
|
|
101
|
+
#
|
|
102
|
+
# Given a path, this method determines whether it is a static asset or not (based
|
|
103
|
+
# solely on filename)
|
|
104
|
+
#
|
|
105
|
+
def static_asset?(path)
|
|
106
|
+
(path =~ Regexp.new(TraceView::Config[:dnt_regexp], TraceView::Config[:dnt_opts]))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
##
|
|
110
|
+
# prettify
|
|
111
|
+
#
|
|
112
|
+
# Even to my surprise, 'prettify' is a real word:
|
|
113
|
+
# transitive v. To make pretty or prettier, especially in a superficial or insubstantial way.
|
|
114
|
+
# from The American Heritage Dictionary of the English Language, 4th Edition
|
|
115
|
+
#
|
|
116
|
+
# This method makes things 'purty' for reporting.
|
|
117
|
+
def prettify(x)
|
|
118
|
+
if (x.to_s =~ /^#</) == 0
|
|
119
|
+
x.class.to_s
|
|
120
|
+
else
|
|
121
|
+
x.to_s
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# upcase
|
|
127
|
+
#
|
|
128
|
+
# Occasionally, we want to send some values in all caps. This is true
|
|
129
|
+
# for things like HTTP scheme or method. This takes anything and does
|
|
130
|
+
# it's best to safely convert it to a string (if needed) and convert it
|
|
131
|
+
# to all uppercase.
|
|
132
|
+
def upcase(o)
|
|
133
|
+
if o.is_a?(String) || o.respond_to?(:to_s)
|
|
134
|
+
o.to_s.upcase
|
|
135
|
+
else
|
|
136
|
+
TraceView.logger.debug "[traceview/debug] TraceView::Util.upcase: could not convert #{o.class}"
|
|
137
|
+
"UNKNOWN"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
##
|
|
143
|
+
# to_query
|
|
144
|
+
#
|
|
145
|
+
# Used under Ruby 1.8.7 to convert a hash into a URL
|
|
146
|
+
# query. A backport of Hash#to_query.
|
|
147
|
+
#
|
|
148
|
+
def to_query(h)
|
|
149
|
+
return "" unless h.is_a?(Hash)
|
|
150
|
+
|
|
151
|
+
# If called from a newer Ruby, use the builtin.
|
|
152
|
+
return h.to_query if RUBY_VERSION >= '1.9.3'
|
|
153
|
+
|
|
154
|
+
result = []
|
|
155
|
+
|
|
156
|
+
h.each { |k, v| result.push (k.to_s + '=' + v.to_s) }
|
|
157
|
+
return result.sort.join('&')
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
##
|
|
161
|
+
# build_report
|
|
162
|
+
#
|
|
163
|
+
# Internal: Build a hash of KVs that reports on the status of the
|
|
164
|
+
# running environment. This is used on stack boot in __Init reporting
|
|
165
|
+
# and for TraceView.support_report.
|
|
166
|
+
def build_init_report
|
|
167
|
+
platform_info = { '__Init' => 1 }
|
|
168
|
+
|
|
169
|
+
begin
|
|
170
|
+
platform_info['Force'] = true
|
|
171
|
+
platform_info['Ruby.Platform.Version'] = RUBY_PLATFORM
|
|
172
|
+
platform_info['Ruby.Version'] = RUBY_VERSION
|
|
173
|
+
platform_info['Ruby.TraceView.Version'] = ::TraceView::Version::STRING
|
|
174
|
+
platform_info['RubyHeroku.TraceView.Version'] = ::TraceViewHeroku::Version::STRING if defined?(::TraceViewHeroku)
|
|
175
|
+
platform_info['Ruby.TraceMode.Version'] = ::TraceView::Config[:tracing_mode]
|
|
176
|
+
|
|
177
|
+
# Report the framework in use
|
|
178
|
+
if defined?(::RailsLts)
|
|
179
|
+
platform_info['Ruby.RailsLts.Version'] = "RailsLts-#{::RailsLts::VERSION}"
|
|
180
|
+
elsif defined?(::Rails)
|
|
181
|
+
platform_info['Ruby.Rails.Version'] = "Rails-#{::Rails.version}"
|
|
182
|
+
end
|
|
183
|
+
platform_info['Ruby.Grape.Version'] = "Grape-#{::Grape::VERSION}" if defined?(::Grape)
|
|
184
|
+
platform_info['Ruby.Cramp.Version'] = "Cramp-#{::Cramp::VERSION}" if defined?(::Cramp)
|
|
185
|
+
|
|
186
|
+
if defined?(::Padrino)
|
|
187
|
+
platform_info['Ruby.Padrino.Version'] = "Padrino-#{::Padrino::VERSION}"
|
|
188
|
+
elsif defined?(::Sinatra)
|
|
189
|
+
platform_info['Ruby.Sinatra.Version'] = "Sinatra-#{::Sinatra::VERSION}"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Report the instrumented libraries
|
|
193
|
+
platform_info['Ruby.Cassandra.Version'] = "Cassandra-#{::Cassandra.VERSION}" if defined?(::Cassandra)
|
|
194
|
+
platform_info['Ruby.Dalli.Version'] = "Dalli-#{::Dalli::VERSION}" if defined?(::Dalli)
|
|
195
|
+
platform_info['Ruby.Excon.Version'] = "Excon-#{::Excon::VERSION}" if defined?(::Excon::VERSION)
|
|
196
|
+
platform_info['Ruby.Faraday.Version'] = "Faraday-#{::Faraday::VERSION}" if defined?(::Faraday)
|
|
197
|
+
platform_info['Ruby.HTTPClient.Version'] = "HTTPClient-#{::HTTPClient::VERSION}" if defined?(::HTTPClient::VERSION)
|
|
198
|
+
platform_info['Ruby.MemCache.Version'] = "MemCache-#{::MemCache::VERSION}" if defined?(::MemCache)
|
|
199
|
+
platform_info['Ruby.Moped.Version'] = "Moped-#{::Moped::VERSION}" if defined?(::Moped)
|
|
200
|
+
platform_info['Ruby.Redis.Version'] = "Redis-#{::Redis::VERSION}" if defined?(::Redis)
|
|
201
|
+
platform_info['Ruby.Resque.Version'] = "Resque-#{::Resque::VERSION}" if defined?(::Resque)
|
|
202
|
+
platform_info['Ruby.RestClient.Version'] = "RestClient-#{::RestClient::VERSION}" if defined?(::RestClient::VERSION)
|
|
203
|
+
platform_info['Ruby.Typhoeus.Version'] = "Typhoeus-#{::Typhoeus::VERSION}" if defined?(::Typhoeus::VERSION)
|
|
204
|
+
|
|
205
|
+
# Special case since the Mongo 1.x driver doesn't embed the version number in the gem directly
|
|
206
|
+
if ::Gem.loaded_specs.key?('mongo')
|
|
207
|
+
platform_info['Ruby.Mongo.Version'] = "Mongo-#{::Gem.loaded_specs['mongo'].version}"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Report the DB adapter in use
|
|
211
|
+
platform_info['Ruby.Mysql.Version'] = Mysql::GemVersion::VERSION if defined?(Mysql::GemVersion::VERSION)
|
|
212
|
+
platform_info['Ruby.PG.Version'] = PG::VERSION if defined?(PG::VERSION)
|
|
213
|
+
platform_info['Ruby.Mysql2.Version'] = Mysql2::VERSION if defined?(Mysql2::VERSION)
|
|
214
|
+
platform_info['Ruby.Sequel.Version'] = ::Sequel::VERSION if defined?(::Sequel::VERSION)
|
|
215
|
+
|
|
216
|
+
# Report the server in use (if possible)
|
|
217
|
+
if defined?(::Unicorn)
|
|
218
|
+
platform_info['Ruby.AppContainer.Version'] = "Unicorn-#{::Unicorn::Const::UNICORN_VERSION}"
|
|
219
|
+
elsif defined?(::Puma)
|
|
220
|
+
platform_info['Ruby.AppContainer.Version'] = "Puma-#{::Puma::Const::PUMA_VERSION} (#{::Puma::Const::CODE_NAME})"
|
|
221
|
+
elsif defined?(::PhusionPassenger)
|
|
222
|
+
platform_info['Ruby.AppContainer.Version'] = "#{::PhusionPassenger::PACKAGE_NAME}-#{::PhusionPassenger::VERSION_STRING}"
|
|
223
|
+
elsif defined?(::Thin)
|
|
224
|
+
platform_info['Ruby.AppContainer.Version'] = "Thin-#{::Thin::VERSION::STRING} (#{::Thin::VERSION::CODENAME})"
|
|
225
|
+
elsif defined?(::Mongrel)
|
|
226
|
+
platform_info['Ruby.AppContainer.Version'] = "Mongrel-#{::Mongrel::Const::MONGREL_VERSION}"
|
|
227
|
+
elsif defined?(::Mongrel2)
|
|
228
|
+
platform_info['Ruby.AppContainer.Version'] = "Mongrel2-#{::Mongrel2::VERSION}"
|
|
229
|
+
elsif defined?(::Trinidad)
|
|
230
|
+
platform_info['Ruby.AppContainer.Version'] = "Trinidad-#{::Trinidad::VERSION}"
|
|
231
|
+
elsif defined?(::WEBrick::VERSION)
|
|
232
|
+
platform_info['Ruby.AppContainer.Version'] = "WEBrick-#{::WEBrick::VERSION}"
|
|
233
|
+
else
|
|
234
|
+
platform_info['Ruby.AppContainer.Version'] = File.basename($PROGRAM_NAME)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
rescue StandardError, ScriptError => e
|
|
238
|
+
# Also rescue ScriptError (aka SyntaxError) in case one of the expected
|
|
239
|
+
# version defines don't exist
|
|
240
|
+
|
|
241
|
+
platform_info['Error'] = "Error in build_report: #{e.message}"
|
|
242
|
+
|
|
243
|
+
TraceView.logger.warn "[traceview/warn] Error in build_init_report: #{e.message}"
|
|
244
|
+
TraceView.logger.debug e.backtrace
|
|
245
|
+
end
|
|
246
|
+
platform_info
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright (c) 2013 AppNeta, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module TraceView
|
|
5
|
+
##
|
|
6
|
+
# The current version of the gem. Used mainly by
|
|
7
|
+
# traceview.gemspec during gem build process
|
|
8
|
+
module Version
|
|
9
|
+
MAJOR = 3
|
|
10
|
+
MINOR = 0
|
|
11
|
+
PATCH = 0
|
|
12
|
+
BUILD = nil
|
|
13
|
+
|
|
14
|
+
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Copyright (c) 2013 AppNeta, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
module TraceView
|
|
5
|
+
##
|
|
6
|
+
# Methods to act on, manipulate or investigate an X-Trace
|
|
7
|
+
# value
|
|
8
|
+
module XTrace
|
|
9
|
+
class << self
|
|
10
|
+
##
|
|
11
|
+
# TraceView::XTrace.valid?
|
|
12
|
+
#
|
|
13
|
+
# Perform basic validation on a potential X-Trace ID
|
|
14
|
+
#
|
|
15
|
+
def valid?(xtrace)
|
|
16
|
+
# Shouldn't be nil
|
|
17
|
+
return false unless xtrace
|
|
18
|
+
|
|
19
|
+
# The X-Trace ID shouldn't be an initialized empty ID
|
|
20
|
+
return false if (xtrace =~ /^1b0000000/i) == 0
|
|
21
|
+
|
|
22
|
+
# Valid X-Trace IDs have a length of 58 bytes and start with '1b'
|
|
23
|
+
return false unless xtrace.length == 58 && (xtrace =~ /^1b/i) == 0
|
|
24
|
+
|
|
25
|
+
true
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
TraceView.logger.debug e.message
|
|
28
|
+
TraceView.logger.debug e.backtrace
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# TraceView::XTrace.task_id
|
|
34
|
+
#
|
|
35
|
+
# Extract and return the task_id portion of an X-Trace ID
|
|
36
|
+
#
|
|
37
|
+
def task_id(xtrace)
|
|
38
|
+
return nil unless TraceView::XTrace.valid?(xtrace)
|
|
39
|
+
|
|
40
|
+
xtrace[2..41]
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
TraceView.logger.debug e.message
|
|
43
|
+
TraceView.logger.debug e.backtrace
|
|
44
|
+
return nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# TraceView::XTrace.edge_id
|
|
49
|
+
#
|
|
50
|
+
# Extract and return the edge_id portion of an X-Trace ID
|
|
51
|
+
#
|
|
52
|
+
def edge_id(xtrace)
|
|
53
|
+
return nil unless TraceView::XTrace.valid?(xtrace)
|
|
54
|
+
|
|
55
|
+
xtrace[42..57]
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
TraceView.logger.debug e.message
|
|
58
|
+
TraceView.logger.debug e.backtrace
|
|
59
|
+
return nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# continue_service_context
|
|
64
|
+
#
|
|
65
|
+
# In the case of service calls such as external HTTP requests, we
|
|
66
|
+
# pass along X-Trace headers so that request context can be maintained
|
|
67
|
+
# across servers and applications.
|
|
68
|
+
#
|
|
69
|
+
# Remote requests can return a X-Trace header in which case we want
|
|
70
|
+
# to pickup on and continue the context in most cases.
|
|
71
|
+
#
|
|
72
|
+
# @start is the context just before the outgoing request
|
|
73
|
+
#
|
|
74
|
+
# @finish is the context returned to us (as an HTTP response header
|
|
75
|
+
# if that be the case)
|
|
76
|
+
#
|
|
77
|
+
def continue_service_context(start, finish)
|
|
78
|
+
if TraceView::XTrace.valid?(finish) && TraceView.tracing?
|
|
79
|
+
|
|
80
|
+
# Assure that we received back a valid X-Trace with the same task_id
|
|
81
|
+
if TraceView::XTrace.task_id(start) == TraceView::XTrace.task_id(finish)
|
|
82
|
+
TraceView::Context.fromString(finish)
|
|
83
|
+
else
|
|
84
|
+
TraceView.logger.debug "Mismatched returned X-Trace ID: #{finish}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/traceview.rb
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Copyright (c) 2013 AppNeta, Inc.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
# Backward compatibility for supported environment variables
|
|
5
|
+
ENV['TRACEVIEW_GEM_VERBOSE'] = ENV['OBOE_GEM_VERBOSE'] if ENV.key?('OBOE_GEM_VERBOSE')
|
|
6
|
+
ENV['TRACEVIEW_GEM_TEST'] = ENV['OBOE_GEM_TEST'] if ENV.key?('OBOE_GEM_TEST')
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require 'traceview/version'
|
|
10
|
+
require 'traceview/thread_local'
|
|
11
|
+
require 'traceview/logger'
|
|
12
|
+
require 'traceview/util'
|
|
13
|
+
require 'traceview/xtrace'
|
|
14
|
+
require 'traceview/support'
|
|
15
|
+
|
|
16
|
+
# If OboeHeroku is already defined then we are in a PaaS environment
|
|
17
|
+
# with an alternate metal (see the oboe-heroku gem)
|
|
18
|
+
unless defined?(OboeHeroku)
|
|
19
|
+
require 'traceview/base'
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
if RUBY_PLATFORM == 'java'
|
|
23
|
+
require '/usr/local/tracelytics/tracelyticsagent.jar'
|
|
24
|
+
require 'joboe_metal'
|
|
25
|
+
else
|
|
26
|
+
require "oboe_metal.so"
|
|
27
|
+
require "oboe_metal.rb"
|
|
28
|
+
end
|
|
29
|
+
rescue LoadError
|
|
30
|
+
TraceView.loaded = false
|
|
31
|
+
|
|
32
|
+
unless ENV['RAILS_GROUP'] == 'assets' or ENV['IGNORE_TRACEVIEW_WARNING']
|
|
33
|
+
$stderr.puts '=============================================================='
|
|
34
|
+
$stderr.puts 'Missing TraceView libraries. Tracing disabled.'
|
|
35
|
+
$stderr.puts 'See: http://bit.ly/1DaNOjw'
|
|
36
|
+
$stderr.puts '=============================================================='
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
require 'traceview/config'
|
|
42
|
+
require 'traceview/loading'
|
|
43
|
+
|
|
44
|
+
if TraceView.loaded
|
|
45
|
+
require 'traceview/method_profiling'
|
|
46
|
+
require 'traceview/instrumentation'
|
|
47
|
+
|
|
48
|
+
# Frameworks
|
|
49
|
+
require 'traceview/frameworks/rails'
|
|
50
|
+
require 'traceview/frameworks/sinatra'
|
|
51
|
+
require 'traceview/frameworks/padrino'
|
|
52
|
+
require 'traceview/frameworks/grape'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Load Ruby module last. If there is no framework detected,
|
|
56
|
+
# it will load all of the Ruby instrumentation
|
|
57
|
+
require 'traceview/ruby'
|
|
58
|
+
require 'oboe/backward_compatibility'
|
|
59
|
+
rescue => e
|
|
60
|
+
$stderr.puts "[traceview/error] Problem loading: #{e.inspect}"
|
|
61
|
+
$stderr.puts e.backtrace
|
|
62
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'grape'
|
|
2
|
+
|
|
3
|
+
class Wrapper < Grape::API
|
|
4
|
+
class GrapeSimple < Grape::API
|
|
5
|
+
rescue_from :all do |e|
|
|
6
|
+
error_response({ message: "rescued from #{e.class.name}" })
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
get '/json_endpoint' do
|
|
10
|
+
present({ :test => true })
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
get "/break" do
|
|
14
|
+
raise Exception.new("This should have http status code 500!")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
get "/error" do
|
|
18
|
+
error!("This is a error with 'error'!")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
get "/breakstring" do
|
|
22
|
+
raise "This should have http status code 500!"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class GrapeNested < Grape::API
|
|
28
|
+
mount Wrapper::GrapeSimple
|
|
29
|
+
end
|
|
30
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'grape'
|
|
2
|
+
|
|
3
|
+
class GrapeSimple < Grape::API
|
|
4
|
+
rescue_from :all do |e|
|
|
5
|
+
error_response({ message: "rescued from #{e.class.name}" })
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
get '/json_endpoint' do
|
|
9
|
+
present({ :test => true })
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
get "/break" do
|
|
13
|
+
raise Exception.new("This should have http status code 500!")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
get "/error" do
|
|
17
|
+
error!("This is a error with 'error'!")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
get "/breakstring" do
|
|
21
|
+
raise "This should have http status code 500!"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# This test Padrino stack file was taken from the padrino-core gem.
|
|
2
|
+
#
|
|
3
|
+
PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
|
|
4
|
+
# Remove this comment if you want do some like this: ruby PADRINO_ENV=test app.rb
|
|
5
|
+
#
|
|
6
|
+
# require 'rubygems'
|
|
7
|
+
# require 'padrino-core'
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
class SimpleDemo < Padrino::Application
|
|
11
|
+
set :public_folder, File.dirname(__FILE__)
|
|
12
|
+
set :reload, true
|
|
13
|
+
before { true }
|
|
14
|
+
after { true }
|
|
15
|
+
error(404) { "404" }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
SimpleDemo.controllers do
|
|
19
|
+
get "/" do
|
|
20
|
+
'The magick number is: 2767356926488785838763860464013972991031534522105386787489885890443740254365!' # Change only the number!!!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
get "/rand" do
|
|
24
|
+
rand(2 ** 256).to_s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
get "/render" do
|
|
28
|
+
render :erb, "This is an erb render"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
get "/break" do
|
|
32
|
+
raise "This is a controller exception!"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
## If you want use this as a standalone app uncomment:
|
|
37
|
+
#
|
|
38
|
+
# Padrino.mount("SimpleDemo").to("/")
|
|
39
|
+
# Padrino.run! unless Padrino.loaded? # If you enable reloader prevent to re-run the app
|
|
40
|
+
#
|
|
41
|
+
# Then run it from your console: ruby -I"lib" test/fixtures/apps/simple.rb
|
|
42
|
+
#
|
|
43
|
+
|
|
44
|
+
Padrino.load!
|
|
45
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'sinatra'
|
|
2
|
+
|
|
3
|
+
class SinatraSimple < Sinatra::Base
|
|
4
|
+
set :reload, true
|
|
5
|
+
|
|
6
|
+
get "/" do
|
|
7
|
+
'The magick number is: 2767356926488785838763860464013972991031534522105386787489885890443740254365!' # Change only the number!!!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
get "/rand" do
|
|
11
|
+
rand(2 ** 256).to_s
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
get "/render" do
|
|
15
|
+
render :erb, "This is an erb render"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
get "/break" do
|
|
19
|
+
raise "This is a controller exception!"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
use SinatraSimple
|
|
24
|
+
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
if RUBY_VERSION >= '1.9.3'
|
|
2
|
+
require 'minitest_helper'
|
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/apps/grape_simple')
|
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/apps/grape_nested')
|
|
5
|
+
|
|
6
|
+
describe Grape do
|
|
7
|
+
before do
|
|
8
|
+
clear_all_traces
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should trace a request to a simple grape stack" do
|
|
12
|
+
@app = GrapeSimple
|
|
13
|
+
|
|
14
|
+
r = get "/json_endpoint"
|
|
15
|
+
|
|
16
|
+
r.status.must_equal 200
|
|
17
|
+
r.headers.key?('X-Trace').must_equal true
|
|
18
|
+
|
|
19
|
+
traces = get_all_traces
|
|
20
|
+
traces.count.must_equal 5
|
|
21
|
+
|
|
22
|
+
validate_outer_layers(traces, 'rack')
|
|
23
|
+
|
|
24
|
+
traces[2]['Layer'].must_equal "grape"
|
|
25
|
+
traces[3]['Layer'].must_equal "grape"
|
|
26
|
+
traces[3].has_key?('Controller').must_equal true
|
|
27
|
+
traces[3].has_key?('Action').must_equal true
|
|
28
|
+
traces[4]['Label'].must_equal "exit"
|
|
29
|
+
|
|
30
|
+
# Validate the existence of the response header
|
|
31
|
+
r.headers.key?('X-Trace').must_equal true
|
|
32
|
+
r.headers['X-Trace'].must_equal traces[4]['X-Trace']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should trace a request to a nested grape stack" do
|
|
36
|
+
@app = GrapeNested
|
|
37
|
+
|
|
38
|
+
r = get "/json_endpoint"
|
|
39
|
+
|
|
40
|
+
r.status.must_equal 200
|
|
41
|
+
r.headers.key?('X-Trace').must_equal true
|
|
42
|
+
|
|
43
|
+
traces = get_all_traces
|
|
44
|
+
traces.count.must_equal 5
|
|
45
|
+
|
|
46
|
+
validate_outer_layers(traces, 'rack')
|
|
47
|
+
|
|
48
|
+
traces[2]['Layer'].must_equal "grape"
|
|
49
|
+
traces[3]['Layer'].must_equal "grape"
|
|
50
|
+
traces[3].has_key?('Controller').must_equal true
|
|
51
|
+
traces[3].has_key?('Action').must_equal true
|
|
52
|
+
traces[4]['Label'].must_equal "exit"
|
|
53
|
+
|
|
54
|
+
# Validate the existence of the response header
|
|
55
|
+
r.headers.key?('X-Trace').must_equal true
|
|
56
|
+
r.headers['X-Trace'].must_equal traces[4]['X-Trace']
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should trace a an error in a nested grape stack" do
|
|
60
|
+
@app = GrapeNested
|
|
61
|
+
|
|
62
|
+
r = get "/error"
|
|
63
|
+
|
|
64
|
+
r.status.must_equal 500
|
|
65
|
+
r.headers.key?('X-Trace').must_equal true
|
|
66
|
+
|
|
67
|
+
traces = get_all_traces
|
|
68
|
+
traces.count.must_equal 6
|
|
69
|
+
|
|
70
|
+
validate_outer_layers(traces, 'rack')
|
|
71
|
+
|
|
72
|
+
traces[2]['Layer'].must_equal "grape"
|
|
73
|
+
traces[2]['Label'].must_equal "entry"
|
|
74
|
+
traces[3]['Layer'].must_equal "grape"
|
|
75
|
+
traces[3]['Label'].must_equal "exit"
|
|
76
|
+
traces[3].has_key?('Controller').must_equal true
|
|
77
|
+
traces[3].has_key?('Action').must_equal true
|
|
78
|
+
traces[4]['Label'].must_equal "error"
|
|
79
|
+
traces[4]['ErrorClass'].must_equal "GrapeError"
|
|
80
|
+
traces[4]['ErrorMsg'].must_equal "This is a error with 'error'!"
|
|
81
|
+
traces[4].has_key?('Backtrace').must_equal true
|
|
82
|
+
traces[5]['Layer'].must_equal "rack"
|
|
83
|
+
traces[5]['Label'].must_equal "exit"
|
|
84
|
+
|
|
85
|
+
# Validate the existence of the response header
|
|
86
|
+
r.headers.key?('X-Trace').must_equal true
|
|
87
|
+
r.headers['X-Trace'].must_equal traces[5]['X-Trace']
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
it "should trace a request with an exception" do
|
|
92
|
+
@app = GrapeSimple
|
|
93
|
+
|
|
94
|
+
begin
|
|
95
|
+
r = get "/break"
|
|
96
|
+
rescue Exception => e
|
|
97
|
+
# Do not handle/raise this error so
|
|
98
|
+
# we can continue to test
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
traces = get_all_traces
|
|
102
|
+
traces.count.must_equal 6
|
|
103
|
+
|
|
104
|
+
validate_outer_layers(traces, 'rack')
|
|
105
|
+
|
|
106
|
+
traces[2]['Layer'].must_equal "grape"
|
|
107
|
+
traces[3]['Layer'].must_equal "grape"
|
|
108
|
+
traces[3].has_key?('Controller').must_equal true
|
|
109
|
+
traces[3].has_key?('Action').must_equal true
|
|
110
|
+
traces[4]['Label'].must_equal "error"
|
|
111
|
+
traces[4]['ErrorClass'].must_equal "Exception"
|
|
112
|
+
traces[4]['ErrorMsg'].must_equal "This should have http status code 500!"
|
|
113
|
+
traces[5]['Label'].must_equal "exit"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "should trace a request with an error" do
|
|
117
|
+
@app = GrapeSimple
|
|
118
|
+
|
|
119
|
+
r = get "/error"
|
|
120
|
+
|
|
121
|
+
traces = get_all_traces
|
|
122
|
+
traces.count.must_equal 6
|
|
123
|
+
|
|
124
|
+
r.status.must_equal 500
|
|
125
|
+
r.headers.key?('X-Trace').must_equal true
|
|
126
|
+
|
|
127
|
+
validate_outer_layers(traces, 'rack')
|
|
128
|
+
|
|
129
|
+
traces[0]['Layer'].must_equal "rack"
|
|
130
|
+
traces[2]['Layer'].must_equal "grape"
|
|
131
|
+
traces[3]['Layer'].must_equal "grape"
|
|
132
|
+
traces[3].has_key?('Controller').must_equal true
|
|
133
|
+
traces[3].has_key?('Action').must_equal true
|
|
134
|
+
traces[4]['Label'].must_equal "error"
|
|
135
|
+
traces[4]['ErrorClass'].must_equal "GrapeError"
|
|
136
|
+
traces[4]['ErrorMsg'].must_equal "This is a error with 'error'!"
|
|
137
|
+
traces[5]['Layer'].must_equal "rack"
|
|
138
|
+
traces[5]['Label'].must_equal "exit"
|
|
139
|
+
traces[5]['Status'].must_equal 500
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|