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,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
@@ -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
@@ -0,0 +1,30 @@
1
+ if RUBY_VERSION >= '1.9.3'
2
+ require "minitest_helper"
3
+ require File.expand_path(File.dirname(__FILE__) + '/apps/padrino_simple')
4
+
5
+ describe Padrino do
6
+ before do
7
+ clear_all_traces
8
+ end
9
+
10
+ it "should trace a request to a simple padrino stack" do
11
+ @app = SimpleDemo
12
+
13
+ r = get "/render"
14
+
15
+ traces = get_all_traces
16
+
17
+ traces.count.must_equal 9
18
+ valid_edges?(traces).must_equal true
19
+ validate_outer_layers(traces, 'rack')
20
+
21
+ traces[2]['Layer'].must_equal "padrino"
22
+ traces[7]['Controller'].must_equal "SimpleDemo"
23
+ traces[8]['Label'].must_equal "exit"
24
+
25
+ # Validate the existence of the response header
26
+ r.headers.key?('X-Trace').must_equal true
27
+ r.headers['X-Trace'].must_equal traces[8]['X-Trace']
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ require "minitest_helper"
2
+ require File.expand_path(File.dirname(__FILE__) + '/apps/sinatra_simple')
3
+
4
+ describe Sinatra do
5
+ before do
6
+ clear_all_traces
7
+ end
8
+
9
+ it "should trace a request to a simple sinatra stack" do
10
+ @app = SinatraSimple
11
+
12
+ r = get "/render"
13
+
14
+ traces = get_all_traces
15
+
16
+ traces.count.must_equal 9
17
+ valid_edges?(traces).must_equal true
18
+ validate_outer_layers(traces, 'rack')
19
+
20
+ traces[2]['Layer'].must_equal "sinatra"
21
+ traces[4]['Label'].must_equal "profile_entry"
22
+ traces[7]['Controller'].must_equal "SinatraSimple"
23
+ traces[8]['Label'].must_equal "exit"
24
+
25
+ # Validate the existence of the response header
26
+ r.headers.key?('X-Trace').must_equal true
27
+ r.headers['X-Trace'].must_equal traces[8]['X-Trace']
28
+ end
29
+ end
30
+
@@ -0,0 +1,380 @@
1
+ require 'minitest_helper'
2
+
3
+ # The cassandra-rb client doesn't support JRuby
4
+ # https://github.com/cassandra-rb/cassandra
5
+ unless defined?(JRUBY_VERSION)
6
+ describe "Cassandra" do
7
+ before do
8
+ clear_all_traces
9
+
10
+ @client = Cassandra.new("system", "127.0.0.1:9160", { :timeout => 10 })
11
+ @client.disable_node_auto_discovery!
12
+
13
+ @ks_name = "AppNetaCassandraTest"
14
+
15
+ ks_def = CassandraThrift::KsDef.new(:name => @ks_name,
16
+ :strategy_class => "SimpleStrategy",
17
+ :strategy_options => { 'replication_factor' => '2' },
18
+ :cf_defs => [])
19
+
20
+ @client.add_keyspace(ks_def) unless @client.keyspaces.include? @ks_name
21
+ @client.keyspace = @ks_name
22
+
23
+ unless @client.column_families.include? "Users"
24
+ cf_def = CassandraThrift::CfDef.new(:keyspace => @ks_name, :name => "Users")
25
+ @client.add_column_family(cf_def)
26
+ end
27
+
28
+ unless @client.column_families.include? "Statuses"
29
+ cf_def = CassandraThrift::CfDef.new(:keyspace => @ks_name, :name => "Statuses")
30
+ @client.add_column_family(cf_def)
31
+ end
32
+
33
+ # These are standard entry/exit KVs that are passed up with all mongo operations
34
+ @entry_kvs = {
35
+ 'Layer' => 'cassandra',
36
+ 'Label' => 'entry',
37
+ 'RemoteHost' => '127.0.0.1',
38
+ 'RemotePort' => '9160' }
39
+
40
+ @exit_kvs = { 'Layer' => 'cassandra', 'Label' => 'exit' }
41
+ @collect_backtraces = TraceView::Config[:cassandra][:collect_backtraces]
42
+ end
43
+
44
+ after do
45
+ TraceView::Config[:cassandra][:collect_backtraces] = @collect_backtraces
46
+ @client.disconnect!
47
+ end
48
+
49
+ it 'Stock Cassandra should be loaded, defined and ready' do
50
+ defined?(::Cassandra).wont_match nil
51
+ end
52
+
53
+ it 'Cassandra should have traceview methods defined' do
54
+ [ :insert, :remove, :count_columns, :get_columns, :multi_get_columns, :get,
55
+ :multi_get, :get_range_single, :get_range_batch, :get_indexed_slices,
56
+ :create_index, :drop_index, :add_column_family, :drop_column_family,
57
+ :add_keyspace, :drop_keyspace ].each do |m|
58
+ ::Cassandra.method_defined?("#{m}_with_traceview").must_equal true
59
+ end
60
+ # Special 'exists?' case
61
+ ::Cassandra.method_defined?("exists_with_traceview?").must_equal true
62
+ end
63
+
64
+ it 'should trace insert' do
65
+ TraceView::API.start_trace('cassandra_test', '', {}) do
66
+ user = {'screen_name' => 'larry', "blah" => "ok"}
67
+ @client.insert(:Users, '5', user, { :ttl => 600, :consistency => 1})
68
+ end
69
+
70
+ traces = get_all_traces
71
+
72
+ traces.count.must_equal 4
73
+ validate_outer_layers(traces, 'cassandra_test')
74
+
75
+ validate_event_keys(traces[1], @entry_kvs)
76
+ traces[1]['Op'].must_equal "insert"
77
+ traces[1]['Cf'].must_equal "Users"
78
+ traces[1]['Key'].must_equal "\"5\""
79
+ traces[1]['Consistency'].must_equal 1
80
+ traces[1]['Ttl'].must_equal 600
81
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
82
+ validate_event_keys(traces[2], @exit_kvs)
83
+ end
84
+
85
+ it 'should trace remove' do
86
+ TraceView::API.start_trace('cassandra_test', '', {}) do
87
+ @client.remove(:Users, '5', 'blah')
88
+ end
89
+
90
+ traces = get_all_traces
91
+
92
+ traces.count.must_equal 4
93
+ validate_outer_layers(traces, 'cassandra_test')
94
+
95
+ validate_event_keys(traces[1], @entry_kvs)
96
+ traces[1]['Op'].must_equal "remove"
97
+ traces[1]['Cf'].must_equal "Users"
98
+ traces[1]['Key'].must_equal "\"5\""
99
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
100
+ validate_event_keys(traces[2], @exit_kvs)
101
+ end
102
+
103
+ it 'should trace count_columns' do
104
+ @client.insert(:Statuses, '12', {'body' => 'v1', 'user' => 'v2'})
105
+
106
+ TraceView::API.start_trace('cassandra_test', '', {}) do
107
+ @client.count_columns(:Statuses, '12', :count => 50)
108
+ end
109
+
110
+ traces = get_all_traces
111
+
112
+ traces.count.must_equal 4
113
+ validate_outer_layers(traces, 'cassandra_test')
114
+
115
+ validate_event_keys(traces[1], @entry_kvs)
116
+ traces[1]['Op'].must_equal "count_columns"
117
+ traces[1]['Cf'].must_equal "Statuses"
118
+ traces[1]['Key'].must_equal "\"12\""
119
+ traces[1]['Count'].must_equal 50
120
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
121
+ validate_event_keys(traces[2], @exit_kvs)
122
+ end
123
+
124
+ it 'should trace get_columns' do
125
+ TraceView::API.start_trace('cassandra_test', '', {}) do
126
+ @client.get_columns(:Statuses, '12', ['body'])
127
+ end
128
+
129
+ traces = get_all_traces
130
+
131
+ traces.count.must_equal 4
132
+ validate_outer_layers(traces, 'cassandra_test')
133
+
134
+ validate_event_keys(traces[1], @entry_kvs)
135
+ traces[1]['Op'].must_equal "get_columns"
136
+ traces[1]['Cf'].must_equal "Statuses"
137
+ traces[1]['Key'].must_equal "\"12\""
138
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
139
+ validate_event_keys(traces[2], @exit_kvs)
140
+ end
141
+
142
+ it 'should trace multi_get_columns' do
143
+ TraceView::API.start_trace('cassandra_test', '', {}) do
144
+ @client.multi_get_columns(:Users, ['12', '5'], ['body'])
145
+ end
146
+
147
+ traces = get_all_traces
148
+
149
+ traces.count.must_equal 4
150
+ validate_outer_layers(traces, 'cassandra_test')
151
+
152
+ validate_event_keys(traces[1], @entry_kvs)
153
+ traces[1]['Op'].must_equal "multi_get_columns"
154
+ traces[1]['Cf'].must_equal "Users"
155
+ traces[1]['Key'].must_equal "[\"12\", \"5\"]"
156
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
157
+ validate_event_keys(traces[2], @exit_kvs)
158
+ end
159
+
160
+ it 'should trace get' do
161
+ TraceView::API.start_trace('cassandra_test', '', {}) do
162
+ @client.get(:Statuses, '12', :reversed => true)
163
+ end
164
+
165
+ traces = get_all_traces
166
+
167
+ traces.count.must_equal 4
168
+ validate_outer_layers(traces, 'cassandra_test')
169
+
170
+ validate_event_keys(traces[1], @entry_kvs)
171
+ traces[1]['Op'].must_equal "get"
172
+ traces[1]['Cf'].must_equal "Statuses"
173
+ traces[1]['Key'].must_equal "\"12\""
174
+ traces[1]['Reversed'].must_equal "true"
175
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
176
+ validate_event_keys(traces[2], @exit_kvs)
177
+ end
178
+
179
+ it 'should trace exists?' do
180
+ TraceView::API.start_trace('cassandra_test', '', {}) do
181
+ @client.exists?(:Statuses, '12')
182
+ @client.exists?(:Statuses, '12', 'body')
183
+ end
184
+
185
+ traces = get_all_traces
186
+
187
+ traces.count.must_equal 6
188
+ validate_outer_layers(traces, 'cassandra_test')
189
+
190
+ validate_event_keys(traces[1], @entry_kvs)
191
+ traces[1]['Op'].must_equal "exists?"
192
+ traces[1]['Cf'].must_equal "Statuses"
193
+ traces[1]['Key'].must_equal "\"12\""
194
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
195
+ validate_event_keys(traces[2], @exit_kvs)
196
+
197
+ traces[3]['Op'].must_equal "exists?"
198
+ traces[3]['Cf'].must_equal "Statuses"
199
+ traces[3]['Key'].must_equal "\"12\""
200
+ traces[3].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
201
+ end
202
+
203
+ it 'should trace get_range_keys' do
204
+ TraceView::API.start_trace('cassandra_test', '', {}) do
205
+ @client.get_range_keys(:Statuses, :key_count => 4)
206
+ end
207
+
208
+ traces = get_all_traces
209
+
210
+ traces.count.must_equal 4
211
+ validate_outer_layers(traces, 'cassandra_test')
212
+
213
+ validate_event_keys(traces[1], @entry_kvs)
214
+ traces[1]['Op'].must_equal "get_range_batch"
215
+ traces[1]['Cf'].must_equal "Statuses"
216
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
217
+ validate_event_keys(traces[2], @exit_kvs)
218
+ end
219
+
220
+ it 'should trace create_index' do
221
+ TraceView::API.start_trace('cassandra_test', '', {}) do
222
+ @client.create_index(@ks_name, 'Statuses', 'column_name', 'LongType')
223
+ end
224
+
225
+ traces = get_all_traces
226
+
227
+ traces.count.must_equal 4
228
+ validate_outer_layers(traces, 'cassandra_test')
229
+
230
+ validate_event_keys(traces[1], @entry_kvs)
231
+ traces[1]['Op'].must_equal "create_index"
232
+ traces[1]['Cf'].must_equal "Statuses"
233
+ traces[1]['Keyspace'].must_equal @ks_name
234
+ traces[1]['Column_name'].must_equal "column_name"
235
+ traces[1]['Validation_class'].must_equal "LongType"
236
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
237
+ validate_event_keys(traces[2], @exit_kvs)
238
+
239
+ # Clean up
240
+ @client.drop_index(@ks_name, 'Statuses', 'column_name')
241
+ end
242
+
243
+ it 'should trace drop_index' do
244
+ # Prep
245
+ @client.create_index(@ks_name, 'Statuses', 'column_name', 'LongType')
246
+
247
+ TraceView::API.start_trace('cassandra_test', '', {}) do
248
+ @client.drop_index(@ks_name, 'Statuses', 'column_name')
249
+ end
250
+
251
+ traces = get_all_traces
252
+
253
+ traces.count.must_equal 4
254
+ validate_outer_layers(traces, 'cassandra_test')
255
+
256
+ validate_event_keys(traces[1], @entry_kvs)
257
+ traces[1]['Op'].must_equal "drop_index"
258
+ traces[1]['Cf'].must_equal "Statuses"
259
+ traces[1]['Keyspace'].must_equal @ks_name
260
+ traces[1]['Column_name'].must_equal "column_name"
261
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
262
+ validate_event_keys(traces[2], @exit_kvs)
263
+ end
264
+
265
+ it 'should trace get_indexed_slices' do
266
+ @client.create_index(@ks_name, 'Statuses', 'x', 'LongType')
267
+ TraceView::API.start_trace('cassandra_test', '', {}) do
268
+ expressions = [
269
+ { :column_name => 'x',
270
+ :value => [0,20].pack("NN"),
271
+ :comparison => "=="},
272
+ { :column_name => 'non_indexed',
273
+ :value => [5].pack("N*"),
274
+ :comparison => ">"} ]
275
+ @client.get_indexed_slices(:Statuses, expressions).length
276
+ end
277
+
278
+ traces = get_all_traces
279
+
280
+ traces.count.must_equal 4
281
+ validate_outer_layers(traces, 'cassandra_test')
282
+
283
+ validate_event_keys(traces[1], @entry_kvs)
284
+ traces[1]['Op'].must_equal "get_indexed_slices"
285
+ traces[1]['Cf'].must_equal "Statuses"
286
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
287
+ validate_event_keys(traces[2], @exit_kvs)
288
+ end
289
+
290
+ it 'should trace add and remove of column family' do
291
+ cf_name = (0...10).map{ ('a'..'z').to_a[rand(26)] }.join
292
+ cf_def = CassandraThrift::CfDef.new(:keyspace => @ks_name, :name => cf_name)
293
+
294
+ TraceView::API.start_trace('cassandra_test', '', {}) do
295
+ @client.add_column_family(cf_def)
296
+ @client.drop_column_family(cf_name)
297
+ end
298
+
299
+ traces = get_all_traces
300
+
301
+ traces.count.must_equal 6
302
+ validate_outer_layers(traces, 'cassandra_test')
303
+
304
+ validate_event_keys(traces[1], @entry_kvs)
305
+ traces[1]['Op'].must_equal "add_column_family"
306
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
307
+ validate_event_keys(traces[2], @exit_kvs)
308
+
309
+ traces[3]['Op'].must_equal "drop_column_family"
310
+ traces[3]['Cf'].must_equal cf_name
311
+ traces[3].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
312
+ end
313
+
314
+ it 'should trace adding a keyspace' do
315
+ ks_name = (0...10).map{ ('a'..'z').to_a[rand(26)] }.join
316
+ ks_def = CassandraThrift::KsDef.new(:name => ks_name,
317
+ :strategy_class => "org.apache.cassandra.locator.SimpleStrategy",
318
+ :strategy_options => { 'replication_factor' => '2' },
319
+ :cf_defs => [])
320
+
321
+ TraceView::API.start_trace('cassandra_test', '', {}) do
322
+ @client.add_keyspace(ks_def)
323
+ @client.keyspace = ks_name
324
+ end
325
+
326
+ traces = get_all_traces
327
+
328
+ traces.count.must_equal 4
329
+ validate_outer_layers(traces, 'cassandra_test')
330
+
331
+ validate_event_keys(traces[1], @entry_kvs)
332
+ traces[1]['Op'].must_equal "add_keyspace"
333
+ traces[1]['Name'].must_equal ks_name
334
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
335
+ validate_event_keys(traces[2], @exit_kvs)
336
+ end
337
+
338
+ it 'should trace the removal of a keyspace' do
339
+ TraceView::API.start_trace('cassandra_test', '', {}) do
340
+ @client.drop_keyspace(@ks_name)
341
+ end
342
+
343
+ traces = get_all_traces
344
+
345
+ traces.count.must_equal 4
346
+ validate_outer_layers(traces, 'cassandra_test')
347
+
348
+ validate_event_keys(traces[1], @entry_kvs)
349
+ traces[1]['Op'].must_equal "drop_keyspace"
350
+ traces[1]['Name'].must_equal @ks_name
351
+ traces[1].has_key?('Backtrace').must_equal TraceView::Config[:cassandra][:collect_backtraces]
352
+ validate_event_keys(traces[2], @exit_kvs)
353
+ end
354
+
355
+ it "should obey :collect_backtraces setting when true" do
356
+ TraceView::Config[:cassandra][:collect_backtraces] = true
357
+
358
+ TraceView::API.start_trace('cassandra_test', '', {}) do
359
+ user = {'screen_name' => 'larry', "blah" => "ok"}
360
+ @client.insert(:Users, '5', user, { :ttl => 600, :consistency => 1})
361
+ end
362
+
363
+ traces = get_all_traces
364
+ layer_has_key(traces, 'cassandra', 'Backtrace')
365
+ end
366
+
367
+ it "should obey :collect_backtraces setting when false" do
368
+ TraceView::Config[:cassandra][:collect_backtraces] = false
369
+
370
+ TraceView::API.start_trace('cassandra_test', '', {}) do
371
+ user = {'screen_name' => 'larry', "blah" => "ok"}
372
+ @client.insert(:Users, '5', user, { :ttl => 600, :consistency => 1})
373
+ end
374
+
375
+ traces = get_all_traces
376
+ layer_doesnt_have_key(traces, 'cassandra', 'Backtrace')
377
+ end
378
+
379
+ end
380
+ end