truex-skylight 0.6.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.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +277 -0
  3. data/CLA.md +9 -0
  4. data/CONTRIBUTING.md +1 -0
  5. data/LICENSE.md +79 -0
  6. data/README.md +4 -0
  7. data/bin/skylight +3 -0
  8. data/ext/extconf.rb +186 -0
  9. data/ext/libskylight.yml +6 -0
  10. data/ext/skylight_memprof.c +115 -0
  11. data/ext/skylight_native.c +416 -0
  12. data/ext/skylight_native.h +20 -0
  13. data/lib/skylight.rb +2 -0
  14. data/lib/skylight/api.rb +79 -0
  15. data/lib/skylight/cli.rb +146 -0
  16. data/lib/skylight/compat.rb +47 -0
  17. data/lib/skylight/config.rb +498 -0
  18. data/lib/skylight/core.rb +122 -0
  19. data/lib/skylight/data/cacert.pem +3894 -0
  20. data/lib/skylight/formatters/http.rb +17 -0
  21. data/lib/skylight/gc.rb +107 -0
  22. data/lib/skylight/helpers.rb +137 -0
  23. data/lib/skylight/instrumenter.rb +290 -0
  24. data/lib/skylight/middleware.rb +75 -0
  25. data/lib/skylight/native.rb +69 -0
  26. data/lib/skylight/normalizers.rb +133 -0
  27. data/lib/skylight/normalizers/action_controller/process_action.rb +35 -0
  28. data/lib/skylight/normalizers/action_controller/send_file.rb +76 -0
  29. data/lib/skylight/normalizers/action_view/render_collection.rb +18 -0
  30. data/lib/skylight/normalizers/action_view/render_partial.rb +18 -0
  31. data/lib/skylight/normalizers/action_view/render_template.rb +18 -0
  32. data/lib/skylight/normalizers/active_record/sql.rb +79 -0
  33. data/lib/skylight/normalizers/active_support/cache.rb +50 -0
  34. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  35. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  36. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  37. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  38. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  39. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  40. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  41. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  42. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  43. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  44. data/lib/skylight/normalizers/default.rb +21 -0
  45. data/lib/skylight/normalizers/moped/query.rb +141 -0
  46. data/lib/skylight/probes.rb +91 -0
  47. data/lib/skylight/probes/excon.rb +25 -0
  48. data/lib/skylight/probes/excon/middleware.rb +65 -0
  49. data/lib/skylight/probes/net_http.rb +44 -0
  50. data/lib/skylight/probes/redis.rb +30 -0
  51. data/lib/skylight/probes/sequel.rb +30 -0
  52. data/lib/skylight/probes/sinatra.rb +74 -0
  53. data/lib/skylight/probes/tilt.rb +27 -0
  54. data/lib/skylight/railtie.rb +122 -0
  55. data/lib/skylight/sinatra.rb +4 -0
  56. data/lib/skylight/subscriber.rb +92 -0
  57. data/lib/skylight/trace.rb +191 -0
  58. data/lib/skylight/util.rb +16 -0
  59. data/lib/skylight/util/allocation_free.rb +17 -0
  60. data/lib/skylight/util/clock.rb +53 -0
  61. data/lib/skylight/util/gzip.rb +15 -0
  62. data/lib/skylight/util/hostname.rb +17 -0
  63. data/lib/skylight/util/http.rb +218 -0
  64. data/lib/skylight/util/inflector.rb +110 -0
  65. data/lib/skylight/util/logging.rb +87 -0
  66. data/lib/skylight/util/multi_io.rb +21 -0
  67. data/lib/skylight/util/native_ext_fetcher.rb +205 -0
  68. data/lib/skylight/util/platform.rb +67 -0
  69. data/lib/skylight/util/ssl.rb +50 -0
  70. data/lib/skylight/vendor/active_support/notifications.rb +207 -0
  71. data/lib/skylight/vendor/active_support/notifications/fanout.rb +159 -0
  72. data/lib/skylight/vendor/active_support/notifications/instrumenter.rb +72 -0
  73. data/lib/skylight/vendor/active_support/per_thread_registry.rb +52 -0
  74. data/lib/skylight/vendor/cli/highline.rb +1034 -0
  75. data/lib/skylight/vendor/cli/highline/color_scheme.rb +134 -0
  76. data/lib/skylight/vendor/cli/highline/compatibility.rb +16 -0
  77. data/lib/skylight/vendor/cli/highline/import.rb +41 -0
  78. data/lib/skylight/vendor/cli/highline/menu.rb +381 -0
  79. data/lib/skylight/vendor/cli/highline/question.rb +481 -0
  80. data/lib/skylight/vendor/cli/highline/simulate.rb +48 -0
  81. data/lib/skylight/vendor/cli/highline/string_extensions.rb +111 -0
  82. data/lib/skylight/vendor/cli/highline/style.rb +181 -0
  83. data/lib/skylight/vendor/cli/highline/system_extensions.rb +242 -0
  84. data/lib/skylight/vendor/cli/thor.rb +473 -0
  85. data/lib/skylight/vendor/cli/thor/actions.rb +318 -0
  86. data/lib/skylight/vendor/cli/thor/actions/create_file.rb +105 -0
  87. data/lib/skylight/vendor/cli/thor/actions/create_link.rb +60 -0
  88. data/lib/skylight/vendor/cli/thor/actions/directory.rb +119 -0
  89. data/lib/skylight/vendor/cli/thor/actions/empty_directory.rb +137 -0
  90. data/lib/skylight/vendor/cli/thor/actions/file_manipulation.rb +314 -0
  91. data/lib/skylight/vendor/cli/thor/actions/inject_into_file.rb +109 -0
  92. data/lib/skylight/vendor/cli/thor/base.rb +652 -0
  93. data/lib/skylight/vendor/cli/thor/command.rb +136 -0
  94. data/lib/skylight/vendor/cli/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  95. data/lib/skylight/vendor/cli/thor/core_ext/io_binary_read.rb +12 -0
  96. data/lib/skylight/vendor/cli/thor/core_ext/ordered_hash.rb +100 -0
  97. data/lib/skylight/vendor/cli/thor/error.rb +28 -0
  98. data/lib/skylight/vendor/cli/thor/group.rb +282 -0
  99. data/lib/skylight/vendor/cli/thor/invocation.rb +172 -0
  100. data/lib/skylight/vendor/cli/thor/parser.rb +4 -0
  101. data/lib/skylight/vendor/cli/thor/parser/argument.rb +74 -0
  102. data/lib/skylight/vendor/cli/thor/parser/arguments.rb +171 -0
  103. data/lib/skylight/vendor/cli/thor/parser/option.rb +121 -0
  104. data/lib/skylight/vendor/cli/thor/parser/options.rb +218 -0
  105. data/lib/skylight/vendor/cli/thor/rake_compat.rb +72 -0
  106. data/lib/skylight/vendor/cli/thor/runner.rb +322 -0
  107. data/lib/skylight/vendor/cli/thor/shell.rb +88 -0
  108. data/lib/skylight/vendor/cli/thor/shell/basic.rb +393 -0
  109. data/lib/skylight/vendor/cli/thor/shell/color.rb +148 -0
  110. data/lib/skylight/vendor/cli/thor/shell/html.rb +127 -0
  111. data/lib/skylight/vendor/cli/thor/util.rb +270 -0
  112. data/lib/skylight/vendor/cli/thor/version.rb +3 -0
  113. data/lib/skylight/vendor/thread_safe.rb +126 -0
  114. data/lib/skylight/vendor/thread_safe/non_concurrent_cache_backend.rb +133 -0
  115. data/lib/skylight/vendor/thread_safe/synchronized_cache_backend.rb +76 -0
  116. data/lib/skylight/version.rb +4 -0
  117. data/lib/skylight/vm/gc.rb +70 -0
  118. data/lib/sql_lexer.rb +6 -0
  119. data/lib/sql_lexer/lexer.rb +579 -0
  120. data/lib/sql_lexer/string_scanner.rb +11 -0
  121. data/lib/sql_lexer/version.rb +3 -0
  122. metadata +179 -0
@@ -0,0 +1,17 @@
1
+ module Skylight
2
+ module Formatters
3
+ module HTTP
4
+ def self.build_opts(method, scheme, host, port, path, query)
5
+ { category: "api.http.#{method.downcase}",
6
+ title: "#{method.upcase} #{host}",
7
+ annotations: {
8
+ method: method.upcase,
9
+ scheme: scheme,
10
+ host: host,
11
+ port: port ? port.to_i : nil,
12
+ path: path,
13
+ query: query }}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,107 @@
1
+ require 'thread'
2
+
3
+ module Skylight
4
+ # @api private
5
+ class GC
6
+ METHODS = [ :enable, :total_time ]
7
+ TH_KEY = :SK_GC_CURR_WINDOW
8
+ MAX_COUNT = 1000
9
+ MAX_TIME = 30_000_000
10
+
11
+ include Util::Logging
12
+
13
+ attr_reader :config
14
+
15
+ def initialize(config, profiler)
16
+ @listeners = []
17
+ @config = config
18
+ @lock = Mutex.new
19
+ @time = 0
20
+
21
+ if METHODS.all? { |m| profiler.respond_to?(m) }
22
+ @profiler = profiler
23
+ @time = @profiler.total_time
24
+ else
25
+ debug "disabling GC profiling"
26
+ end
27
+ end
28
+
29
+ def enable
30
+ @profiler.enable if @profiler
31
+ end
32
+
33
+ def track
34
+ unless @profiler
35
+ win = Window.new(nil)
36
+ else
37
+ win = Window.new(self)
38
+
39
+ @lock.synchronize do
40
+ __update
41
+ @listeners << win
42
+
43
+ # Cleanup any listeners that might have leaked
44
+ until @listeners[0].time < MAX_TIME
45
+ @listeners.shift
46
+ end
47
+
48
+ if @listeners.length > MAX_COUNT
49
+ @listeners.shift
50
+ end
51
+ end
52
+ end
53
+
54
+ win
55
+ end
56
+
57
+ def release(win)
58
+ @lock.synchronize do
59
+ @listeners.delete(win)
60
+ end
61
+ end
62
+
63
+ def update
64
+ @lock.synchronize do
65
+ __update
66
+ end
67
+
68
+ nil
69
+ end
70
+
71
+ private
72
+
73
+ def __update
74
+ time = @profiler.total_time
75
+ diff = time - @time
76
+ @time = time
77
+
78
+ if diff > 0
79
+ @listeners.each do |l|
80
+ l.add(diff)
81
+ end
82
+ end
83
+ end
84
+
85
+ class Window
86
+ attr_reader :time
87
+
88
+ def initialize(global)
89
+ @global = global
90
+ @time = 0
91
+ end
92
+
93
+ def update
94
+ @global.update if @global
95
+ end
96
+
97
+ def add(time)
98
+ @time += time
99
+ end
100
+
101
+ def release
102
+ @global.release(self) if @global
103
+ end
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,137 @@
1
+ module Skylight
2
+ # Instrumenting a specific method will cause an event to be created every time that method is called.
3
+ # The event will be inserted at the appropriate place in the Skylight trace.
4
+ #
5
+ # To instrument a method, the first thing to do is include {Skylight::Helpers Skylight::Helpers}
6
+ # into the class that you will be instrumenting. Then, annotate each method that
7
+ # you wish to instrument with {Skylight::Helpers::ClassMethods#instrument_method instrument_method}.
8
+ module Helpers
9
+
10
+ # @see Skylight::Helpers
11
+ module ClassMethods
12
+ # @api private
13
+ def method_added(name)
14
+ super
15
+
16
+ if opts = @__sk_instrument_next_method
17
+ @__sk_instrument_next_method = nil
18
+ title = "#{to_s}##{name}"
19
+ __sk_instrument_method_on(self, name, title, opts)
20
+ end
21
+ end
22
+
23
+ # @api private
24
+ def singleton_method_added(name)
25
+ super
26
+
27
+ if opts = @__sk_instrument_next_method
28
+ @__sk_instrument_next_method = nil
29
+ title = "#{to_s}.#{name}"
30
+ __sk_instrument_method_on(__sk_singleton_class, name, title, opts)
31
+ end
32
+ end
33
+
34
+ # @overload instrument_method
35
+ # Instruments the following method
36
+ #
37
+ # @example
38
+ # class MyClass
39
+ # include Skylight::Helpers
40
+ #
41
+ # instrument_method
42
+ # def my_method
43
+ # do_expensive_stuff
44
+ # end
45
+ #
46
+ # end
47
+ #
48
+ # @overload instrument_method([name], opts={})
49
+ # @param [Symbol|String] [name]
50
+ # @param [Hash] opts
51
+ # @option opts [String] :category ('app.method')
52
+ # @option opts [String] :title (ClassName#method_name)
53
+ # @option opts [String] :description
54
+ #
55
+ # You may also declare the methods to instrument at any time by passing the name
56
+ # of the method as the first argument to `instrument_method`.
57
+ #
58
+ # @example With name
59
+ # class MyClass
60
+ # include Skylight::Helpers
61
+ #
62
+ # def my_method
63
+ # do_expensive_stuff
64
+ # end
65
+ #
66
+ # instrument_method :my_method
67
+ #
68
+ # end
69
+ #
70
+ # By default, the event will be titled using the name of the class and the
71
+ # method. For example, in our previous example, the event name will be:
72
+ # +MyClass#my_method+. You can customize this by passing using the *:title* option.
73
+ #
74
+ # @example Without name
75
+ # class MyClass
76
+ # include Skylight::Helpers
77
+ #
78
+ # instrument_method title: 'Expensive work'
79
+ # def my_method
80
+ # do_expensive_stuff
81
+ # end
82
+ # end
83
+ def instrument_method(*args)
84
+ opts = args.pop if Hash === args.last
85
+
86
+ if name = args.pop
87
+ title = "#{to_s}##{name}"
88
+ __sk_instrument_method_on(self, name, title, opts || {})
89
+ else
90
+ @__sk_instrument_next_method = opts || {}
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def __sk_instrument_method_on(klass, name, title, opts)
97
+ category = (opts[:category] || "app.method").to_s
98
+ title = (opts[:title] || title).to_s
99
+ desc = opts[:description].to_s if opts[:description]
100
+
101
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
102
+ alias_method :"before_instrument_#{name}", :"#{name}"
103
+
104
+ def #{name}(*args, &blk)
105
+ span = Skylight.instrument(
106
+ category: :"#{category}",
107
+ title: #{title.inspect},
108
+ description: #{desc.inspect})
109
+
110
+ begin
111
+ before_instrument_#{name}(*args, &blk)
112
+ ensure
113
+ Skylight.done(span) if span
114
+ end
115
+ end
116
+ RUBY
117
+ end
118
+
119
+ if respond_to?(:singleton_class)
120
+ alias :__sk_singleton_class :singleton_class
121
+ else
122
+ def __sk_singleton_class
123
+ class << self; self; end
124
+ end
125
+ end
126
+ end
127
+
128
+ # @api private
129
+ def self.included(base)
130
+ base.class_eval do
131
+ @__sk_instrument_next_method = nil
132
+ extend ClassMethods
133
+ end
134
+ end
135
+
136
+ end
137
+ end
@@ -0,0 +1,290 @@
1
+ require 'thread'
2
+ require 'strscan'
3
+ require 'skylight/api'
4
+
5
+ module Skylight
6
+ # @api private
7
+ class Instrumenter
8
+ KEY = :__skylight_current_trace
9
+ LOCK = Mutex.new
10
+ DESC_LOCK = Mutex.new
11
+
12
+ TOO_MANY_UNIQUES = "<too many unique descriptions>"
13
+
14
+ include Util::Logging
15
+
16
+ class TraceInfo
17
+ def current
18
+ Thread.current[KEY]
19
+ end
20
+
21
+ def current=(trace)
22
+ Thread.current[KEY] = trace
23
+ end
24
+ end
25
+
26
+ def self.instance
27
+ @instance
28
+ end
29
+
30
+ # Do start
31
+ # @param [Config] config The config
32
+ def self.start!(config = nil)
33
+ return @instance if @instance
34
+
35
+ LOCK.synchronize do
36
+ return @instance if @instance
37
+ @instance = new(config).start!
38
+ end
39
+ rescue => e
40
+ message = sprintf("[SKYLIGHT] [#{Skylight::VERSION}] Unable to start Instrumenter; msg=%s; class=%s", e.message, e.class)
41
+ if config && config.respond_to?(:logger)
42
+ config.logger.warn message
43
+ else
44
+ warn message
45
+ end
46
+ false
47
+ end
48
+
49
+ def self.stop!
50
+ LOCK.synchronize do
51
+ return unless @instance
52
+ # This is only really helpful for getting specs to pass.
53
+ @instance.current_trace = nil
54
+
55
+ @instance.shutdown
56
+ @instance = nil
57
+ end
58
+ end
59
+
60
+ at_exit do
61
+ if RUBY_VERSION == '1.9.2'
62
+ # workaround for MRI bug losing exit status in at_exit block
63
+ # http://bugs.ruby-lang.org/issues/5218
64
+ exit_status = $!.status if $!.is_a?(SystemExit)
65
+ stop!
66
+ exit exit_status if exit_status
67
+ else
68
+ stop!
69
+ end
70
+ end
71
+
72
+ attr_reader :config, :gc, :trace_info
73
+
74
+ def self.new(config)
75
+ config ||= {}
76
+ config = Config.load(config) unless config.is_a?(Config)
77
+ config.validate!
78
+
79
+ inst = native_new(config.to_env)
80
+ inst.send(:initialize, config)
81
+ inst
82
+ end
83
+
84
+ def initialize(config)
85
+ @gc = config.gc
86
+ @config = config
87
+ @subscriber = Subscriber.new(config, self)
88
+
89
+ @trace_info = @config[:trace_info] || TraceInfo.new
90
+ @descriptions = Hash.new { |h,k| h[k] = {} }
91
+ end
92
+
93
+ def current_trace
94
+ @trace_info.current
95
+ end
96
+
97
+ def current_trace=(trace)
98
+ @trace_info.current = trace
99
+ end
100
+
101
+ def start!
102
+ # Warn if there was an error installing Skylight.
103
+ # We do this here since we can't report these issues via Gem install without stopping install entirely.
104
+ Skylight.check_install_errors(config)
105
+
106
+ unless Skylight.native?
107
+ Skylight.warn_skylight_native_missing(config)
108
+ return
109
+ end
110
+
111
+ t { "starting instrumenter" }
112
+ @config.validate!
113
+
114
+ unless validate_authentication
115
+ warn "invalid authentication token"
116
+ return
117
+ end
118
+
119
+ t { "starting native instrumenter" }
120
+ unless native_start
121
+ warn "failed to start instrumenter"
122
+ return
123
+ end
124
+
125
+ @config.gc.enable
126
+ @subscriber.register!
127
+
128
+ self
129
+
130
+ rescue Exception => e
131
+ log_error "failed to start instrumenter; msg=%s; config=%s", e.message, @config.inspect
132
+ t { e.backtrace.join("\n") }
133
+ nil
134
+ end
135
+
136
+ def shutdown
137
+ @subscriber.unregister!
138
+ native_stop
139
+ end
140
+
141
+ def trace(endpoint, cat, title=nil, desc=nil, annot=nil)
142
+ # If a trace is already in progress, continue with that one
143
+ if trace = @trace_info.current
144
+ return yield(trace) if block_given?
145
+ return trace
146
+ end
147
+
148
+ begin
149
+ trace = Trace.new(self, endpoint, Util::Clock.nanos, cat, title, desc, annot)
150
+ rescue Exception => e
151
+ log_error e.message
152
+ t { e.backtrace.join("\n") }
153
+ return
154
+ end
155
+
156
+ @trace_info.current = trace
157
+ return trace unless block_given?
158
+
159
+ begin
160
+ yield trace
161
+
162
+ ensure
163
+ @trace_info.current = nil
164
+ t { "submitting trace" }
165
+ trace.submit
166
+ end
167
+ end
168
+
169
+ def disable
170
+ @disabled = true
171
+ yield
172
+ ensure
173
+ @disabled = false
174
+ end
175
+
176
+ def disabled?
177
+ @disabled
178
+ end
179
+
180
+ @scanner = StringScanner.new('')
181
+ def self.match?(string, regex)
182
+ @scanner.string = string
183
+ @scanner.match?(regex)
184
+ end
185
+
186
+ def match?(string, regex)
187
+ self.class.match?(string, regex)
188
+ end
189
+
190
+ def done(span)
191
+ return unless trace = @trace_info.current
192
+ trace.done(span)
193
+ end
194
+
195
+ def instrument(cat, title=nil, desc=nil, annot=nil)
196
+ raise ArgumentError, 'cat is required' unless cat
197
+
198
+ unless trace = @trace_info.current
199
+ return yield if block_given?
200
+ return
201
+ end
202
+
203
+ cat = cat.to_s
204
+
205
+ unless match?(cat, CATEGORY_REGEX)
206
+ warn "invalid skylight instrumentation category; value=%s", cat
207
+ return yield if block_given?
208
+ return
209
+ end
210
+
211
+ cat = "other.#{cat}" unless match?(cat, TIER_REGEX)
212
+
213
+ unless sp = trace.instrument(cat, title, desc, annot)
214
+ return yield if block_given?
215
+ return
216
+ end
217
+
218
+ return sp unless block_given?
219
+
220
+ begin
221
+ yield sp
222
+ ensure
223
+ trace.done(sp)
224
+ end
225
+ end
226
+
227
+ def limited_description(description)
228
+ endpoint = @trace_info.current.endpoint
229
+
230
+ DESC_LOCK.synchronize do
231
+ set = @descriptions[endpoint]
232
+
233
+ if set.size >= 100
234
+ return TOO_MANY_UNIQUES
235
+ end
236
+
237
+ set[description] = true
238
+ description
239
+ end
240
+ end
241
+
242
+ def process(trace)
243
+ t { fmt "processing trace" }
244
+
245
+ if ignore?(trace)
246
+ t { fmt "ignoring trace" }
247
+ return false
248
+ end
249
+
250
+ begin
251
+ native_submit_trace(trace)
252
+ true
253
+ rescue => e
254
+ warn "failed to submit trace to worker; err=%s", e
255
+ false
256
+ end
257
+ end
258
+
259
+ def ignore?(trace)
260
+ @config.ignored_endpoints.include?(trace.endpoint)
261
+ end
262
+
263
+ # Validates that the provided authentication token is valid. This is done
264
+ # by issuing a request for a session token and checking the response
265
+ def validate_authentication
266
+ # If a session token is specified, don't bother attempting to validate
267
+ if config[:session_token]
268
+ debug "using pre-generated session token"
269
+ true
270
+ else
271
+ api = Api.new(config)
272
+ api.authentication = config[:authentication]
273
+
274
+ case res = api.validate_authentication
275
+ when :ok
276
+ true
277
+ when :invalid
278
+ false
279
+ when :unknown
280
+ warn "unable to validate authentication token"
281
+ true
282
+ else
283
+ error "[BUG] unexpected validate_token result; res=%s", res
284
+ true
285
+ end
286
+ end
287
+ end
288
+
289
+ end
290
+ end