skylight 0.2.0.beta.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/lib/skylight.rb +7 -0
- data/lib/skylight/config.rb +4 -0
- data/lib/skylight/formatters/http.rb +32 -0
- data/lib/skylight/normalizers.rb +2 -1
- data/lib/skylight/probes.rb +90 -0
- data/lib/skylight/probes/excon.rb +19 -0
- data/lib/skylight/probes/excon/middleware.rb +62 -0
- data/lib/skylight/probes/net_http.rb +42 -0
- data/lib/skylight/railtie.rb +12 -0
- data/lib/skylight/util/inflector.rb +107 -0
- data/lib/skylight/version.rb +1 -1
- data/lib/skylight/worker/collector.rb +1 -0
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01c568e8a8c6485f4257cc1cbd29c7649fa11397
|
4
|
+
data.tar.gz: 15a33396cd41de105c07c3879c65fbe7a0c30ea3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0dda0d97a94a3e6366110f8a88fa50c16abede270e0f1eae2908f80aeef5b4c279c948e53cc5e52c89f815d906aed9c05c55abe22bdd6ebb6a00a15b2cd8acd6
|
7
|
+
data.tar.gz: e9563e4dda34af825883d28260d098176bc30bb7217fdf6f2a845472a6c1a9a7d2bdf26ba92988b6e46eac568cbcedc2094977add1e2f5e1845db4cc8475eedb
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
##
|
1
|
+
## 0.2.0 (December 3, 2013)
|
2
|
+
|
3
|
+
* Added Probes, initially Net::HTTP and Excon
|
4
|
+
* Wide-ranging memory cleanup
|
5
|
+
* Better resiliance to binary and encoding errors
|
6
|
+
* Add support for disabling
|
7
|
+
* De-dupe rendering instrumentation better
|
8
|
+
* Fix send_file event to not spew a gazillion nodes
|
9
|
+
* Rails 3.0 compatibility
|
10
|
+
* Detailed SQL annotations
|
2
11
|
|
3
12
|
## 0.1.8 (July 19, 2013)
|
4
13
|
|
data/lib/skylight.rb
CHANGED
@@ -35,12 +35,17 @@ module Skylight
|
|
35
35
|
autoload :Clock, 'skylight/util/clock'
|
36
36
|
autoload :Gzip, 'skylight/util/gzip'
|
37
37
|
autoload :HTTP, 'skylight/util/http'
|
38
|
+
autoload :Inflector, 'skylight/util/inflector'
|
38
39
|
autoload :Logging, 'skylight/util/logging'
|
39
40
|
autoload :Queue, 'skylight/util/queue'
|
40
41
|
autoload :Task, 'skylight/util/task'
|
41
42
|
autoload :UniformSample, 'skylight/util/uniform_sample'
|
42
43
|
end
|
43
44
|
|
45
|
+
module Formatters
|
46
|
+
autoload :HTTP, 'skylight/formatters/http'
|
47
|
+
end
|
48
|
+
|
44
49
|
# ==== Vendor ====
|
45
50
|
autoload :Beefcake, 'skylight/vendor/beefcake'
|
46
51
|
|
@@ -127,4 +132,6 @@ module Skylight
|
|
127
132
|
if defined?(Rails)
|
128
133
|
require 'skylight/railtie'
|
129
134
|
end
|
135
|
+
|
136
|
+
require 'skylight/probes'
|
130
137
|
end
|
data/lib/skylight/config.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Skylight
|
2
|
+
module Formatters
|
3
|
+
module HTTP
|
4
|
+
|
5
|
+
def self.build_opts(method, scheme, host, port, path, query)
|
6
|
+
category = "api.http.#{method.downcase}"
|
7
|
+
title = "#{method.upcase} #{host || path}"
|
8
|
+
description = "#{method.upcase} #{build_url(scheme, host, port, path, query)}"
|
9
|
+
|
10
|
+
{ category: category, title: title, description: description }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.build_url(scheme, host, port, path, query)
|
14
|
+
url = ''
|
15
|
+
if scheme
|
16
|
+
url << "#{scheme}://"
|
17
|
+
end
|
18
|
+
if host
|
19
|
+
url << host
|
20
|
+
end
|
21
|
+
if port
|
22
|
+
url << ":#{port}"
|
23
|
+
end
|
24
|
+
url << path
|
25
|
+
if query
|
26
|
+
url << "?#{query}"
|
27
|
+
end
|
28
|
+
url
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/skylight/normalizers.rb
CHANGED
@@ -78,7 +78,8 @@ module Skylight
|
|
78
78
|
|
79
79
|
SEPARATOR_BYTE = File::SEPARATOR.ord
|
80
80
|
|
81
|
-
if File::NULL == "NUL"
|
81
|
+
if File.const_defined?(:NULL) ? File::NULL == "NUL" : RbConfig::CONFIG['host_os'] =~ /mingw|mswin32/
|
82
|
+
# This is a DOSish environment
|
82
83
|
ALT_SEPARATOR_BYTE = File::ALT_SEPARATOR && File::ALT_SEPARATOR.ord
|
83
84
|
COLON_BYTE = ":".ord
|
84
85
|
def absolute_path?(path)
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# skylight/probes.rb
|
2
|
+
|
3
|
+
module Skylight
|
4
|
+
module Probes
|
5
|
+
|
6
|
+
class ProbeRegistration
|
7
|
+
attr_reader :klass_name, :require_paths, :probe
|
8
|
+
|
9
|
+
def initialize(klass_name, require_paths, probe)
|
10
|
+
@klass_name = klass_name
|
11
|
+
@require_paths = Array(require_paths)
|
12
|
+
@probe = probe
|
13
|
+
end
|
14
|
+
|
15
|
+
def install
|
16
|
+
probe.install
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.require_hooks
|
21
|
+
@require_hooks ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.installed
|
25
|
+
@installed ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.is_available?(klass_name)
|
29
|
+
!!Skylight::Util::Inflector.safe_constantize(klass_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.register(*args)
|
33
|
+
registration = ProbeRegistration.new(*args)
|
34
|
+
|
35
|
+
if is_available?(registration.klass_name)
|
36
|
+
installed[registration.klass_name] = registration
|
37
|
+
registration.install
|
38
|
+
else
|
39
|
+
register_require_hook(registration)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.require_hook(require_path)
|
44
|
+
registration = lookup_by_require_path(require_path)
|
45
|
+
return unless registration
|
46
|
+
|
47
|
+
# Double check constant is available
|
48
|
+
if is_available?(registration.klass_name)
|
49
|
+
installed[registration.klass_name] = registration
|
50
|
+
registration.install
|
51
|
+
|
52
|
+
# Don't need this to be called again
|
53
|
+
unregister_require_hook(registration)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.register_require_hook(registration)
|
58
|
+
registration.require_paths.each do |p|
|
59
|
+
require_hooks[p] = registration
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.unregister_require_hook(registration)
|
64
|
+
registration.require_paths.each do |p|
|
65
|
+
require_hooks.delete(p)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.lookup_by_require_path(require_path)
|
70
|
+
require_hooks[require_path]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Allow hooking require
|
76
|
+
module ::Kernel
|
77
|
+
alias require_without_sk require
|
78
|
+
|
79
|
+
def require(name)
|
80
|
+
ret = require_without_sk(name)
|
81
|
+
|
82
|
+
begin
|
83
|
+
Skylight::Probes.require_hook(name)
|
84
|
+
rescue Exception
|
85
|
+
# FIXME: Log these errors
|
86
|
+
end
|
87
|
+
|
88
|
+
ret
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Skylight
|
2
|
+
module Probes
|
3
|
+
module Excon
|
4
|
+
class Probe
|
5
|
+
def install
|
6
|
+
# Don't require until installation since it depends on Excon being loaded
|
7
|
+
require 'skylight/probes/excon/middleware'
|
8
|
+
|
9
|
+
idx = ::Excon.defaults[:middlewares].index(::Excon::Middleware::Instrumentor)
|
10
|
+
|
11
|
+
# TODO: Handle possibility of idx being nil
|
12
|
+
::Excon.defaults[:middlewares].insert(idx, Skylight::Probes::Excon::Middleware)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
register("Excon", "excon", Excon::Probe.new)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Skylight
|
2
|
+
module Probes
|
3
|
+
module Excon
|
4
|
+
class Middleware < ::Excon::Middleware::Base
|
5
|
+
|
6
|
+
include Util::Logging
|
7
|
+
|
8
|
+
def initialize(*)
|
9
|
+
@requests = {}
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO:
|
14
|
+
# - Consider whether a LIFO queue would be sufficient
|
15
|
+
# - Check that errors can't be called without a request
|
16
|
+
|
17
|
+
def request_call(datum)
|
18
|
+
begin_instrumentation(datum)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_call(datum)
|
23
|
+
super
|
24
|
+
ensure
|
25
|
+
end_instrumentation(datum)
|
26
|
+
end
|
27
|
+
|
28
|
+
def error_call(datum)
|
29
|
+
super
|
30
|
+
ensure
|
31
|
+
end_instrumentation(datum)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def begin_instrumentation(datum)
|
37
|
+
method = datum[:method].to_s
|
38
|
+
scheme = datum[:scheme]
|
39
|
+
host = datum[:host]
|
40
|
+
# TODO: Maybe don't show other default ports like 443
|
41
|
+
port = datum[:port] != 80 ? datum[:port] : nil
|
42
|
+
path = datum[:path]
|
43
|
+
query = datum[:query]
|
44
|
+
|
45
|
+
opts = Formatters::HTTP.build_opts(method, scheme, host, port, path, query)
|
46
|
+
|
47
|
+
@requests[datum.object_id] = Skylight.instrument(opts)
|
48
|
+
rescue Exception => e
|
49
|
+
error "failed to begin instrumentation for Excon; msg=%s", e.message
|
50
|
+
end
|
51
|
+
|
52
|
+
def end_instrumentation(datum)
|
53
|
+
@requests[datum.object_id].done
|
54
|
+
@requests.delete(datum)
|
55
|
+
rescue Exception => e
|
56
|
+
error "failed to end instrumentation for Excon; msg=%s", e.message
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Skylight
|
2
|
+
module Probes
|
3
|
+
module NetHTTP
|
4
|
+
class Probe
|
5
|
+
def install
|
6
|
+
Net::HTTP.class_eval do
|
7
|
+
alias request_without_sk request
|
8
|
+
|
9
|
+
def request(req, body = nil, &block)
|
10
|
+
unless started?
|
11
|
+
return request_without_sk(req, body, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
method = req.method
|
15
|
+
|
16
|
+
# req['host'] also includes special handling for default ports
|
17
|
+
host, port = req['host'] ? req['host'].split(':') : nil
|
18
|
+
|
19
|
+
# If we're connected with a persistent socket
|
20
|
+
host ||= self.address
|
21
|
+
port ||= self.port
|
22
|
+
|
23
|
+
path = req.path
|
24
|
+
scheme = use_ssl? ? "https" : "http"
|
25
|
+
|
26
|
+
# Contained in the path
|
27
|
+
query = nil
|
28
|
+
|
29
|
+
opts = Formatters::HTTP.build_opts(method, scheme, host, port, path, query)
|
30
|
+
|
31
|
+
Skylight.instrument(opts) do
|
32
|
+
request_without_sk(req, body, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
register("Net::HTTP", "net/http", NetHTTP::Probe.new)
|
41
|
+
end
|
42
|
+
end
|
data/lib/skylight/railtie.rb
CHANGED
@@ -11,8 +11,13 @@ module Skylight
|
|
11
11
|
# The path to the configuration file
|
12
12
|
config.skylight.config_path = "config/skylight.yml"
|
13
13
|
|
14
|
+
# The probes to load
|
15
|
+
config.skylight.probes = []
|
16
|
+
|
14
17
|
initializer 'skylight.configure' do |app|
|
15
18
|
if activate?
|
19
|
+
load_probes
|
20
|
+
|
16
21
|
if config = load_skylight_config(app)
|
17
22
|
Instrumenter.start!(config)
|
18
23
|
app.middleware.insert 0, Middleware
|
@@ -85,5 +90,12 @@ module Skylight
|
|
85
90
|
def activate?
|
86
91
|
environments.include?(Rails.env.to_s)
|
87
92
|
end
|
93
|
+
|
94
|
+
def load_probes
|
95
|
+
probes = config.skylight.probes || []
|
96
|
+
probes.each do |p|
|
97
|
+
require 'skylight/probes/#{p}'
|
98
|
+
end
|
99
|
+
end
|
88
100
|
end
|
89
101
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Skylight
|
2
|
+
module Util
|
3
|
+
module Inflector
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# From https://github.com/rails/rails/blob/f8e5022c73679f41db9bb6743179bab4571fb28e/activesupport/lib/active_support/inflector/methods.rb
|
7
|
+
|
8
|
+
# Tries to find a constant with the name specified in the argument string.
|
9
|
+
#
|
10
|
+
# 'Module'.constantize # => Module
|
11
|
+
# 'Test::Unit'.constantize # => Test::Unit
|
12
|
+
#
|
13
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
14
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
15
|
+
# account:
|
16
|
+
#
|
17
|
+
# C = 'outside'
|
18
|
+
# module M
|
19
|
+
# C = 'inside'
|
20
|
+
# C # => 'inside'
|
21
|
+
# 'C'.constantize # => 'outside', same as ::C
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# NameError is raised when the name is not in CamelCase or the constant is
|
25
|
+
# unknown.
|
26
|
+
def constantize(camel_cased_word)
|
27
|
+
names = camel_cased_word.split('::')
|
28
|
+
|
29
|
+
# Trigger a builtin NameError exception including the ill-formed constant in the message.
|
30
|
+
Object.const_get(camel_cased_word) if names.empty?
|
31
|
+
|
32
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
33
|
+
names.shift if names.size > 1 && names.first.empty?
|
34
|
+
|
35
|
+
names.inject(Object) do |constant, name|
|
36
|
+
if constant == Object
|
37
|
+
constant.const_get(name)
|
38
|
+
else
|
39
|
+
candidate = constant.const_get(name)
|
40
|
+
next candidate if constant.const_defined?(name, false)
|
41
|
+
next candidate unless Object.const_defined?(name)
|
42
|
+
|
43
|
+
# Go down the ancestors to check it it's owned
|
44
|
+
# directly before we reach Object or the end of ancestors.
|
45
|
+
constant = constant.ancestors.inject do |const, ancestor|
|
46
|
+
break const if ancestor == Object
|
47
|
+
break ancestor if ancestor.const_defined?(name, false)
|
48
|
+
const
|
49
|
+
end
|
50
|
+
|
51
|
+
# owner is in Object, so raise
|
52
|
+
constant.const_get(name, false)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Tries to find a constant with the name specified in the argument string.
|
58
|
+
#
|
59
|
+
# 'Module'.safe_constantize # => Module
|
60
|
+
# 'Test::Unit'.safe_constantize # => Test::Unit
|
61
|
+
#
|
62
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
63
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
64
|
+
# account:
|
65
|
+
#
|
66
|
+
# C = 'outside'
|
67
|
+
# module M
|
68
|
+
# C = 'inside'
|
69
|
+
# C # => 'inside'
|
70
|
+
# 'C'.safe_constantize # => 'outside', same as ::C
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# +nil+ is returned when the name is not in CamelCase or the constant (or
|
74
|
+
# part of it) is unknown.
|
75
|
+
#
|
76
|
+
# 'blargle'.safe_constantize # => nil
|
77
|
+
# 'UnknownModule'.safe_constantize # => nil
|
78
|
+
# 'UnknownModule::Foo::Bar'.safe_constantize # => nil
|
79
|
+
def safe_constantize(camel_cased_word)
|
80
|
+
constantize(camel_cased_word)
|
81
|
+
rescue NameError => e
|
82
|
+
raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
|
83
|
+
e.name.to_s == camel_cased_word.to_s
|
84
|
+
rescue ArgumentError => e
|
85
|
+
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Mount a regular expression that will match part by part of the constant.
|
91
|
+
#
|
92
|
+
# const_regexp("Foo::Bar::Baz") # => /Foo(::Bar(::Baz)?)?/
|
93
|
+
# const_regexp("::") # => /::/
|
94
|
+
def const_regexp(camel_cased_word) #:nodoc:
|
95
|
+
parts = camel_cased_word.split("::")
|
96
|
+
|
97
|
+
return Regexp.escape(camel_cased_word) if parts.empty?
|
98
|
+
|
99
|
+
last = parts.pop
|
100
|
+
|
101
|
+
parts.reverse.inject(last) do |acc, part|
|
102
|
+
part.empty? ? acc : "#{part}(::#{acc})?"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/skylight/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skylight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tilde, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 3.0.0
|
27
|
-
description:
|
27
|
+
description:
|
28
28
|
email:
|
29
29
|
- engineering@tilde.io
|
30
30
|
executables:
|
@@ -38,6 +38,7 @@ files:
|
|
38
38
|
- lib/skylight/compat.rb
|
39
39
|
- lib/skylight/config.rb
|
40
40
|
- lib/skylight/data/cacert.pem
|
41
|
+
- lib/skylight/formatters/http.rb
|
41
42
|
- lib/skylight/gc.rb
|
42
43
|
- lib/skylight/helpers.rb
|
43
44
|
- lib/skylight/instrumenter.rb
|
@@ -60,12 +61,17 @@ files:
|
|
60
61
|
- lib/skylight/normalizers/render_template.rb
|
61
62
|
- lib/skylight/normalizers/send_file.rb
|
62
63
|
- lib/skylight/normalizers/sql.rb
|
64
|
+
- lib/skylight/probes.rb
|
65
|
+
- lib/skylight/probes/excon.rb
|
66
|
+
- lib/skylight/probes/excon/middleware.rb
|
67
|
+
- lib/skylight/probes/net_http.rb
|
63
68
|
- lib/skylight/railtie.rb
|
64
69
|
- lib/skylight/subscriber.rb
|
65
70
|
- lib/skylight/util/allocation_free.rb
|
66
71
|
- lib/skylight/util/clock.rb
|
67
72
|
- lib/skylight/util/gzip.rb
|
68
73
|
- lib/skylight/util/http.rb
|
74
|
+
- lib/skylight/util/inflector.rb
|
69
75
|
- lib/skylight/util/logging.rb
|
70
76
|
- lib/skylight/util/queue.rb
|
71
77
|
- lib/skylight/util/task.rb
|
@@ -149,13 +155,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
155
|
version: 1.9.2
|
150
156
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
157
|
requirements:
|
152
|
-
- - '
|
158
|
+
- - '>='
|
153
159
|
- !ruby/object:Gem::Version
|
154
|
-
version:
|
160
|
+
version: '0'
|
155
161
|
requirements: []
|
156
162
|
rubyforge_project:
|
157
|
-
rubygems_version: 2.1.
|
163
|
+
rubygems_version: 2.1.0
|
158
164
|
signing_key:
|
159
165
|
specification_version: 4
|
160
|
-
summary: Skylight is a ruby application monitoring tool.
|
166
|
+
summary: Skylight is a ruby application monitoring tool. Currently in closed beta.
|
161
167
|
test_files: []
|