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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c34ce7c2bd1892ae5f05d4e9cc0ee5054cbb9f1
4
- data.tar.gz: ce556c192f1f5b1d72be7680d358d8373a80cfe7
3
+ metadata.gz: 01c568e8a8c6485f4257cc1cbd29c7649fa11397
4
+ data.tar.gz: 15a33396cd41de105c07c3879c65fbe7a0c30ea3
5
5
  SHA512:
6
- metadata.gz: a9b4be609c1d76a2075cbb9ad116d724fc35be01dfe133e00978c3b09a8e32765a78d2db0ccb2f3c43a60182891a57e42ee8bcecd53ec8d50750a6fc2f38f138
7
- data.tar.gz: 082e9dd9bfc81ea18b53b830c52bf26796b90943c6af65b78723b21a8d1207478f87ac835a8386c4f0741a4fb97d9d89200ed2746b8fdadb8d9945b869c1a975
6
+ metadata.gz: 0dda0d97a94a3e6366110f8a88fa50c16abede270e0f1eae2908f80aeef5b4c279c948e53cc5e52c89f815d906aed9c05c55abe22bdd6ebb6a00a15b2cd8acd6
7
+ data.tar.gz: e9563e4dda34af825883d28260d098176bc30bb7217fdf6f2a845472a6c1a9a7d2bdf26ba92988b6e46eac568cbcedc2094977add1e2f5e1845db4cc8475eedb
@@ -1,4 +1,13 @@
1
- ## unreleased ##
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
 
@@ -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
@@ -255,6 +255,10 @@ authentication: #{self[:authentication]}
255
255
  @gc ||= GC.new(self, get('gc.profiler', VM::GC.new))
256
256
  end
257
257
 
258
+ def constant_flush?
259
+ get('test.constant_flush')
260
+ end
261
+
258
262
  def root
259
263
  self[:root] || Dir.pwd
260
264
  end
@@ -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
@@ -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
@@ -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
@@ -1,4 +1,4 @@
1
1
  module Skylight
2
- VERSION = '0.2.0.beta.4'
2
+ VERSION = '0.2.0'
3
3
  end
4
4
 
@@ -173,6 +173,7 @@ module Skylight
173
173
  end
174
174
 
175
175
  def should_flush?(now)
176
+ return true if @config.constant_flush?
176
177
  now >= @flush_at
177
178
  end
178
179
 
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.beta.4
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-09 00:00:00.000000000 Z
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: Currently in pre-alpha.
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: 1.3.1
160
+ version: '0'
155
161
  requirements: []
156
162
  rubyforge_project:
157
- rubygems_version: 2.1.9
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: []