skylight 0.0.6 → 0.0.7

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.
@@ -7,6 +7,12 @@ require 'active_support/notifications'
7
7
  module Skylight
8
8
  class Error < RuntimeError; end
9
9
 
10
+ TIERS = %w(
11
+ app
12
+ view
13
+ db
14
+ gc)
15
+
10
16
  # First require all util files
11
17
  require 'skylight/util/atomic'
12
18
  require 'skylight/util/bytes'
@@ -22,6 +28,7 @@ module Skylight
22
28
  require 'skylight/config'
23
29
  require 'skylight/instrumenter'
24
30
  require 'skylight/middleware'
31
+ require 'skylight/normalize'
25
32
  require 'skylight/json_proto'
26
33
  require 'skylight/subscriber'
27
34
  require 'skylight/trace'
@@ -25,7 +25,7 @@ module Skylight
25
25
 
26
26
  def start!
27
27
  @worker.start!
28
- Subscriber.register!
28
+ Subscriber.register!(config)
29
29
 
30
30
  # Ensure properly configured
31
31
  return unless config
@@ -22,7 +22,7 @@ module Skylight
22
22
  # spans: [
23
23
  # [
24
24
  # null, // parent-id -- index of the parent span or null if root node
25
- # 0292352, // Span start timestamp in 0.1ms granularity
25
+ # 0292352, // Span start timestamp in 0.1ms granularity, relative to the start of the trace
26
26
  # 20, // Duration of the span in 0.1ms granularity
27
27
  # "action_controller.process", // Span category
28
28
  # "Processing WidgetsController#index", // Span title, max 60 chars (optional)
@@ -33,8 +33,10 @@ module Skylight
33
33
  # 0, // The previous span is this span's parent
34
34
  # 1340923,
35
35
  # 0, // No duration
36
- # "log.info", // category
36
+ # "log.info", // category "foo" "\"foo\"
37
+ # "Title", // Title
37
38
  # "Doing some stuff..." // Span description
39
+ # {}, // Map String->String
38
40
  # ]
39
41
  # ]
40
42
  # }
@@ -68,7 +70,9 @@ module Skylight
68
70
  s.started_at,
69
71
  s.ended_at - s.started_at,
70
72
  s.category,
71
- s.description
73
+ s.title,
74
+ s.description,
75
+ s.annotations
72
76
  ]
73
77
  end
74
78
 
@@ -78,6 +82,9 @@ module Skylight
78
82
  ehash
79
83
  end
80
84
 
85
+ require "pp"
86
+ @config.logger.debug PP.pp(hash, "")
87
+
81
88
  out << hash.to_json
82
89
  end
83
90
  end
@@ -13,7 +13,7 @@ module Skylight
13
13
 
14
14
  def call(env)
15
15
  instrumenter.trace("Rack") do
16
- ActiveSupport::Notifications.instrument("rack.request") do
16
+ ActiveSupport::Notifications.instrument("app.rack.request") do
17
17
  @app.call(env)
18
18
  end
19
19
  end
@@ -0,0 +1,38 @@
1
+ module Skylight
2
+ module Normalize
3
+ @registry = {}
4
+
5
+ def self.normalize(trace, name, payload)
6
+ klass = @registry[name]
7
+
8
+ if klass
9
+ klass.new(trace, name, payload).normalize
10
+ else
11
+ Default.new(trace, name, payload).normalize
12
+ end
13
+ end
14
+
15
+ def self.register(name, klass)
16
+ @registry[name] = klass
17
+ end
18
+ end
19
+
20
+ class Normalizer
21
+ def self.register(name)
22
+ Normalize.register(name, self)
23
+ end
24
+
25
+ def initialize(trace, name, payload)
26
+ @trace, @name, @payload = trace, name, payload
27
+ end
28
+ end
29
+ end
30
+
31
+ require "skylight/normalize/default"
32
+ require "skylight/normalize/start_processing"
33
+ require "skylight/normalize/process_action"
34
+ require "skylight/normalize/render_collection"
35
+ require "skylight/normalize/render_template"
36
+ require "skylight/normalize/render_partial"
37
+ require "skylight/normalize/send_file"
38
+ require "skylight/normalize/sql"
@@ -0,0 +1,17 @@
1
+ module Skylight
2
+ module Normalize
3
+ class Default < Normalizer
4
+ REGEX = /^(?:#{TIERS.join('|')})(?:\.|$)/
5
+
6
+ def normalize
7
+ if @name =~ REGEX
8
+ annot = @payload.dup
9
+ [ @name, annot.delete(:title), annot.delete(:description), annot ]
10
+ else
11
+ :skip
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Skylight
2
+ module Normalize
3
+ class ProcessAction < Normalizer
4
+ register "process_action.action_controller"
5
+
6
+ def normalize
7
+ @trace.endpoint = controller_action
8
+ [ "app.controller.request", controller_action, controller_action, @payload ]
9
+ end
10
+
11
+ private
12
+ def controller_action
13
+ "#{@payload[:controller]}##{@payload[:action]}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Skylight
2
+ module Normalize
3
+ class RenderCollection < Normalizer
4
+ register "render_collection.action_view"
5
+
6
+ def normalize
7
+ path = @payload[:identifier]
8
+ annotations = { count: @payload[:count] }
9
+
10
+ [ "view.render.collection", path, path, annotations ]
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,17 @@
1
+ module Skylight
2
+ module Normalize
3
+ class RenderPartial < Normalizer
4
+ register "render_partial.action_view"
5
+
6
+ def normalize
7
+ path = @payload[:identifier]
8
+ annotations = { partial: true }
9
+
10
+ [ "view.render.template", path, path, annotations ]
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+
@@ -0,0 +1,16 @@
1
+ module Skylight
2
+ module Normalize
3
+ class RenderTemplate < Normalizer
4
+ register "render_template.action_view"
5
+
6
+ def normalize
7
+ path = @payload[:identifier]
8
+ annotations = { partial: false }
9
+
10
+ [ "view.render.template", path, path, annotations ]
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+
@@ -0,0 +1,44 @@
1
+ require "action_dispatch/http/mime_type"
2
+ require "action_dispatch/http/mime_types"
3
+ require "rack/utils"
4
+
5
+ module Skylight
6
+ module Normalize
7
+ class SendFile < Normalizer
8
+ register "send_file.action_controller"
9
+
10
+ def normalize
11
+ path = @payload[:path]
12
+
13
+ annotations = {
14
+ path: path,
15
+ filename: @payload[:filename],
16
+ type: normalize_type,
17
+ disposition: normalize_disposition,
18
+ status: normalize_status
19
+ }
20
+
21
+ # These will eventually be different
22
+ title = desc = "send file: #{path}"
23
+
24
+ [ "app.controller.send_file", title, desc, annotations ]
25
+ end
26
+
27
+ private
28
+ def normalize_type
29
+ type = @payload[:type] || "application/octet-stream"
30
+ type = Mime[type].to_s if type.is_a?(Symbol)
31
+ type
32
+ end
33
+
34
+ def normalize_status
35
+ status = @payload[:status] || 200
36
+ Rack::Utils.status_code(status)
37
+ end
38
+
39
+ def normalize_disposition
40
+ @payload[:disposition] || "attachment"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ module Skylight
2
+ module Normalize
3
+ class SQL < Normalizer
4
+ register "sql.active_record"
5
+
6
+ def normalize
7
+ if @payload[:name] == "SCHEMA"
8
+ return :skip
9
+ elsif @payload[:name] == "CACHE"
10
+ name = "db.sql.cache"
11
+ title = "Cached Load"
12
+ else
13
+ name = "db.sql.query"
14
+ title = @payload[:name]
15
+ end
16
+
17
+ binds = @payload[:binds]
18
+
19
+ annotations = {
20
+ sql: @payload[:sql],
21
+ binds: binds ? binds.map(&:last) : []
22
+ }
23
+
24
+ [ name, title, nil, annotations ]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ module Skylight
2
+ module Normalize
3
+ class StartProcessing < Normalizer
4
+ register "start_processing.action_controller"
5
+
6
+ def normalize
7
+ return :skip
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -1,30 +1,49 @@
1
1
  module Skylight
2
2
  # TODO: Handle filtering out notifications that we don't care about
3
3
  class Subscriber
4
- PROCESS_ACTION = "process_action.action_controller"
4
+ def self.register!(config=Config.new)
5
+ ActiveSupport::Notifications.subscribe nil, new(config)
6
+ end
5
7
 
6
- def self.register!
7
- ActiveSupport::Notifications.subscribe nil, new
8
+ def initialize(config)
9
+ @config = config
8
10
  end
9
11
 
10
12
  def start(name, id, payload)
11
13
  return unless trace = Trace.current
12
14
 
13
- if name == PROCESS_ACTION
14
- trace.endpoint = controller_action(payload)
15
+ name, title, desc, payload = Normalize.normalize(trace, name, payload)
16
+
17
+ if name != :skip
18
+ logger.debug("[SKYLIGHT] START: #{name} (#{title}, \"#{desc}\")")
19
+ logger.debug("[SKYLIGHT] > #{payload.inspect}")
20
+ else
21
+ logger.debug("[SKYLIGHT] START: skipped")
15
22
  end
16
23
 
17
- trace.start(name, nil, payload)
24
+ trace.start(name, title, desc, payload)
18
25
  end
19
26
 
20
27
  def finish(name, id, payload)
21
28
  return unless trace = Trace.current
29
+
30
+ logger.debug("[SKYLIGHT] END")
22
31
  trace.stop
23
32
  end
24
33
 
25
34
  def measure(name, id, payload)
26
35
  return unless trace = Trace.current
27
- trace.record(name, nil, payload)
36
+
37
+ name, title, desc, payload = Normalize.normalize(trace, name, payload)
38
+
39
+ if name != :skip
40
+ logger.debug("[SKYLIGHT] MEASURE: #{name} (#{title}, \"#{desc}\")")
41
+ logger.debug("[SKYLIGHT] > #{payload.inspect}")
42
+ else
43
+ logger.debug("[SKYLIGHT] MEASURE: skipped")
44
+ end
45
+
46
+ trace.record(name, title, desc, payload)
28
47
  end
29
48
 
30
49
  private
@@ -33,5 +52,8 @@ module Skylight
33
52
  "#{payload[:controller]}##{payload[:action]}"
34
53
  end
35
54
 
55
+ def logger
56
+ @config.logger
57
+ end
36
58
  end
37
59
  end
@@ -1,4 +1,8 @@
1
1
  module Skylight
2
+ # The first span will always have a timestamp of 0, which other
3
+ # spans will report times relative to.
4
+ #
5
+ # build_span will lazily start the timestamp at 0
2
6
  class Trace
3
7
  KEY = :__skylight_current_trace
4
8
 
@@ -10,10 +14,11 @@ module Skylight
10
14
  class Span < Struct.new(
11
15
  :parent,
12
16
  :started_at,
13
- :ended_at,
14
17
  :category,
18
+ :title,
15
19
  :description,
16
- :annotations)
20
+ :annotations,
21
+ :ended_at)
17
22
 
18
23
  def key
19
24
  @key ||= [category, description]
@@ -24,26 +29,30 @@ module Skylight
24
29
  attr_writer :endpoint
25
30
 
26
31
  def initialize(endpoint = "Unknown", ident = nil)
27
- @ident = ident
28
- @endpoint = endpoint
29
- @spans = []
32
+ @ident = ident
33
+ @endpoint = endpoint
34
+ @spans = []
35
+ @timestamp = nil
36
+ @finish = nil
37
+ @stack = []
30
38
 
31
39
  # Tracks the ID of the current parent
32
40
  @parent = nil
33
41
  end
34
42
 
35
43
  def from
36
- return unless span = @spans.first
37
- span.started_at
44
+ return unless @finish
45
+ @timestamp
38
46
  end
39
47
 
40
48
  def to
41
- return unless span = @spans.last
42
- span.ended_at
49
+ @finish
43
50
  end
44
51
 
45
- def record(cat, desc = nil, annot = nil)
46
- span = build_span(cat, desc, annot)
52
+ def record(cat, title, desc, annot)
53
+ return self if cat == :skip
54
+
55
+ span = build_span(cat, title, desc, annot)
47
56
  span.ended_at = span.started_at
48
57
 
49
58
  @spans << span
@@ -51,8 +60,11 @@ module Skylight
51
60
  self
52
61
  end
53
62
 
54
- def start(cat, desc = nil, annot = nil)
55
- span = build_span(cat, desc, annot)
63
+ def start(cat, title, desc, annot)
64
+ @stack.push cat
65
+ return self if cat == :skip
66
+
67
+ span = build_span(cat, title, desc, annot)
56
68
 
57
69
  @parent = @spans.length
58
70
 
@@ -62,6 +74,9 @@ module Skylight
62
74
  end
63
75
 
64
76
  def stop
77
+ last = @stack.pop
78
+ return self if last == :skip
79
+
65
80
  # Find last unclosed span
66
81
  span = @spans.last
67
82
  while span && span.ended_at
@@ -71,7 +86,7 @@ module Skylight
71
86
  raise "trace unbalanced" unless span
72
87
 
73
88
  # Set ended_at
74
- span.ended_at = now
89
+ span.ended_at = now - @timestamp
75
90
 
76
91
  # Update the parent
77
92
  @parent = @spans[@parent].parent
@@ -84,6 +99,7 @@ module Skylight
84
99
  raise "trace unbalanced" if @parent
85
100
 
86
101
  @ident ||= gen_ident
102
+ @finish = now
87
103
 
88
104
  # No more changes should be made
89
105
  freeze
@@ -101,8 +117,11 @@ module Skylight
101
117
  Util::UUID.gen Digest::MD5.digest(@endpoint)[0, 2]
102
118
  end
103
119
 
104
- def build_span(cat, desc, annot)
105
- Span.new(@parent, now, nil, cat, desc || "", annot)
120
+ def build_span(cat, title, desc, annot)
121
+ n = now
122
+ @timestamp ||= n
123
+
124
+ Span.new(@parent, n - @timestamp, cat, title, desc, annot)
106
125
  end
107
126
 
108
127
  end
@@ -1,3 +1,3 @@
1
1
  module Skylight
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -15,31 +15,20 @@ module Skylight
15
15
  attr_reader :from, :counts, :sample
16
16
 
17
17
  def initialize(config, from, interval)
18
- @config = config
19
- @interval = interval
20
- @from = from
21
- @to = from + interval
22
- @flush_at = @to + FLUSH_DELAY
23
- @sample = Util::UniformSample.new(config.samples_per_interval)
24
- @counts = Hash.new { |h,k| h[k] = 0 }
18
+ @from = from
19
+ @flush_at = from + interval + FLUSH_DELAY
20
+ @sample = Util::UniformSample.new(config.samples_per_interval)
21
+ @counts = Hash.new(0)
25
22
  end
26
23
 
27
24
  def should_flush?(now)
28
25
  now >= @flush_at
29
26
  end
30
27
 
31
- def next_batch
32
- Batch.new(@config, @to, @interval)
33
- end
34
-
35
28
  def empty?
36
29
  @sample.empty?
37
30
  end
38
31
 
39
- def wants?(trace)
40
- return trace.to >= @from && trace.to < @to
41
- end
42
-
43
32
  def push(trace)
44
33
  # Count it
45
34
  @counts[trace.endpoint] += 1
@@ -92,41 +81,28 @@ module Skylight
92
81
 
93
82
  # A worker iteration
94
83
  def iter(msg, now=Util.clock.now)
95
- unless @current_batch
96
- interval = Util.clock.convert(@interval)
97
- from = (now / interval) * interval
98
-
99
- # If we're still accepting traces from the previous batch
100
- # Create the previous interval instead
101
- if now < from + FLUSH_DELAY
102
- from -= interval
103
- end
84
+ interval = Util.clock.convert(@interval)
104
85
 
105
- @current_batch = Batch.new(config, from, interval)
106
- @next_batch = @current_batch.next_batch
107
- end
86
+ @current_batch ||= Batch.new(config, start(now, interval), interval)
108
87
 
109
88
  if msg == :SHUTDOWN
110
89
  flush(@current_batch) if @current_batch
111
- flush(@next_batch) if @next_batch
90
+ @current_batch = nil
112
91
  return false
113
92
  end
114
93
 
115
- while @current_batch && @current_batch.should_flush?(now)
94
+ if @current_batch.should_flush?(now)
116
95
  flush(@current_batch)
117
- @current_batch = @next_batch
118
- @next_batch = @current_batch.next_batch
96
+ @current_batch = Batch.new(config, start(now, interval), interval)
119
97
  end
120
98
 
99
+ return true if msg.nil?
100
+
121
101
  if Trace === msg
122
102
  debug "Received trace"
123
- if @current_batch.wants?(msg)
124
- @current_batch.push(msg)
125
- elsif @next_batch.wants?(msg)
126
- @next_batch.push(msg)
127
- else
128
- # Seems bad bro
129
- end
103
+ @current_batch.push(msg)
104
+ else
105
+ debug "Received something other than a trace: #{msg.inspect}"
130
106
  end
131
107
 
132
108
  true
@@ -138,6 +114,10 @@ module Skylight
138
114
  @instrumenter.config
139
115
  end
140
116
 
117
+ def start(now, interval)
118
+ (now / interval) * interval
119
+ end
120
+
141
121
  def logger
142
122
  config.logger
143
123
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skylight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-05 00:00:00.000000000 Z
12
+ date: 2013-04-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: 3.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: actionpack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 3.0.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
30
46
  description: Currently in pre-alpha.
31
47
  email:
32
48
  - engineering@tilde.io
@@ -44,6 +60,15 @@ files:
44
60
  - lib/skylight/instrumenter.rb
45
61
  - lib/skylight/json_proto.rb
46
62
  - lib/skylight/middleware.rb
63
+ - lib/skylight/normalize.rb
64
+ - lib/skylight/normalize/default.rb
65
+ - lib/skylight/normalize/process_action.rb
66
+ - lib/skylight/normalize/render_collection.rb
67
+ - lib/skylight/normalize/render_partial.rb
68
+ - lib/skylight/normalize/render_template.rb
69
+ - lib/skylight/normalize/send_file.rb
70
+ - lib/skylight/normalize/sql.rb
71
+ - lib/skylight/normalize/start_processing.rb
47
72
  - lib/skylight/railtie.rb
48
73
  - lib/skylight/subscriber.rb
49
74
  - lib/skylight/trace.rb
@@ -78,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
103
  version: '0'
79
104
  requirements: []
80
105
  rubyforge_project:
81
- rubygems_version: 1.8.25
106
+ rubygems_version: 1.8.24
82
107
  signing_key:
83
108
  specification_version: 3
84
109
  summary: Skylight is a ruby application monitoring tool.