traceview 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +58 -0
  5. data/Appraisals +10 -0
  6. data/CHANGELOG.md +490 -0
  7. data/CONFIG.md +16 -0
  8. data/Gemfile +95 -0
  9. data/LICENSE +199 -0
  10. data/README.md +380 -0
  11. data/Rakefile +109 -0
  12. data/examples/DNT.md +35 -0
  13. data/examples/carrying_context.rb +225 -0
  14. data/examples/instrumenting_metal_controller.rb +8 -0
  15. data/examples/puma_on_heroku_config.rb +17 -0
  16. data/examples/tracing_async_threads.rb +125 -0
  17. data/examples/tracing_background_jobs.rb +52 -0
  18. data/examples/tracing_forked_processes.rb +100 -0
  19. data/examples/unicorn_on_heroku_config.rb +28 -0
  20. data/ext/oboe_metal/extconf.rb +61 -0
  21. data/ext/oboe_metal/noop/noop.c +7 -0
  22. data/ext/oboe_metal/src/bson/bson.h +221 -0
  23. data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
  24. data/ext/oboe_metal/src/oboe.h +275 -0
  25. data/ext/oboe_metal/src/oboe.hpp +352 -0
  26. data/ext/oboe_metal/src/oboe_wrap.cxx +3886 -0
  27. data/ext/oboe_metal/tests/test.rb +11 -0
  28. data/gemfiles/mongo.gemfile +33 -0
  29. data/gemfiles/moped.gemfile +33 -0
  30. data/get_version.rb +5 -0
  31. data/init.rb +4 -0
  32. data/lib/joboe_metal.rb +206 -0
  33. data/lib/oboe.rb +7 -0
  34. data/lib/oboe/README +2 -0
  35. data/lib/oboe/backward_compatibility.rb +59 -0
  36. data/lib/oboe/inst/rack.rb +11 -0
  37. data/lib/oboe_metal.rb +151 -0
  38. data/lib/rails/generators/traceview/install_generator.rb +76 -0
  39. data/lib/rails/generators/traceview/templates/traceview_initializer.rb +159 -0
  40. data/lib/traceview.rb +62 -0
  41. data/lib/traceview/api.rb +18 -0
  42. data/lib/traceview/api/layerinit.rb +51 -0
  43. data/lib/traceview/api/logging.rb +209 -0
  44. data/lib/traceview/api/memcache.rb +31 -0
  45. data/lib/traceview/api/profiling.rb +50 -0
  46. data/lib/traceview/api/tracing.rb +135 -0
  47. data/lib/traceview/api/util.rb +121 -0
  48. data/lib/traceview/base.rb +225 -0
  49. data/lib/traceview/config.rb +238 -0
  50. data/lib/traceview/frameworks/grape.rb +97 -0
  51. data/lib/traceview/frameworks/padrino.rb +64 -0
  52. data/lib/traceview/frameworks/padrino/templates.rb +58 -0
  53. data/lib/traceview/frameworks/rails.rb +145 -0
  54. data/lib/traceview/frameworks/rails/helpers/rum/rum_ajax_header.js.erb +5 -0
  55. data/lib/traceview/frameworks/rails/helpers/rum/rum_footer.js.erb +1 -0
  56. data/lib/traceview/frameworks/rails/helpers/rum/rum_header.js.erb +3 -0
  57. data/lib/traceview/frameworks/rails/inst/action_controller.rb +216 -0
  58. data/lib/traceview/frameworks/rails/inst/action_view.rb +56 -0
  59. data/lib/traceview/frameworks/rails/inst/action_view_2x.rb +54 -0
  60. data/lib/traceview/frameworks/rails/inst/action_view_30.rb +48 -0
  61. data/lib/traceview/frameworks/rails/inst/active_record.rb +24 -0
  62. data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
  63. data/lib/traceview/frameworks/rails/inst/connection_adapters/mysql2.rb +28 -0
  64. data/lib/traceview/frameworks/rails/inst/connection_adapters/oracle.rb +18 -0
  65. data/lib/traceview/frameworks/rails/inst/connection_adapters/postgresql.rb +30 -0
  66. data/lib/traceview/frameworks/rails/inst/connection_adapters/utils.rb +117 -0
  67. data/lib/traceview/frameworks/sinatra.rb +95 -0
  68. data/lib/traceview/frameworks/sinatra/templates.rb +56 -0
  69. data/lib/traceview/inst/cassandra.rb +279 -0
  70. data/lib/traceview/inst/dalli.rb +86 -0
  71. data/lib/traceview/inst/em-http-request.rb +99 -0
  72. data/lib/traceview/inst/excon.rb +111 -0
  73. data/lib/traceview/inst/faraday.rb +73 -0
  74. data/lib/traceview/inst/http.rb +87 -0
  75. data/lib/traceview/inst/httpclient.rb +173 -0
  76. data/lib/traceview/inst/memcache.rb +102 -0
  77. data/lib/traceview/inst/memcached.rb +94 -0
  78. data/lib/traceview/inst/mongo.rb +238 -0
  79. data/lib/traceview/inst/moped.rb +474 -0
  80. data/lib/traceview/inst/rack.rb +122 -0
  81. data/lib/traceview/inst/redis.rb +271 -0
  82. data/lib/traceview/inst/resque.rb +192 -0
  83. data/lib/traceview/inst/rest-client.rb +38 -0
  84. data/lib/traceview/inst/sequel.rb +162 -0
  85. data/lib/traceview/inst/typhoeus.rb +102 -0
  86. data/lib/traceview/instrumentation.rb +21 -0
  87. data/lib/traceview/loading.rb +94 -0
  88. data/lib/traceview/logger.rb +41 -0
  89. data/lib/traceview/method_profiling.rb +84 -0
  90. data/lib/traceview/ruby.rb +36 -0
  91. data/lib/traceview/support.rb +113 -0
  92. data/lib/traceview/thread_local.rb +26 -0
  93. data/lib/traceview/util.rb +250 -0
  94. data/lib/traceview/version.rb +16 -0
  95. data/lib/traceview/xtrace.rb +90 -0
  96. data/test/frameworks/apps/grape_nested.rb +30 -0
  97. data/test/frameworks/apps/grape_simple.rb +24 -0
  98. data/test/frameworks/apps/padrino_simple.rb +45 -0
  99. data/test/frameworks/apps/sinatra_simple.rb +24 -0
  100. data/test/frameworks/grape_test.rb +142 -0
  101. data/test/frameworks/padrino_test.rb +30 -0
  102. data/test/frameworks/sinatra_test.rb +30 -0
  103. data/test/instrumentation/cassandra_test.rb +380 -0
  104. data/test/instrumentation/dalli_test.rb +171 -0
  105. data/test/instrumentation/em_http_request_test.rb +86 -0
  106. data/test/instrumentation/excon_test.rb +207 -0
  107. data/test/instrumentation/faraday_test.rb +235 -0
  108. data/test/instrumentation/http_test.rb +140 -0
  109. data/test/instrumentation/httpclient_test.rb +296 -0
  110. data/test/instrumentation/memcache_test.rb +251 -0
  111. data/test/instrumentation/memcached_test.rb +226 -0
  112. data/test/instrumentation/mongo_test.rb +462 -0
  113. data/test/instrumentation/moped_test.rb +496 -0
  114. data/test/instrumentation/rack_test.rb +116 -0
  115. data/test/instrumentation/redis_hashes_test.rb +265 -0
  116. data/test/instrumentation/redis_keys_test.rb +318 -0
  117. data/test/instrumentation/redis_lists_test.rb +310 -0
  118. data/test/instrumentation/redis_misc_test.rb +160 -0
  119. data/test/instrumentation/redis_sets_test.rb +293 -0
  120. data/test/instrumentation/redis_sortedsets_test.rb +325 -0
  121. data/test/instrumentation/redis_strings_test.rb +333 -0
  122. data/test/instrumentation/resque_test.rb +62 -0
  123. data/test/instrumentation/rest-client_test.rb +294 -0
  124. data/test/instrumentation/sequel_mysql2_test.rb +326 -0
  125. data/test/instrumentation/sequel_mysql_test.rb +326 -0
  126. data/test/instrumentation/sequel_pg_test.rb +330 -0
  127. data/test/instrumentation/typhoeus_test.rb +285 -0
  128. data/test/minitest_helper.rb +187 -0
  129. data/test/profiling/method_test.rb +198 -0
  130. data/test/servers/rackapp_8101.rb +22 -0
  131. data/test/support/backcompat_test.rb +269 -0
  132. data/test/support/config_test.rb +128 -0
  133. data/test/support/dnt_test.rb +73 -0
  134. data/test/support/liboboe_settings_test.rb +104 -0
  135. data/test/support/xtrace_test.rb +35 -0
  136. data/traceview.gemspec +29 -0
  137. metadata +250 -0
@@ -0,0 +1,84 @@
1
+ # Copyright (c) 2013 AppNeta, Inc.
2
+ # All rights reserved.
3
+
4
+ ##
5
+ # Provides the methods necessary for method profiling. Profiling
6
+ # results are sent to the TraceView dashboard.
7
+ #
8
+ # Example usage:
9
+ # class MyApp
10
+ # include TraceViewMethodProfiling
11
+ #
12
+ # def process_request()
13
+ # # The hard work
14
+ # end
15
+ #
16
+ # # call syntax: profile_method <method>, <profile_name>
17
+ # profile_method :process_request, 'request_processor'
18
+ # end
19
+ module TraceViewMethodProfiling
20
+ def self.included(klass)
21
+ klass.extend ClassMethods
22
+ end
23
+
24
+ module ClassMethods
25
+ def profile_method(method_name, profile_name, store_args = false, store_return = false, *_)
26
+ begin
27
+ # this only gets file and line where profiling is turned on, presumably
28
+ # right after the function definition. ruby 1.9 and 2.0 has nice introspection (Method.source_location)
29
+ # but its appears no such luck for ruby 1.8
30
+ file = ''
31
+ line = ''
32
+ if RUBY_VERSION >= '1.9'
33
+ info = instance_method(method_name).source_location
34
+ unless info.nil?
35
+ file = info[0].to_s
36
+ line = info[1].to_s
37
+ end
38
+ else
39
+ info = Kernel.caller[0].split(':')
40
+ file = info.first.to_s
41
+ line = info.last.to_s
42
+ end
43
+
44
+ # Safety: Make sure there are no quotes or double quotes to break the class_eval
45
+ file = file.gsub(/[\'\"]/, '')
46
+ line = line.gsub(/[\'\"]/, '')
47
+
48
+ # profiling via ruby-prof, is it possible to get return value of profiled code?
49
+ code = "def _traceview_profiled_#{method_name}(*args, &block)
50
+ entry_kvs = {}
51
+ entry_kvs['Language'] = 'ruby'
52
+ entry_kvs['ProfileName'] = '#{TraceView::Util.prettify(profile_name)}'
53
+ entry_kvs['FunctionName'] = '#{TraceView::Util.prettify(method_name)}'
54
+ entry_kvs['File'] = '#{file}'
55
+ entry_kvs['LineNumber'] = '#{line}'
56
+ entry_kvs['Args'] = TraceView::API.pps(*args) if #{store_args}
57
+ entry_kvs.merge!(::TraceView::API.get_class_name(self))
58
+
59
+ TraceView::API.log(nil, 'profile_entry', entry_kvs)
60
+
61
+ ret = _traceview_orig_#{method_name}(*args, &block)
62
+
63
+ exit_kvs = {}
64
+ exit_kvs['Language'] = 'ruby'
65
+ exit_kvs['ProfileName'] = '#{TraceView::Util.prettify(profile_name)}'
66
+ exit_kvs['ReturnValue'] = TraceView::API.pps(ret) if #{store_return}
67
+
68
+ TraceView::API.log(nil, 'profile_exit', exit_kvs)
69
+ ret
70
+ end"
71
+ rescue => e
72
+ TraceView.logger.warn "[traceview/warn] profile_method: #{e.inspect}"
73
+ end
74
+
75
+ begin
76
+ class_eval code, __FILE__, __LINE__
77
+ alias_method "_traceview_orig_#{method_name}", method_name
78
+ alias_method method_name, "_traceview_profiled_#{method_name}"
79
+ rescue => e
80
+ TraceView.logger.warn "[traceview/warn] Fatal error profiling method (#{method_name}): #{e.inspect}" if TraceView::Config[:verbose]
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2013 AppNeta, Inc.
2
+ # All rights reserved.
3
+
4
+ module TraceView
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 TraceView.loaded
26
+ TraceView::Loading.load_access_key
27
+ TraceView::Inst.load_instrumentation
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ if TraceView.loaded and !TraceView.framework?
35
+ ::TraceView::Ruby.load
36
+ end
@@ -0,0 +1,113 @@
1
+ # Copyright (c) 2014 AppNeta, Inc.
2
+ # All rights reserved.
3
+
4
+ require 'rbconfig'
5
+
6
+ module TraceView
7
+ ##
8
+ # This module is used to debug problematic setups and/or environments.
9
+ # Depending on the environment, output may be to stdout or the framework
10
+ # log file (e.g. log/production.log)
11
+
12
+ ##
13
+ # yesno
14
+ #
15
+ # Utility method to translate value/nil to "yes"/"no" strings
16
+ def self.yesno(x)
17
+ x ? "yes" : "no"
18
+ end
19
+
20
+ def self.support_report
21
+ TraceView.logger.warn "********************************************************"
22
+ TraceView.logger.warn "* BEGIN TraceView Support Report"
23
+ TraceView.logger.warn "* Please email the output of this report to traceviewsupport@appneta.com"
24
+ TraceView.logger.warn "********************************************************"
25
+ TraceView.logger.warn "Ruby: #{RUBY_DESCRIPTION}"
26
+ TraceView.logger.warn "$0: #{$0}"
27
+ TraceView.logger.warn "$1: #{$1}" unless $1.nil?
28
+ TraceView.logger.warn "$2: #{$2}" unless $2.nil?
29
+ TraceView.logger.warn "$3: #{$3}" unless $3.nil?
30
+ TraceView.logger.warn "$4: #{$4}" unless $4.nil?
31
+ TraceView.logger.warn "TraceView.loaded == #{TraceView.loaded}"
32
+
33
+ using_jruby = defined?(JRUBY_VERSION)
34
+ TraceView.logger.warn "Using JRuby?: #{yesno(using_jruby)}"
35
+ if using_jruby
36
+ TraceView.logger.warn "Jtraceview Agent Status: #{Java::ComTracelyticsAgent::Agent.getStatus}"
37
+ end
38
+
39
+ on_heroku = TraceView.heroku?
40
+ TraceView.logger.warn "On Heroku?: #{yesno(on_heroku)}"
41
+ if on_heroku
42
+ TraceView.logger.warn "TRACEVIEW_URL: #{ENV['TRACEVIEW_URL']}"
43
+ end
44
+
45
+ TraceView.logger.warn "TraceView::Ruby defined?: #{yesno(defined?(TraceView::Ruby))}"
46
+ TraceView.logger.warn "TraceView.reporter: #{TraceView.reporter}"
47
+
48
+ TraceView.logger.warn "********************************************************"
49
+ TraceView.logger.warn "* Frameworks"
50
+ TraceView.logger.warn "********************************************************"
51
+
52
+ using_rails = defined?(::Rails)
53
+ TraceView.logger.warn "Using Rails?: #{yesno(using_rails)}"
54
+ if using_rails
55
+ TraceView.logger.warn "TraceView::Rails loaded?: #{yesno(defined?(::TraceView::Rails))}"
56
+ end
57
+
58
+ using_sinatra = defined?(::Sinatra)
59
+ TraceView.logger.warn "Using Sinatra?: #{yesno(using_sinatra)}"
60
+
61
+ using_padrino = defined?(::Padrino)
62
+ TraceView.logger.warn "Using Padrino?: #{yesno(using_padrino)}"
63
+
64
+ using_grape = defined?(::Grape)
65
+ TraceView.logger.warn "Using Grape?: #{yesno(using_grape)}"
66
+
67
+ TraceView.logger.warn "********************************************************"
68
+ TraceView.logger.warn "* TraceView Libraries"
69
+ TraceView.logger.warn "********************************************************"
70
+ files = Dir.glob('/usr/lib/liboboe*')
71
+ if files.empty?
72
+ TraceView.logger.warn "Error: No liboboe libs!"
73
+ else
74
+ files.each { |f|
75
+ TraceView.logger.warn f
76
+ }
77
+ end
78
+
79
+ TraceView.logger.warn "********************************************************"
80
+ TraceView.logger.warn "* TraceView::Config Values"
81
+ TraceView.logger.warn "********************************************************"
82
+ TraceView::Config.show.each { |k,v|
83
+ TraceView.logger.warn "#{k}: #{v}"
84
+ }
85
+
86
+ TraceView.logger.warn "********************************************************"
87
+ TraceView.logger.warn "* OS, Platform + Env"
88
+ TraceView.logger.warn "********************************************************"
89
+ TraceView.logger.warn RbConfig::CONFIG['host_os']
90
+ TraceView.logger.warn RbConfig::CONFIG['sitearch']
91
+ TraceView.logger.warn RbConfig::CONFIG['arch']
92
+ TraceView.logger.warn RUBY_PLATFORM
93
+ TraceView.logger.warn "RACK_ENV: #{ENV['RACK_ENV']}"
94
+ TraceView.logger.warn "RAILS_ENV: #{ENV['RAILS_ENV']}" if using_rails
95
+
96
+ TraceView.logger.warn "********************************************************"
97
+ TraceView.logger.warn "* Raw __Init KVs"
98
+ TraceView.logger.warn "********************************************************"
99
+ platform_info = TraceView::Util.build_init_report
100
+ platform_info.each { |k,v|
101
+ TraceView.logger.warn "#{k}: #{v}"
102
+ }
103
+
104
+ TraceView.logger.warn "********************************************************"
105
+ TraceView.logger.warn "* END TraceView Support Report"
106
+ TraceView.logger.warn "* Support Email: traceviewsupport@appneta.com"
107
+ TraceView.logger.warn "* Support Portal: https://support.tv.appneta.com"
108
+ TraceView.logger.warn "* Freenode IRC: #appneta"
109
+ TraceView.logger.warn "* Github: https://github.com/appneta/traceview-ruby"
110
+ TraceView.logger.warn "********************************************************"
111
+ nil
112
+ end
113
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2014 AppNeta, Inc.
2
+ # All rights reserved.
3
+
4
+ module TraceView
5
+ ##
6
+ # Provides thread local storage for TraceView.
7
+ #
8
+ # Example usage:
9
+ # module TraceViewBase
10
+ # extend ::TraceView::ThreadLocal
11
+ # thread_local :layer_op
12
+ # end
13
+ module ThreadLocal
14
+ def thread_local(name)
15
+ key = "__#{self}_#{name}__".intern
16
+
17
+ define_method(name) do
18
+ Thread.current[key]
19
+ end
20
+
21
+ define_method(name.to_s + '=') do |value|
22
+ Thread.current[key] = value
23
+ end
24
+ end
25
+ end
26
+ end
@@ -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