skylight 0.2.0.beta.4 → 0.2.0
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.
- 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: []
|