skylight 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.