caliper 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +34 -0
  4. data/README.md +22 -0
  5. data/Rakefile +6 -0
  6. data/caliper.gemspec +27 -0
  7. data/lib/caliper/app_error.rb +33 -0
  8. data/lib/caliper/rack.rb +33 -0
  9. data/lib/caliper/rails/append_info.rb +25 -0
  10. data/lib/caliper/rails/instrumentation.rb +27 -0
  11. data/lib/caliper/railtie.rb +42 -0
  12. data/lib/caliper/route_inspector.rb +108 -0
  13. data/lib/caliper/tasks/caliper.rake +10 -0
  14. data/lib/caliper/tracer.rb +77 -0
  15. data/lib/caliper/version.rb +3 -0
  16. data/lib/caliper.rb +60 -0
  17. data/lib/caliper_api/http.rb +62 -0
  18. data/spec/caliper/tracer_spec.rb +64 -0
  19. data/spec/data/rails_get_request_samples.txt +9 -0
  20. data/spec/data/trace.json +1 -0
  21. data/spec/dummy/.gitignore +15 -0
  22. data/spec/dummy/Gemfile +43 -0
  23. data/spec/dummy/README.rdoc +261 -0
  24. data/spec/dummy/Rakefile +7 -0
  25. data/spec/dummy/app/assets/images/rails.png +0 -0
  26. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  27. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  28. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  29. data/spec/dummy/app/controllers/posts_controller.rb +18 -0
  30. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  31. data/spec/dummy/app/mailers/.gitkeep +0 -0
  32. data/spec/dummy/app/models/.gitkeep +0 -0
  33. data/spec/dummy/app/models/post.rb +3 -0
  34. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  35. data/spec/dummy/app/views/posts/index.html.erb +15 -0
  36. data/spec/dummy/config/application.rb +62 -0
  37. data/spec/dummy/config/boot.rb +6 -0
  38. data/spec/dummy/config/caliper.yml +17 -0
  39. data/spec/dummy/config/database.yml +25 -0
  40. data/spec/dummy/config/environment.rb +5 -0
  41. data/spec/dummy/config/environments/development.rb +37 -0
  42. data/spec/dummy/config/environments/production.rb +67 -0
  43. data/spec/dummy/config/environments/test.rb +37 -0
  44. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  45. data/spec/dummy/config/initializers/inflections.rb +15 -0
  46. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  47. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  48. data/spec/dummy/config/initializers/session_store.rb +8 -0
  49. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  50. data/spec/dummy/config/locales/en.yml +5 -0
  51. data/spec/dummy/config/routes.rb +8 -0
  52. data/spec/dummy/config.ru +4 -0
  53. data/spec/dummy/db/migrate/20121203233624_create_posts.rb +9 -0
  54. data/spec/dummy/db/schema.rb +22 -0
  55. data/spec/dummy/db/seeds.rb +7 -0
  56. data/spec/dummy/lib/assets/.gitkeep +0 -0
  57. data/spec/dummy/lib/tasks/.gitkeep +0 -0
  58. data/spec/dummy/log/.gitkeep +0 -0
  59. data/spec/dummy/public/404.html +26 -0
  60. data/spec/dummy/public/422.html +26 -0
  61. data/spec/dummy/public/500.html +25 -0
  62. data/spec/dummy/public/favicon.ico +0 -0
  63. data/spec/dummy/public/index.html +241 -0
  64. data/spec/dummy/public/robots.txt +5 -0
  65. data/spec/dummy/script/rails +6 -0
  66. data/spec/dummy/test/integration/.gitkeep +0 -0
  67. data/spec/dummy/test/integration/caliper_test.rb +64 -0
  68. data/spec/dummy/test/lib/caliper/route_inspector_test.rb +14 -0
  69. data/spec/dummy/test/test_helper.rb +30 -0
  70. data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
  71. data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
  72. data/spec/dummy/vendor/plugins/.gitkeep +0 -0
  73. data/spec/spec_helper.rb +32 -0
  74. metadata +270 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in caliper.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,34 @@
1
+ All other components of this product are
2
+ Copyright (c) 2012-2013 Coherence Solutions Inc. All rights reserved.
3
+
4
+ Subject to the terms of this notice, Coherence Solutions Inc grants you a
5
+ nonexclusive, nontransferable license, without the right to
6
+ sublicense, to (a) install and execute one copy of these files on any
7
+ number of workstations owned or controlled by you and (b) distribute
8
+ verbatim copies of these files to third parties. As a condition to the
9
+ foregoing grant, you must provide this notice along with each copy you
10
+ distribute and you must not remove, alter, or obscure this notice. All
11
+ other use, reproduction, modification, distribution, or other
12
+ exploitation of these files is strictly prohibited, except as may be set
13
+ forth in a separate written license agreement between you and Coherence
14
+ Solutions Inc. The terms of any such license agreement will control over this
15
+ notice. The license stated above will be automatically terminated and
16
+ revoked if you exceed its scope or violate any of the terms of this
17
+ notice.
18
+
19
+ This License does not grant permission to use the trade names,
20
+ trademarks, service marks, or product names of Coherence Solutions Inc
21
+ except as required for reasonable and customary use in describing the origin
22
+ of this file and reproducing the content of this notice. You may
23
+ not mark or brand this file with any trade name, trademarks,
24
+ servicemarks, or product names other than the original brand
25
+ (if any)provided by Coherence Solutions Inc.
26
+
27
+ Unless otherwise expressly agreed by Coherence Solutions Inc, in a
28
+ separate written license agreement, these files are provided AS IS,
29
+ WITHOUT WARRANTY OF ANY KIND, including without any implied warranties
30
+ of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, or NON-INFRINGEMENT.
31
+ As a condition to your use of these files, you are solely responsible for
32
+ such use. Coherence Solutions Inc will have no liability to you for direct,
33
+ indirect, consequential, incidental, special, or punitive damages or
34
+ for lost profits or data.
data/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # Caliper
2
+
3
+ To use this gem please register for a beta invite at
4
+ [http://caliper.io](http://caliper.io)
5
+
6
+ ## Tests
7
+
8
+ Caliper gem:
9
+
10
+ bundle exec rake
11
+
12
+ Caliper rails dummy app (could only get rack middleware working with
13
+ IntegrationTest for now):
14
+
15
+ cd spec/dummy; bundle exec rake
16
+
17
+ ## Contributing
18
+
19
+ We welcome pull requests for fixing bugs or new features. We follow the
20
+ [rails coding
21
+ conventions](http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#follow-the-coding-conventions)
22
+ as best we can.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ # TODO: sort out later
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task :default => :spec
data/caliper.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'caliper/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "caliper"
8
+ gem.version = Caliper::VERSION
9
+ gem.authors = ["Kalvir Sandhu"]
10
+ gem.email = ["kalv@coherence.io"]
11
+ gem.description = %q{Caliper your rails applications}
12
+ gem.summary = %q{Caliper profiles your rails application giving you insight on customers performance}
13
+ gem.homepage = "http://caliper.io"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^spec/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency(%q<rack>, [">=0"])
21
+ gem.add_dependency(%q<yajl-ruby>, [">= 0"])
22
+ gem.add_dependency(%q<uuid>, [">= 0"])
23
+
24
+ gem.add_development_dependency(%q<rspec>, [">= 0"])
25
+ gem.add_development_dependency(%q<activesupport>, [">= 3.2.9"])
26
+ gem.add_development_dependency(%q<rake>,[">=10.0.2"])
27
+ end
@@ -0,0 +1,33 @@
1
+ module Caliper
2
+ module AppError
3
+ def self.create(exception, request_env)
4
+ req = ::Rack::Request.new(request_env)
5
+
6
+ remote_ip = request_env["action_dispatch.remote_ip"]
7
+ remote_ip = remote_ip.first if remote_ip.is_a?(Array)
8
+
9
+ error_attributes = {
10
+ "message" => exception.message,
11
+ "class_name" => exception.class.name,
12
+ "host" => Socket.gethostname,
13
+ "request_data" => {
14
+ "http_method" => request_env["REQUEST_METHOD"],
15
+ "uri" => request_env["REQUEST_URI"],
16
+ "path" => request_env["PATH_INFO"],
17
+ "remote_ip" => "#{remote_ip}",
18
+ "parameters" => req.params,
19
+ "session_data" => req.session,
20
+ "HTTP_USER_AGENT" => request_env["HTTP_USER_AGENT"],
21
+ "HTTP_HOST" => request_env["HTTP_HOST"],
22
+ "HTTP_X_REAL_IP" => request_env["HTTP_X_REAL_IP"],
23
+ "HTTP_X_FORWARDED_FOR" => request_env["HTTP_X_FORWARDED_FOR"]
24
+ }
25
+ }
26
+ error_attributes["backtrace"] = exception.backtrace.join("\n") if exception.backtrace
27
+
28
+ CaliperApi.create_error(
29
+ Yajl::Encoder.encode({"error" => error_attributes})
30
+ )
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Caliper
2
+ class Rack
3
+ def initialize(app, options = {}, &blk)
4
+ @app = app
5
+
6
+ yield self if block_given?
7
+ end
8
+
9
+ def call(env)
10
+ if Caliper.config[:enabled] && !env['PATH_INFO'][/^\/assets/]
11
+ env['caliper.tracer'] = Caliper::Tracer.new(env)
12
+ end
13
+
14
+ @status, @headers, @response = @app.call(env)
15
+
16
+ # check for routing error in rails way
17
+ if @status == 404 && @headers['X-Cascade'] == 'pass'
18
+ Caliper::AppError.create(
19
+ ActionController::RoutingError.new("No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"),
20
+ env
21
+ )
22
+ elsif env['caliper.tracer']
23
+ env['caliper.tracer'].finish
24
+ end
25
+
26
+ [@status, @headers, @response]
27
+ rescue Exception => exception
28
+ Caliper::AppError.create(exception, env)
29
+ raise exception
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ # add the resolved controller and action to the trace
2
+ module Caliper
3
+ module Rails
4
+ module AppendInfo
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_filter :add_request_info_to_tracer
9
+ end
10
+
11
+ def add_request_info_to_tracer
12
+ return true unless env['caliper.tracer'] # elegantly fail if something went wrong with middleware and also when errors are handled
13
+ request.env["caliper.tracer"].reqs = "#{params[:controller]}##{params[:action]}"
14
+
15
+ # check if developer has added customer data
16
+ if defined? self.add_to_caliper_trace
17
+ request.env["caliper.tracer"].add_to_trace(self.add_to_caliper_trace)
18
+ end
19
+
20
+ # TODO: add other parameters for the trace?
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # monkey patch the action controller to get reference to middleware caliper tracer
2
+ module ActionController
3
+ module Instrumentation
4
+ def process_action(action, *args)
5
+ raw_payload = {
6
+ :controller => self.class.name,
7
+ :action => self.action_name,
8
+ :params => request.filtered_parameters,
9
+ :formats => request.formats.map(&:to_sym),
10
+ :method => request.method,
11
+ :path => (request.fullpath rescue "unknown"),
12
+
13
+ # Need payload to reference the request env tracer
14
+ :caliper_tracer => request.env['caliper.tracer']
15
+ }
16
+
17
+ ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
18
+
19
+ ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
20
+ result = super
21
+ payload[:status] = response.status
22
+ append_info_to_payload(payload)
23
+ result
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ require 'caliper/rails/instrumentation'
2
+ require 'caliper/rails/append_info'
3
+
4
+ module Caliper
5
+ class Railtie < ::Rails::Railtie
6
+ rake_tasks do
7
+ load "caliper/tasks/caliper.rake"
8
+ end
9
+
10
+ config.caliper = ActiveSupport::OrderedOptions.new
11
+
12
+ initializer "caliper.initialize" do |app|
13
+ app.middleware.use Caliper::Rack
14
+
15
+ ActiveSupport.on_load(:action_controller) do
16
+ ActionController::Base.send(:include, Caliper::Rails::AppendInfo)
17
+ end
18
+
19
+ ActiveSupport::Notifications.subscribe(/(^render|action_controller|active_record)/) do |*args|
20
+ event = ActiveSupport::Notifications::Event.new(*args)
21
+
22
+ # set this so that sql and view notifications has access to caliper tracer
23
+ if event.name == 'start_processing.action_controller'
24
+ @caliper_tracer = event.payload[:caliper_tracer]
25
+ end
26
+
27
+ @caliper_tracer.record(event) if @caliper_tracer
28
+ end
29
+
30
+ end
31
+
32
+ # update routes to Caliper
33
+ config.after_initialize do
34
+ ::Rails.application.reload_routes!
35
+ inspector = Caliper::RouteInspector.new
36
+ all_routes = ::Rails.application.routes.routes
37
+ CaliperApi.update_routes(
38
+ Yajl::Encoder.encode({"routes" => inspector.routes(all_routes)})
39
+ )
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,108 @@
1
+ # Influenced by ActionDispatch::Routing::RoutesInspector
2
+ require 'delegate'
3
+
4
+ module Caliper
5
+
6
+ class RouteWrapper < SimpleDelegator
7
+ def endpoint
8
+ unless rack_app
9
+ "#{controller}##{action}"
10
+ end
11
+ # ignore rack apps for now (don't want to know about redirects)
12
+ #rack_app ? rack_app.inspect : "#{controller}##{action}"
13
+ end
14
+
15
+ def constraints
16
+ requirements.except(:controller, :action)
17
+ end
18
+
19
+ def rack_app(app = self.app)
20
+ @rack_app ||= begin
21
+ class_name = app.class.name.to_s
22
+ if class_name == "ActionDispatch::Routing::Mapper::Constraints"
23
+ rack_app(app.app)
24
+ elsif ActionDispatch::Routing::Redirect === app || class_name !~ /^ActionDispatch::Routing/
25
+ app
26
+ end
27
+ end
28
+ end
29
+
30
+ def verb
31
+ super.source.gsub(/[$^]/, '')
32
+ end
33
+
34
+ def path
35
+ super.spec.to_s
36
+ end
37
+
38
+ def name
39
+ super.to_s
40
+ end
41
+
42
+ def reqs
43
+ @reqs ||= begin
44
+ reqs = endpoint
45
+ # commenting out for now until tested and really required
46
+ #reqs += " #{constraints.to_s}" unless constraints.empty?
47
+ reqs
48
+ end
49
+ end
50
+
51
+ def controller
52
+ requirements[:controller] || ':controller'
53
+ end
54
+
55
+ def action
56
+ requirements[:action] || ':action'
57
+ end
58
+
59
+ def internal?
60
+ path =~ %r{/rails/info.*|^#{::Rails.application.config.assets.prefix}}
61
+ end
62
+
63
+ def engine?
64
+ rack_app && rack_app.respond_to?(:routes)
65
+ end
66
+ end
67
+
68
+ class RouteInspector
69
+ def initialize
70
+ @engines = Hash.new
71
+ end
72
+
73
+ def routes(all_routes)
74
+ routes = collect_routes(all_routes)
75
+
76
+ routes + routes_for_engines
77
+ end
78
+
79
+ def collect_routes(routes)
80
+ routes = routes.collect do |route|
81
+ RouteWrapper.new(route)
82
+ end.reject do |route|
83
+ route.internal?
84
+ end.collect do |route|
85
+ collect_engine_routes(route)
86
+
87
+ {:name => route.name, :verb => route.verb, :path => route.path, :reqs => route.reqs } unless route.reqs.nil?
88
+ end.compact
89
+ end
90
+
91
+ def collect_engine_routes(route)
92
+ name = route.endpoint
93
+ return unless route.engine?
94
+ return if @engines[name]
95
+
96
+ routes = route.rack_app.routes
97
+ if routes.is_a?(ActionDispatch::Routing::RouteSet)
98
+ @engines[name] = collect_routes(routes.routes)
99
+ end
100
+ end
101
+
102
+ def routes_for_engines
103
+ @engines.map do |name, routes|
104
+ formatted_routes(routes)
105
+ end.flatten
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,10 @@
1
+ namespace :caliper do
2
+
3
+ desc "Send a test call to Caliper to ensure everything is working correctly"
4
+ task :test => :environment do
5
+ Rails.logger = Logger.new(STDOUT)
6
+ puts "Posting a test message to Caliper with API KEY: #{Caliper.config[:api_key]} to #{Caliper.config[:api_host]}"
7
+ CaliperApi.test_post
8
+ puts "Test sent. Go ahead and deploy and view data on #{Caliper.config[:api_host]}"
9
+ end
10
+ end
@@ -0,0 +1,77 @@
1
+ require 'uuid'
2
+ require 'yajl'
3
+ require 'socket'
4
+
5
+ module Caliper
6
+ class Tracer
7
+ attr_accessor :uuid, :http_method, :uri, :path, :remote_ip, :samples, :host, :reqs, :extra_info
8
+
9
+ def initialize(env)
10
+ @uuid = UUID.generate
11
+
12
+ @http_method = env['REQUEST_METHOD']
13
+ @uri = env['REQUEST_URI'] # full URI
14
+ @path = env['PATH_INFO'] # user path_info because it's in the rack spec rather than REQUEST_PATH
15
+
16
+ @remote_ip = env["action_dispatch.remote_ip"] #requesting ip
17
+ @remote_ip = @remote_ip.first if @remote_ip.is_a?(Array)
18
+
19
+ @host = Socket.gethostname
20
+
21
+ @samples = []
22
+ end
23
+
24
+ def record(event)
25
+ event.payload.delete(:caliper_tracer) if event.payload[:caliper_tracer]
26
+ return if event.name[/active_record/] && (event.payload[:name] == nil || event.payload[:name][/SCHEMA/])
27
+
28
+ # strip un-required payload data for activerecord notifications
29
+ if event.name[/active_record/]
30
+ ["binds", "connection_id"].each do |key|
31
+ event.payload.delete(key)
32
+ end
33
+ end
34
+
35
+ #Caliper.logger.debug [event.name, event.time, event.end, event.transaction_id, event.payload].join("||")
36
+ @samples << event
37
+ end
38
+
39
+ def add_to_trace(data = {})
40
+ @extra_info = data
41
+ end
42
+
43
+ def finish
44
+ return true if samples.size == 0
45
+
46
+ trace_data = {
47
+ "uuid" => uuid,
48
+ "http_method" => http_method,
49
+ "uri" => uri,
50
+ "path" => path,
51
+ "remote_ip" => remote_ip.to_s,
52
+ "host" => host,
53
+ "reqs" => reqs,
54
+ "extra_info" => extra_info,
55
+ "samples" => []
56
+ }
57
+
58
+ samples.each do |sample|
59
+ trace_data["samples"] << {
60
+ "name" => sample.name,
61
+ "duration" => sample.duration,
62
+ #"start" => sample.time,
63
+ #"end" => sample.end,
64
+ "payload" => sample.payload
65
+ }
66
+ if sample.name[/process_action.action_controller/]
67
+ trace_data["view_runtime"] = sample.payload[:view_runtime]
68
+ trace_data["db_runtime"] = sample.payload[:db_runtime]
69
+ trace_data["duration"] = sample.duration
70
+ end
71
+ end
72
+ CaliperApi.create_trace(
73
+ Yajl::Encoder.encode({ "trace" => trace_data })
74
+ )
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,3 @@
1
+ module Caliper
2
+ VERSION = "0.0.1"
3
+ end
data/lib/caliper.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'uuid'
2
+ require 'logger'
3
+
4
+ require 'caliper/version'
5
+ require 'caliper/tracer'
6
+ require 'caliper/rack'
7
+ require 'caliper/route_inspector'
8
+ require 'caliper/app_error'
9
+
10
+ require 'caliper_api/http'
11
+
12
+
13
+ if defined? ::Rails
14
+ if ::Rails::VERSION::MAJOR >= 3
15
+ require 'caliper/railtie'
16
+ else
17
+ raise "Your Rails application is currently not supported with Caliper, get in touch we can make it so soon"
18
+ end
19
+ elsif !ENV["CALIPER_API_HOST"] #(might be in testing mode)
20
+ raise "Your Ruby application is currently not supported with Caliper, get in touch, we can make it so soon"
21
+ end
22
+
23
+ module Caliper
24
+ def self.logger
25
+ unless @logger
26
+ # if in Rails use Rails logger
27
+ if defined? ::Rails
28
+ @logger = ::Rails.logger
29
+ else
30
+ @logger = Logger.new(STDOUT)
31
+ @logger.level = Logger::INFO
32
+ end
33
+ end
34
+
35
+ @logger
36
+ end
37
+
38
+ def self.config
39
+ if defined? ::Rails
40
+ config_path = ::Rails.root
41
+ env = ::Rails.env
42
+ else
43
+ config_path = File.new("./")
44
+ env = "test"
45
+ end
46
+ begin
47
+ @config ||= YAML.load_file("#{config_path.join("config", "caliper.yml")}")[::Rails.env].symbolize_keys
48
+
49
+ Caliper.logger.error "No API Key set in caliper.yml" unless @config[:api_key]
50
+
51
+ # set host
52
+ @config[:api_host] = ENV["CALIPER_API_HOST"] || 'http://alpha.caliper.io/'
53
+
54
+ @config
55
+ rescue StandardError => e
56
+ Caliper.logger.error "No caliper.yml config file could be found!"
57
+ raise e
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,62 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'singleton'
4
+
5
+ module CaliperApi
6
+
7
+ class Poster
8
+ include Singleton
9
+
10
+ def initialize
11
+ @base_uri = Caliper.config[:api_host]
12
+ end
13
+
14
+ def self.send(url, data)
15
+ instance.post(url, data)
16
+ end
17
+
18
+ def post(url, data = "")
19
+ begin
20
+ uri = URI.parse(@base_uri + url)
21
+ http = Net::HTTP.new(uri.host, uri.port)
22
+
23
+ http.open_timeout = 5 # in seconds
24
+ http.read_timeout = 5 # in seconds
25
+
26
+ request = Net::HTTP::Post.new(uri.request_uri)
27
+ request["X_API_KEY"] = Caliper.config[:api_key]
28
+ request["Content-Type"] = "application/json"
29
+ request["Accept"] = "application/json"
30
+
31
+ request.body = data
32
+
33
+ response = http.request(request)
34
+
35
+ status = response.code.to_i
36
+ if status == 401
37
+ Caliper.logger.error "CaliperApi: Error: Not authorized check your API key on #{Caliper.config[:api_host]}"
38
+ elsif status > 400
39
+ Caliper.logger.error "CaliperApi: Error: got #{response.code} from server"
40
+ end
41
+ rescue Exception => e
42
+ Caliper.logger.error "CaliperApi: Http error: #{e.message}"
43
+ end
44
+ end
45
+ end
46
+
47
+ def self.test_post
48
+ Poster.send("/api/test", "")
49
+ end
50
+
51
+ def self.create_trace(tracer_json)
52
+ Poster.send("/api/traces", tracer_json)
53
+ end
54
+
55
+ def self.create_error(error_json)
56
+ Poster.send("/api/errors", error_json)
57
+ end
58
+
59
+ def self.update_routes(routes_json)
60
+ Poster.send("/api/routes", routes_json)
61
+ end
62
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Caliper::Tracer do
4
+
5
+ it 'should generate a uid on creation' do
6
+ tracer = Caliper::Tracer.new({"REQEUEST_METHOD" => "POST", "REQUEST_PATH" => "/users"})
7
+ tracer.uuid.should_not be_nil
8
+ end
9
+
10
+ context "When storing 2 events" do
11
+ before do
12
+ @tracer = Caliper::Tracer.new({"REQEUEST_METHOD" => "GET", "REQUEST_PATH" => "/users"})
13
+ @events = []
14
+ 2.times {
15
+ event = mock_event
16
+ @tracer.record(event)
17
+ @events << event
18
+ }
19
+ end
20
+
21
+ it 'should store the two events to referencing later' do
22
+ @tracer.samples.should_not be_nil
23
+ @tracer.samples.size.should eq 2
24
+ end
25
+
26
+ it 'should post data to CaliperAPI when finished' do
27
+ CaliperApi.should_receive(:create_trace)
28
+ @tracer.finish
29
+ end
30
+ end
31
+
32
+ context "Given a tracer with no samples" do
33
+ before do
34
+ @tracer = Caliper::Tracer.new({"REQUEST_METHOD" => "GET", "REQUEST_PATH" => "/users"})
35
+ end
36
+
37
+ it 'should not post data to CaliperAPI when finished' do
38
+ CaliperApi.should_not_receive(:create_trace)
39
+ @tracer.finish
40
+ end
41
+ end
42
+
43
+ context "A typical rails controller request trace" do
44
+ before do
45
+ @tracer = Caliper::Tracer.new({"REQEUEST_METHOD" => "GET", "REQUEST_PATH" => "/users"})
46
+
47
+ replay_into_tracer(@tracer, "rails_get_request_samples")
48
+ end
49
+
50
+ it 'should have stored correct amount of samples ignoring SCHEMA named events' do
51
+ @tracer.samples.each do |sample|
52
+ if sample.name[/active_record/]
53
+ sample.payload[:name].should_not eq "SCHEMA"
54
+ end
55
+ end
56
+ @tracer.samples.size.should eq 6
57
+ end
58
+
59
+ it 'should dump the correct json data' do
60
+ pending
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,9 @@
1
+ start_processing.action_controller||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:controller=>"PostsController", :action=>"index", :params=>{"action"=>"index", "controller"=>"posts"}, :formats=>[:html], :method=>"GET", :path=>"/posts"}
2
+ sql.active_record||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:sql=>"PRAGMA table_info(\"posts\")", :name=>"SCHEMA", :connection_id=>70199742565160, :binds=>[]}
3
+ sql.active_record||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:sql=>" SELECT name\n FROM sqlite_master\n WHERE type = 'table' AND NOT name = 'sqlite_sequence'\n AND name = \"posts\"", :name=>"SCHEMA", :connection_id=>70199742565160, :binds=>[]}
4
+ sql.active_record||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:sql=>"PRAGMA table_info(\"posts\")", :name=>"SCHEMA", :connection_id=>70199742565160, :binds=>[]}
5
+ sql.active_record||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:sql=>"SELECT \"posts\".* FROM \"posts\" ", :name=>"Post Load", :connection_id=>70199742565160, :binds=>[]}
6
+ !render_template.action_view||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:virtual_path=>"posts/index"}
7
+ render_template.action_view||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:identifier=>"/Users/kalv/Development/ruby/test-caliper/app/views/posts/index.html.erb", :layout=>"layouts/application"}
8
+ !render_template.action_view||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:virtual_path=>"layouts/application"}
9
+ process_action.action_controller||2012-11-30 15:07:13 -0800||2012-11-30 15:07:13 -0800||36dc476cf70478c5aecb||{:controller=>"PostsController", :action=>"index", :params=>{"action"=>"index", "controller"=>"posts"}, :formats=>[:html], :method=>"GET", :path=>"/posts", :status=>200, :view_runtime=>22.308999999999997, :db_runtime=>1.735}