diff --git a/.gitignore b/.gitignore index e5dba86d..68072775 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ config/travis.yml .yardoc log/ vendor +config/skylight.yml diff --git a/Gemfile b/Gemfile index aa41780b..7a2f174e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gemspec -ruby "2.1.5" +ruby '2.1.2' if ENV.key?('DYNO') gem 's3', github: 'travis-ci/s3' @@ -27,6 +27,7 @@ gem 'pry' gem 'metriks', '0.9.9.6' gem 'metriks-librato_metrics', github: 'eric/metriks-librato_metrics' gem 'micro_migrations' +gem 'skylight' group :test do gem 'rspec', '~> 2.13' diff --git a/Gemfile.lock b/Gemfile.lock index f85ae2cf..3f38a26c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -81,7 +81,7 @@ GIT GIT remote: git://github.com/travis-ci/travis-yaml.git - revision: f3aa306016a08b66a487f966eb8aa3a60ee9b319 + revision: 1630d576d221aea2340615e87f402df7889b5176 specs: travis-yaml (0.2.0) @@ -302,6 +302,8 @@ GEM rack-test sinatra (~> 1.4.0) tilt (~> 1.3) + skylight (0.6.0.beta.1) + activesupport (>= 3.0.0) slop (3.6.0) sprockets (2.2.3) hike (~> 1.2) @@ -367,6 +369,7 @@ DEPENDENCIES sentry-raven! sinatra sinatra-contrib + skylight travis-api! travis-config (~> 0.1.0) travis-core! diff --git a/lib/travis/api/app.rb b/lib/travis/api/app.rb index 1934ede2..e5f18631 100644 --- a/lib/travis/api/app.rb +++ b/lib/travis/api/app.rb @@ -19,7 +19,9 @@ require 'metriks/reporter/logger' require 'metriks/librato_metrics_reporter' require 'travis/support/log_subscriber/active_record_metrics' require 'fileutils' +require 'travis/api/instruments' require 'travis/api/v2/http' +require 'travis/api/app/stack_instrumentation' # Rack class implementing the HTTP API. # Instances respond to #call. @@ -75,6 +77,8 @@ module Travis::Api def initialize @app = Rack::Builder.app do + extend StackInstrumentation + use Travis::Api::App::Middleware::Skylight use(Rack::Config) { |env| env['metriks.request.start'] ||= Time.now.utc } Rack::Utils::HTTP_STATUS_CODES[420] = "Enhance Your Calm" diff --git a/lib/travis/api/app/access_token.rb b/lib/travis/api/app/access_token.rb index 99a70ad5..13219b3b 100644 --- a/lib/travis/api/app/access_token.rb +++ b/lib/travis/api/app/access_token.rb @@ -3,6 +3,8 @@ require 'securerandom' class Travis::Api::App class AccessToken + include Skylight::Helpers + DEFAULT_SCOPES = [:public, :private] attr_reader :token, :scopes, :user_id, :app_id, :expires_in, :extra @@ -22,6 +24,7 @@ class Travis::Api::App new(token: token, scopes: scopes, user_id: user_id, app_id: app_id, extra: extra) if user_id end + instrument_method def initialize(options = {}) raise ArgumentError, 'must supply either user_id or user' unless options.key?(:user) ^ options.key?(:user_id) raise ArgumentError, 'must supply app_id' unless options.key?(:app_id) @@ -40,6 +43,7 @@ class Travis::Api::App @extra = options[:extra] end + instrument_method def save key = key(token) redis.del(key) @@ -90,6 +94,7 @@ class Travis::Api::App private + instrument_method def reuse_token redis.get(reuse_key) unless expires_in end diff --git a/lib/travis/api/app/middleware/skylight.rb b/lib/travis/api/app/middleware/skylight.rb new file mode 100644 index 00000000..ccd2c527 --- /dev/null +++ b/lib/travis/api/app/middleware/skylight.rb @@ -0,0 +1,5 @@ +if ENV['SKYLIGHT_AUTHENTICATION'] + require_relative 'skylight/actual' +else + require_relative 'skylight/dummy' +end diff --git a/lib/travis/api/app/middleware/skylight/actual.rb b/lib/travis/api/app/middleware/skylight/actual.rb new file mode 100644 index 00000000..8d466b34 --- /dev/null +++ b/lib/travis/api/app/middleware/skylight/actual.rb @@ -0,0 +1,26 @@ +require 'travis/api/app' +require 'skylight' +require 'skylight/probes/tilt' +require 'skylight/probes/redis' + +class Travis::Api::App + class Middleware + class Skylight < Middleware + set(:setup) { ::Skylight.start! } + use ::Skylight::Middleware + + after do + instrumenter = ::Skylight::Instrumenter.instance + trace = instrumenter.current_trace if instrumenter + trace.endpoint = "#{request.request_method} #{endpoint || '???'}" if trace + end + + def endpoint + return @endpoint if defined? @endpoint and @endpoint + return unless headers['X-Pattern'].present? and headers['X-Endpoint'].present? + @endpoint = Object.const_get(headers['X-Endpoint']).prefix + headers['X-Pattern'] + rescue NameError + end + end + end +end diff --git a/lib/travis/api/app/middleware/skylight/dummy.rb b/lib/travis/api/app/middleware/skylight/dummy.rb new file mode 100644 index 00000000..d4309f23 --- /dev/null +++ b/lib/travis/api/app/middleware/skylight/dummy.rb @@ -0,0 +1,7 @@ +class Travis::Api::App + class Middleware + module Skylight + def self.new(app) app end + end + end +end diff --git a/lib/travis/api/app/responders/atom.rb b/lib/travis/api/app/responders/atom.rb index 163cf7a3..8bdd3e44 100644 --- a/lib/travis/api/app/responders/atom.rb +++ b/lib/travis/api/app/responders/atom.rb @@ -44,6 +44,7 @@ module Travis::Api::App::Responders super && resource.is_a?(ActiveRecord::Relation) && resource.first.is_a?(Build) end + instrument_method def apply super diff --git a/lib/travis/api/app/responders/badge.rb b/lib/travis/api/app/responders/badge.rb index 6c759b85..ec348228 100644 --- a/lib/travis/api/app/responders/badge.rb +++ b/lib/travis/api/app/responders/badge.rb @@ -4,6 +4,7 @@ module Travis::Api::App::Responders 'svg' end + instrument_method def apply set_headers send_file(filename, type: :svg, last_modified: last_modified) diff --git a/lib/travis/api/app/responders/base.rb b/lib/travis/api/app/responders/base.rb index 5f463d9c..525779a2 100644 --- a/lib/travis/api/app/responders/base.rb +++ b/lib/travis/api/app/responders/base.rb @@ -1,5 +1,6 @@ module Travis::Api::App::Responders class Base + include Skylight::Helpers attr_reader :endpoint, :resource, :options def initialize(endpoint, resource, options = {}) diff --git a/lib/travis/api/app/responders/image.rb b/lib/travis/api/app/responders/image.rb index df126d55..5ce73814 100644 --- a/lib/travis/api/app/responders/image.rb +++ b/lib/travis/api/app/responders/image.rb @@ -10,6 +10,7 @@ module Travis::Api::App::Responders headers['Content-Disposition'] = %(inline; filename="#{File.basename(filename)}") end + instrument_method def apply set_headers send_file(filename, type: :png, last_modified: last_modified) diff --git a/lib/travis/api/app/responders/json.rb b/lib/travis/api/app/responders/json.rb index 8d8b0314..441039a9 100644 --- a/lib/travis/api/app/responders/json.rb +++ b/lib/travis/api/app/responders/json.rb @@ -7,6 +7,7 @@ class Travis::Api::App super && !resource.is_a?(String) && !resource.nil? && accepts_log? end + instrument_method def apply super diff --git a/lib/travis/api/app/responders/plain.rb b/lib/travis/api/app/responders/plain.rb index 236bfdcf..df27b210 100644 --- a/lib/travis/api/app/responders/plain.rb +++ b/lib/travis/api/app/responders/plain.rb @@ -13,6 +13,7 @@ module Travis::Api::App::Responders super && (resource.is_a?(Log) || resource.is_a?(String)) end + instrument_method def apply super diff --git a/lib/travis/api/app/responders/service.rb b/lib/travis/api/app/responders/service.rb index 57942234..5f6f5655 100644 --- a/lib/travis/api/app/responders/service.rb +++ b/lib/travis/api/app/responders/service.rb @@ -10,6 +10,7 @@ module Travis::Api resource.respond_to?(:run) end + instrument_method def apply cache_control result = normalize(resource.run) diff --git a/lib/travis/api/app/responders/xml.rb b/lib/travis/api/app/responders/xml.rb index 7db0a964..871dbc22 100644 --- a/lib/travis/api/app/responders/xml.rb +++ b/lib/travis/api/app/responders/xml.rb @@ -37,6 +37,7 @@ module Travis::Api::App::Responders super && @resource.first.is_a?(Repository) end + instrument_method def apply super diff --git a/lib/travis/api/app/stack_instrumentation.rb b/lib/travis/api/app/stack_instrumentation.rb new file mode 100644 index 00000000..0307b6b3 --- /dev/null +++ b/lib/travis/api/app/stack_instrumentation.rb @@ -0,0 +1,52 @@ +require 'travis/api/app' + +class Travis::Api::App + module StackInstrumentation + class Middleware + def initialize(app, title = nil) + @app = app + @title = title || StackInstrumentation.title_for(app, :use) + end + + def call(env) + instrument { @app.call(env) } + end + + def instrument(&block) + return yield unless instrument? + ::Skylight.instrument(title: @title, &block) + end + + def instrument? + defined? ::Skylight + end + end + + def self.title_for(object, verb) + object &&= case object + when ::Sinatra::Wrapper then object.settings.inspect + when Class, Module then object.inspect + when String then object + else object.class.inspect + end + "Rack: #{verb} #{object}" + end + + def use(*) + super(Middleware) + super + end + + def run(app) + super Middleware.new(app, StackInstrumentation.title_for(app, :run)) + end + + def map(path, &block) + super(path) do + use(Middleware, StackInstrumentation.title_for(path, :map)) + extend StackInstrumentation + instance_eval(&block) + end + end + end +end diff --git a/lib/travis/api/instruments.rb b/lib/travis/api/instruments.rb new file mode 100644 index 00000000..a6973f8d --- /dev/null +++ b/lib/travis/api/instruments.rb @@ -0,0 +1,6 @@ +require 'skylight' + +Travis.services.send(:services).each_value do |service| + service.send(:include, Skylight::Helpers) + service.send(:instrument_method, :run) +end \ No newline at end of file