skylight 0.0.16 → 0.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -19
  3. data/bin/skylight +0 -2
  4. data/lib/skylight.rb +52 -42
  5. data/lib/skylight/api.rb +34 -0
  6. data/lib/skylight/cli.rb +68 -57
  7. data/lib/skylight/compat.rb +19 -5
  8. data/lib/skylight/config.rb +219 -98
  9. data/lib/skylight/gc.rb +109 -0
  10. data/lib/skylight/instrumenter.rb +53 -70
  11. data/lib/skylight/messages.rb +19 -0
  12. data/lib/skylight/messages/annotation.rb +13 -0
  13. data/lib/skylight/messages/base.rb +24 -0
  14. data/lib/skylight/messages/batch.rb +11 -0
  15. data/lib/skylight/messages/endpoint.rb +12 -0
  16. data/lib/skylight/messages/event.rb +12 -0
  17. data/lib/skylight/messages/hello.rb +53 -0
  18. data/lib/skylight/messages/span.rb +21 -0
  19. data/lib/skylight/messages/trace.rb +162 -0
  20. data/lib/skylight/middleware.rb +2 -4
  21. data/lib/skylight/normalizers.rb +89 -0
  22. data/lib/skylight/normalizers/default.rb +22 -0
  23. data/lib/skylight/normalizers/process_action.rb +19 -0
  24. data/lib/skylight/normalizers/render_collection.rb +14 -0
  25. data/lib/skylight/normalizers/render_partial.rb +14 -0
  26. data/lib/skylight/normalizers/render_template.rb +14 -0
  27. data/lib/skylight/{normalize → normalizers}/send_file.rb +15 -15
  28. data/lib/skylight/normalizers/sql.rb +25 -0
  29. data/lib/skylight/railtie.rb +21 -41
  30. data/lib/skylight/subscriber.rb +29 -19
  31. data/lib/skylight/util/clock.rb +8 -21
  32. data/lib/skylight/util/http.rb +93 -46
  33. data/lib/skylight/util/logging.rb +66 -0
  34. data/lib/skylight/util/queue.rb +7 -3
  35. data/lib/skylight/util/task.rb +154 -0
  36. data/lib/skylight/{compat → vendor/active_support}/notifications.rb +56 -24
  37. data/lib/skylight/{compat → vendor/active_support}/notifications/fanout.rb +19 -26
  38. data/lib/skylight/{compat → vendor/active_support}/notifications/instrumenter.rb +25 -18
  39. data/lib/skylight/vendor/active_support/per_thread_registry.rb +52 -0
  40. data/lib/skylight/vendor/beefcake.rb +256 -0
  41. data/lib/skylight/vendor/beefcake/buffer.rb +112 -0
  42. data/lib/skylight/vendor/beefcake/decode.rb +107 -0
  43. data/lib/skylight/vendor/beefcake/encode.rb +115 -0
  44. data/lib/skylight/vendor/{highline.rb → cli/highline.rb} +0 -0
  45. data/lib/skylight/vendor/{highline → cli/highline}/color_scheme.rb +0 -0
  46. data/lib/skylight/vendor/{highline → cli/highline}/compatibility.rb +0 -0
  47. data/lib/skylight/vendor/{highline → cli/highline}/import.rb +0 -0
  48. data/lib/skylight/vendor/{highline → cli/highline}/menu.rb +0 -0
  49. data/lib/skylight/vendor/{highline → cli/highline}/question.rb +0 -0
  50. data/lib/skylight/vendor/{highline → cli/highline}/simulate.rb +0 -0
  51. data/lib/skylight/vendor/{highline → cli/highline}/string_extensions.rb +0 -0
  52. data/lib/skylight/vendor/{highline → cli/highline}/style.rb +0 -0
  53. data/lib/skylight/vendor/{highline → cli/highline}/system_extensions.rb +0 -0
  54. data/lib/skylight/vendor/{thor.rb → cli/thor.rb} +0 -0
  55. data/lib/skylight/vendor/{thor → cli/thor}/actions.rb +0 -0
  56. data/lib/skylight/vendor/{thor → cli/thor}/actions/create_file.rb +0 -0
  57. data/lib/skylight/vendor/{thor → cli/thor}/actions/create_link.rb +0 -0
  58. data/lib/skylight/vendor/{thor → cli/thor}/actions/directory.rb +0 -0
  59. data/lib/skylight/vendor/{thor → cli/thor}/actions/empty_directory.rb +0 -0
  60. data/lib/skylight/vendor/{thor → cli/thor}/actions/file_manipulation.rb +0 -0
  61. data/lib/skylight/vendor/{thor → cli/thor}/actions/inject_into_file.rb +0 -0
  62. data/lib/skylight/vendor/{thor → cli/thor}/base.rb +0 -0
  63. data/lib/skylight/vendor/{thor → cli/thor}/command.rb +0 -0
  64. data/lib/skylight/vendor/{thor → cli/thor}/core_ext/hash_with_indifferent_access.rb +0 -0
  65. data/lib/skylight/vendor/{thor → cli/thor}/core_ext/io_binary_read.rb +0 -0
  66. data/lib/skylight/vendor/{thor → cli/thor}/core_ext/ordered_hash.rb +0 -0
  67. data/lib/skylight/vendor/{thor → cli/thor}/error.rb +0 -0
  68. data/lib/skylight/vendor/{thor → cli/thor}/group.rb +0 -0
  69. data/lib/skylight/vendor/{thor → cli/thor}/invocation.rb +0 -0
  70. data/lib/skylight/vendor/{thor → cli/thor}/parser.rb +0 -0
  71. data/lib/skylight/vendor/{thor → cli/thor}/parser/argument.rb +0 -0
  72. data/lib/skylight/vendor/{thor → cli/thor}/parser/arguments.rb +0 -0
  73. data/lib/skylight/vendor/{thor → cli/thor}/parser/option.rb +0 -0
  74. data/lib/skylight/vendor/{thor → cli/thor}/parser/options.rb +0 -0
  75. data/lib/skylight/vendor/{thor → cli/thor}/rake_compat.rb +0 -0
  76. data/lib/skylight/vendor/{thor → cli/thor}/runner.rb +0 -0
  77. data/lib/skylight/vendor/{thor → cli/thor}/shell.rb +0 -0
  78. data/lib/skylight/vendor/{thor → cli/thor}/shell/basic.rb +0 -0
  79. data/lib/skylight/vendor/{thor → cli/thor}/shell/color.rb +0 -0
  80. data/lib/skylight/vendor/{thor → cli/thor}/shell/html.rb +0 -0
  81. data/lib/skylight/vendor/{thor → cli/thor}/util.rb +0 -0
  82. data/lib/skylight/vendor/{thor → cli/thor}/version.rb +0 -0
  83. data/lib/skylight/vendor/thread_safe.rb +126 -0
  84. data/lib/skylight/vendor/thread_safe/non_concurrent_cache_backend.rb +133 -0
  85. data/lib/skylight/vendor/thread_safe/synchronized_cache_backend.rb +76 -0
  86. data/lib/skylight/version.rb +2 -1
  87. data/lib/skylight/worker.rb +12 -154
  88. data/lib/skylight/worker/builder.rb +72 -0
  89. data/lib/skylight/worker/collector.rb +124 -0
  90. data/lib/skylight/worker/connection.rb +77 -0
  91. data/lib/skylight/worker/embedded.rb +6 -0
  92. data/lib/skylight/worker/server.rb +307 -0
  93. data/lib/skylight/worker/standalone.rb +356 -0
  94. metadata +89 -77
  95. data/lib/skylight/connection.rb +0 -25
  96. data/lib/skylight/json_proto.rb +0 -88
  97. data/lib/skylight/normalize.rb +0 -63
  98. data/lib/skylight/normalize/default.rb +0 -17
  99. data/lib/skylight/normalize/process_action.rb +0 -17
  100. data/lib/skylight/normalize/render_collection.rb +0 -11
  101. data/lib/skylight/normalize/render_partial.rb +0 -14
  102. data/lib/skylight/normalize/render_template.rb +0 -13
  103. data/lib/skylight/normalize/sql.rb +0 -26
  104. data/lib/skylight/normalize/start_processing.rb +0 -12
  105. data/lib/skylight/sanity_checker.rb +0 -73
  106. data/lib/skylight/trace.rb +0 -160
  107. data/lib/skylight/util/atomic.rb +0 -73
  108. data/lib/skylight/util/bytes.rb +0 -40
  109. data/lib/skylight/util/ewma.rb +0 -32
  110. data/lib/skylight/util/uuid.rb +0 -33
@@ -11,65 +11,45 @@ module Skylight
11
11
  # The path to the configuration file
12
12
  config.skylight.config_path = "config/skylight.yml"
13
13
 
14
- attr_accessor :instrumenter
14
+ initializer 'skylight.configure' do |app|
15
+ if activate?
16
+ if config = load_skylight_config(app)
17
+ @instrumenter = Instrumenter.start!(config)
15
18
 
16
- initializer "skylight.configure" do |app|
17
- if environments.include?(Rails.env.to_s)
18
- config = load_config(app)
19
-
20
- if good_to_go?(app, config)
21
- @instrumenter = Instrumenter.new(config)
22
-
23
- Rails.logger.debug "[SKYLIGHT] Installing middleware"
24
19
  app.middleware.insert 0, Middleware, @instrumenter
25
- else
26
- puts "[SKYLIGHT] Skipping Skylight boot"
20
+
21
+ Rails.logger.info "[SKYLIGHT] Skylight agent enabled"
27
22
  end
28
23
  end
29
24
  end
30
25
 
31
26
  private
32
27
 
33
- def good_to_go?(app, config)
34
- unless problems = check_for_problems(app, config)
35
- return true
36
- end
37
-
38
- problems.each do |group, problem_list|
39
- problem_list.each do |problem|
40
- puts "[SKYLIGHT] PROBLEM: #{group} #{problem}"
41
- end
42
- end
28
+ def load_skylight_config(app)
29
+ path = config_path(app)
30
+ path = nil unless File.exist?(path)
43
31
 
44
- false
45
- end
32
+ config = Config.load(path, Rails.env.to_s, ENV)
33
+ config.logger = Rails.logger
34
+ config['normalizers.render.view_paths'] = app.config.paths["app/views"].existent
35
+ config.validate!
36
+ config
46
37
 
47
- def check_for_problems(app, config)
48
- checker = SanityChecker.new
49
- checker.smoke_test(config_path(app)) || checker.sanity_check(config)
38
+ rescue ConfigError => e
39
+ Rails.logger.warn "[SKYLIGHT] #{e.message}; disabling Skylight agent"
40
+ nil
50
41
  end
51
42
 
52
- def load_config(app)
53
- config = Config.load_from_yaml(config_path(app), ENV).tap do |c|
54
- c.logger = Rails.logger
55
- end
56
-
57
- config.normalizer.view_paths = app.config.paths["app/views"].existent
58
- config
59
- rescue => e
60
- raise
61
- Rails.logger.error "[SKYLIGHT] #{e.message} (#{e.class}) - #{e.backtrace.first}"
43
+ def config_path(app)
44
+ File.expand_path(config.skylight.config_path, app.root)
62
45
  end
63
46
 
64
47
  def environments
65
48
  Array(config.skylight.environments).map { |e| e && e.to_s }.compact
66
49
  end
67
50
 
68
- def config_path(app)
69
- path = config.skylight.config_path
70
- File.expand_path(path, app.root)
51
+ def activate?
52
+ environments.include?(Rails.env.to_s)
71
53
  end
72
-
73
-
74
54
  end
75
55
  end
@@ -1,43 +1,53 @@
1
1
  module Skylight
2
- # TODO: Handle filtering out notifications that we don't care about
3
2
  class Subscriber
4
- def self.register!(config=Config.new)
5
- ActiveSupport::Notifications.subscribe nil, new(config)
6
- end
3
+
4
+ attr_reader :config
7
5
 
8
6
  def initialize(config)
9
7
  @config = config
8
+ @normalizers = Normalizers.build(config)
9
+ end
10
+
11
+ def register!
12
+ ActiveSupport::Notifications.subscribe nil, self
13
+ end
14
+
15
+ def unregister!
16
+ ActiveSupport::Notifications.unsubscribe self
10
17
  end
11
18
 
12
19
  def start(name, id, payload)
13
- return unless trace = Trace.current
20
+ return unless trace = Instrumenter.current_trace
14
21
 
15
- name, title, desc, payload = Normalize.normalize(trace, name, payload, @config.normalizer)
22
+ cat, title, desc, annot = normalize(trace, name, payload)
23
+ trace.start(now - gc_time, cat, title, desc, annot)
16
24
 
17
- trace.start(name, title, desc, payload)
25
+ trace
18
26
  end
19
27
 
20
28
  def finish(name, id, payload)
21
- return unless trace = Trace.current
22
- trace.stop
29
+ return unless trace = Instrumenter.current_trace
30
+ trace.stop(now - gc_time)
23
31
  end
24
32
 
25
- def measure(name, id, payload)
26
- return unless trace = Trace.current
27
-
28
- name, title, desc, payload = Normalize.normalize(trace, name, payload)
29
-
30
- trace.record(name, title, desc, payload)
33
+ def publish(name, *args)
34
+ # Ignored for now because nothing in rails uses it
31
35
  end
32
36
 
33
37
  private
34
38
 
35
- def controller_action(payload)
36
- "#{payload[:controller]}##{payload[:action]}"
39
+ def normalize(*args)
40
+ @normalizers.normalize(*args)
37
41
  end
38
42
 
39
- def logger
40
- @config.logger
43
+ def gc_time
44
+ GC.update
45
+ GC.time
41
46
  end
47
+
48
+ def now
49
+ Util::Clock.default.now
50
+ end
51
+
42
52
  end
43
53
  end
@@ -1,37 +1,24 @@
1
1
  module Skylight
2
2
  module Util
3
3
  class Clock
4
- MICROSEC_PER_SEC = 1.to_f / 1_000_000
5
-
6
- # Resolution is in seconds
7
- def initialize(resolution)
8
- @resolution = resolution
9
- @usec_mult = MICROSEC_PER_SEC / resolution
10
- end
11
4
 
12
5
  def now
13
- at(Time.now)
6
+ n = Time.now
7
+ n.to_i + n.usec.to_f / 1_000_000
14
8
  end
15
9
 
16
- def at(time)
17
- sec = time.to_i / @resolution
18
- usec = time.usec * @usec_mult
19
- (sec + usec).floor
10
+ def self.now
11
+ default.now
20
12
  end
21
13
 
22
- def convert(secs)
23
- (secs / @resolution).floor
14
+ def self.default
15
+ @clock ||= Clock.new
24
16
  end
25
17
 
26
- def to_seconds(clock_val)
27
- (clock_val * @resolution).floor
18
+ def self.default=(clock)
19
+ @clock = clock
28
20
  end
29
- end
30
-
31
- @clock = Clock.new(0.0001)
32
21
 
33
- def self.clock
34
- @clock
35
22
  end
36
23
  end
37
24
  end
@@ -1,4 +1,5 @@
1
- require "json"
1
+ require 'json'
2
+ require 'net/http'
2
3
 
3
4
  module Skylight
4
5
  module Util
@@ -12,86 +13,132 @@ module Skylight
12
13
  DEFLATE = 'deflate'.freeze
13
14
  GZIP = 'gzip'.freeze
14
15
 
15
- def initialize(config)
16
+ include Logging
17
+
18
+ attr_accessor :authentication, :config
19
+
20
+ def initialize(config, service = :report)
16
21
  @config = config
22
+ @ssl = config["#{service}.ssl"]
23
+ @host = config["#{service}.host"]
24
+ @port = config["#{service}.port"]
25
+ @deflate = config["#{service}.deflate"]
26
+ @authentication = config[:'authentication']
17
27
  end
18
28
 
19
- def auth(email, password)
20
- req = Net::HTTP::Get.new("/login")
21
- req.basic_auth email, password
22
- response = make_request(req, nil, ACCEPT => APPLICATION_JSON)
23
- JSON.parse(response)
29
+ def get(endpoint, hdrs = {})
30
+ request = build_request(Net::HTTP::Get, endpoint, hdrs)
31
+ execute(request)
32
+ rescue Exception => e
33
+ error "http GET failed; msg=%s", e.message
34
+ t { e.backtrace.join("\n") }
35
+ nil
24
36
  end
25
37
 
26
- def create_app(user_token, app_name)
27
- req = Net::HTTP::Post.new("/apps")
28
- req["Authorization"] = user_token
29
-
30
- body = JSON.dump(app: { name: app_name })
31
- headers = { ACCEPT => APPLICATION_JSON }
32
- headers[CONTENT_TYPE] = APPLICATION_JSON
33
- headers[CONTENT_ENCODING] = GZIP if @config.deflate?
34
- response = make_request(req, body, headers)
38
+ def post(endpoint, body, hdrs = {})
39
+ unless body.respond_to?(:to_str)
40
+ hdrs[CONTENT_TYPE] = APPLICATION_JSON
41
+ body = body.to_json
42
+ end
35
43
 
36
- JSON.parse(response)
44
+ request = build_request(Net::HTTP::Post, endpoint, hdrs, body.bytesize)
45
+ execute(request, body)
46
+ rescue Exception => e
47
+ error "http POST failed; msg=%s", e.message
48
+ t { e.backtrace.join("\n") }
49
+ nil
37
50
  end
38
51
 
39
- def post(endpoint, body)
40
- req = request(Net::HTTP::Post, endpoint, body.bytesize)
41
- make_request(req, body)
42
- rescue => e
43
- logger.error "[SKYLIGHT] POST #{@config.host}:#{@config.port}(ssl=#{@config.ssl?}) - #{e.message} - #{e.class} - #{e.backtrace.first}"
44
- debug(e.backtrace.join("\n"))
45
- end
52
+ private
46
53
 
47
- def request(type, endpoint, length=nil)
54
+ def build_request(type, endpoint, hdrs, length=nil)
48
55
  headers = {}
49
56
 
50
57
  headers[CONTENT_LENGTH] = length.to_s if length
51
- headers[AUTHORIZATION] = @config.authentication_token
52
- headers[CONTENT_TYPE] = APPLICATION_JSON if length
58
+ headers[AUTHORIZATION] = authentication if authentication
53
59
  headers[ACCEPT] = APPLICATION_JSON
54
- headers[CONTENT_ENCODING] = GZIP if @config.deflate?
60
+ headers[CONTENT_ENCODING] = GZIP if @deflate
61
+
62
+ hdrs.each do |k, v|
63
+ headers[k] = v
64
+ end
55
65
 
56
66
  type.new(endpoint, headers)
57
67
  end
58
68
 
59
- private
60
- def make_request(req, body=nil, headers={})
69
+ def execute(req, body=nil)
70
+ t { fmt "executing HTTP request; host=%s; port=%s; body=%s",
71
+ @host, @port, body && body.bytesize }
72
+
61
73
  if body
62
- body = Gzip.compress(body) if @config.deflate?
74
+ body = Gzip.compress(body) if @deflate
63
75
  req.body = body
64
76
  end
65
77
 
66
- headers.each do |name, value|
67
- req[name] = value
68
- end
69
-
70
- http = Net::HTTP.new @config.host, @config.port
78
+ http = Net::HTTP.new @host, @port
71
79
 
72
- if @config.ssl?
80
+ if @ssl
73
81
  http.use_ssl = true
74
82
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
75
83
  end
76
84
 
77
85
  http.start do |client|
78
- response = client.request(req)
86
+ res = client.request(req)
79
87
 
80
- unless response.code == '200'
81
- debug "Server responded with #{response.code}"
88
+ unless res.code =~ /2\d\d/
89
+ debug "server responded with #{res.code}"
90
+ t { fmt "body=%s", res.body }
82
91
  end
83
92
 
84
- return response.body
93
+ Response.new(res.code.to_i, res, res.body)
85
94
  end
86
95
  end
87
96
 
88
- def logger
89
- @config.logger
90
- end
97
+ class Response
98
+ attr_reader :status, :headers, :body
99
+
100
+ def initialize(status, headers, body)
101
+ @status = status
102
+ @headers = headers
103
+
104
+ if headers[CONTENT_TYPE] == APPLICATION_JSON
105
+ @body = JSON.parse(body)
106
+ else
107
+ @body = body
108
+ end
109
+ end
110
+
111
+ def success?
112
+ status >= 200 && status < 300
113
+ end
91
114
 
92
- def debug(msg)
93
- logger.debug "[SKYLIGHT] #{msg}" if logger.debug?
115
+ def to_s
116
+ body.to_s
117
+ end
118
+
119
+ def get(key)
120
+ return nil unless Hash === body
121
+
122
+ res = body
123
+ key.split('.').each do |part|
124
+ return unless res = res[part]
125
+ end
126
+ res
127
+ end
128
+
129
+ def respond_to_missing?(name, include_all=false)
130
+ super || body.respond_to?(name, include_all)
131
+ end
132
+
133
+ def method_missing(name, *args, &blk)
134
+ if respond_to_missing?(name)
135
+ body.send(name, *args, &blk)
136
+ else
137
+ super
138
+ end
139
+ end
94
140
  end
141
+
95
142
  end
96
143
  end
97
144
  end
@@ -0,0 +1,66 @@
1
+ require 'logger'
2
+
3
+ module Skylight
4
+ module Util
5
+ module Logging
6
+ if ENV[TRACE_ENV_KEY]
7
+ def trace(msg, *args)
8
+ log :DEBUG, msg, *args
9
+ end
10
+
11
+ def t
12
+ log :DEBUG, yield
13
+ end
14
+ else
15
+ def trace(*)
16
+ end
17
+
18
+ def t
19
+ end
20
+ end
21
+
22
+ def debug(msg, *args)
23
+ log :DEBUG, msg, *args
24
+ end
25
+
26
+ def info(msg, *args)
27
+ log :INFO, msg, *args
28
+ end
29
+
30
+ def warn(msg, *args)
31
+ log :WARN, msg, *args
32
+ end
33
+
34
+ def error(msg, *args)
35
+ log :ERROR, msg, *args
36
+ end
37
+
38
+ alias fmt sprintf
39
+
40
+ MAP = {
41
+ :DEBUG => Logger::DEBUG,
42
+ :INFO => Logger::INFO,
43
+ :WARN => Logger::WARN,
44
+ :ERROR => Logger::ERROR }
45
+
46
+ def log(level, msg, *args)
47
+ return unless respond_to?(:config)
48
+ return unless c = config
49
+
50
+ if logger = c.logger
51
+ if args.length > 0
52
+ logger.log MAP[level], sprintf("[SKYLIGHT] #{msg}", *args)
53
+ else
54
+ logger.log MAP[level], "[SKYLIGHT] #{msg}"
55
+ end
56
+ end
57
+ rescue Exception => e
58
+ if ENV[TRACE_ENV_KEY]
59
+ puts "[ERROR] #{e.message}"
60
+ puts e.backtrace
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end