skylight-core 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/lib/skylight/core/config.rb +454 -0
  3. data/lib/skylight/core/errors.rb +6 -0
  4. data/lib/skylight/core/fanout.rb +44 -0
  5. data/lib/skylight/core/formatters/http.rb +23 -0
  6. data/lib/skylight/core/gc.rb +107 -0
  7. data/lib/skylight/core/instrumentable.rb +144 -0
  8. data/lib/skylight/core/instrumenter.rb +249 -0
  9. data/lib/skylight/core/middleware.rb +101 -0
  10. data/lib/skylight/core/normalizers/action_controller/process_action.rb +50 -0
  11. data/lib/skylight/core/normalizers/action_controller/send_file.rb +50 -0
  12. data/lib/skylight/core/normalizers/action_view/render_collection.rb +22 -0
  13. data/lib/skylight/core/normalizers/action_view/render_partial.rb +21 -0
  14. data/lib/skylight/core/normalizers/action_view/render_template.rb +21 -0
  15. data/lib/skylight/core/normalizers/active_job/enqueue_at.rb +21 -0
  16. data/lib/skylight/core/normalizers/active_model_serializers/render.rb +26 -0
  17. data/lib/skylight/core/normalizers/active_record/instantiation.rb +17 -0
  18. data/lib/skylight/core/normalizers/active_record/sql.rb +33 -0
  19. data/lib/skylight/core/normalizers/active_support/cache.rb +20 -0
  20. data/lib/skylight/core/normalizers/active_support/cache_clear.rb +16 -0
  21. data/lib/skylight/core/normalizers/active_support/cache_decrement.rb +16 -0
  22. data/lib/skylight/core/normalizers/active_support/cache_delete.rb +16 -0
  23. data/lib/skylight/core/normalizers/active_support/cache_exist.rb +16 -0
  24. data/lib/skylight/core/normalizers/active_support/cache_fetch_hit.rb +16 -0
  25. data/lib/skylight/core/normalizers/active_support/cache_generate.rb +16 -0
  26. data/lib/skylight/core/normalizers/active_support/cache_increment.rb +16 -0
  27. data/lib/skylight/core/normalizers/active_support/cache_read.rb +16 -0
  28. data/lib/skylight/core/normalizers/active_support/cache_read_multi.rb +16 -0
  29. data/lib/skylight/core/normalizers/active_support/cache_write.rb +16 -0
  30. data/lib/skylight/core/normalizers/coach/handler_finish.rb +36 -0
  31. data/lib/skylight/core/normalizers/coach/middleware_finish.rb +23 -0
  32. data/lib/skylight/core/normalizers/couch_potato/query.rb +20 -0
  33. data/lib/skylight/core/normalizers/data_mapper/sql.rb +12 -0
  34. data/lib/skylight/core/normalizers/default.rb +27 -0
  35. data/lib/skylight/core/normalizers/elasticsearch/request.rb +20 -0
  36. data/lib/skylight/core/normalizers/faraday/request.rb +37 -0
  37. data/lib/skylight/core/normalizers/grape/endpoint.rb +30 -0
  38. data/lib/skylight/core/normalizers/grape/endpoint_render.rb +26 -0
  39. data/lib/skylight/core/normalizers/grape/endpoint_run.rb +33 -0
  40. data/lib/skylight/core/normalizers/grape/endpoint_run_filters.rb +23 -0
  41. data/lib/skylight/core/normalizers/moped/query.rb +100 -0
  42. data/lib/skylight/core/normalizers/sequel/sql.rb +12 -0
  43. data/lib/skylight/core/normalizers/sql.rb +49 -0
  44. data/lib/skylight/core/normalizers.rb +170 -0
  45. data/lib/skylight/core/probes/action_controller.rb +31 -0
  46. data/lib/skylight/core/probes/action_view.rb +37 -0
  47. data/lib/skylight/core/probes/active_model_serializers.rb +55 -0
  48. data/lib/skylight/core/probes/elasticsearch.rb +37 -0
  49. data/lib/skylight/core/probes/excon/middleware.rb +72 -0
  50. data/lib/skylight/core/probes/excon.rb +26 -0
  51. data/lib/skylight/core/probes/faraday.rb +22 -0
  52. data/lib/skylight/core/probes/grape.rb +80 -0
  53. data/lib/skylight/core/probes/httpclient.rb +46 -0
  54. data/lib/skylight/core/probes/middleware.rb +58 -0
  55. data/lib/skylight/core/probes/mongo.rb +171 -0
  56. data/lib/skylight/core/probes/mongoid.rb +21 -0
  57. data/lib/skylight/core/probes/moped.rb +39 -0
  58. data/lib/skylight/core/probes/net_http.rb +64 -0
  59. data/lib/skylight/core/probes/redis.rb +71 -0
  60. data/lib/skylight/core/probes/sequel.rb +33 -0
  61. data/lib/skylight/core/probes/sinatra.rb +69 -0
  62. data/lib/skylight/core/probes/tilt.rb +27 -0
  63. data/lib/skylight/core/probes.rb +129 -0
  64. data/lib/skylight/core/railtie.rb +166 -0
  65. data/lib/skylight/core/subscriber.rb +124 -0
  66. data/lib/skylight/core/test.rb +98 -0
  67. data/lib/skylight/core/trace.rb +190 -0
  68. data/lib/skylight/core/user_config.rb +61 -0
  69. data/lib/skylight/core/util/allocation_free.rb +26 -0
  70. data/lib/skylight/core/util/clock.rb +56 -0
  71. data/lib/skylight/core/util/deploy.rb +132 -0
  72. data/lib/skylight/core/util/gzip.rb +21 -0
  73. data/lib/skylight/core/util/inflector.rb +112 -0
  74. data/lib/skylight/core/util/logging.rb +127 -0
  75. data/lib/skylight/core/util/platform.rb +77 -0
  76. data/lib/skylight/core/util/proxy.rb +13 -0
  77. data/lib/skylight/core/util.rb +14 -0
  78. data/lib/skylight/core/vendor/active_support/notifications.rb +207 -0
  79. data/lib/skylight/core/vendor/active_support/per_thread_registry.rb +52 -0
  80. data/lib/skylight/core/vendor/thread_safe/non_concurrent_cache_backend.rb +133 -0
  81. data/lib/skylight/core/vendor/thread_safe/synchronized_cache_backend.rb +76 -0
  82. data/lib/skylight/core/vendor/thread_safe.rb +126 -0
  83. data/lib/skylight/core/version.rb +6 -0
  84. data/lib/skylight/core/vm/gc.rb +70 -0
  85. data/lib/skylight/core.rb +99 -0
  86. metadata +254 -0
@@ -0,0 +1,56 @@
1
+ module Skylight::Core
2
+ module Util
3
+ # A more precise clock
4
+ class Clock
5
+
6
+ def self.use_native!
7
+ class_eval do
8
+ def tick
9
+ native_hrtime
10
+ end
11
+ end
12
+ end
13
+
14
+ def tick
15
+ now = Time.now
16
+ now.to_i * 1_000_000_000 + now.usec * 1_000
17
+ end
18
+
19
+ # TODO: rename to secs
20
+ def absolute_secs
21
+ Time.now.to_i
22
+ end
23
+
24
+ # TODO: remove
25
+ def nanos
26
+ tick
27
+ end
28
+
29
+ # TODO: remove
30
+ def secs
31
+ nanos / 1_000_000_000
32
+ end
33
+
34
+ def self.absolute_secs
35
+ default.absolute_secs
36
+ end
37
+
38
+ def self.nanos
39
+ default.nanos
40
+ end
41
+
42
+ def self.secs
43
+ default.secs
44
+ end
45
+
46
+ def self.default
47
+ @clock ||= Clock.new
48
+ end
49
+
50
+ def self.default=(clock)
51
+ @clock = clock
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,132 @@
1
+ require 'json'
2
+ require 'uri'
3
+ require 'skylight/core/util/logging'
4
+
5
+ module Skylight::Core
6
+ module Util
7
+
8
+ module Deploy
9
+
10
+ def self.build(config)
11
+ DEPLOY_TYPES.each do |type|
12
+ deploy = type.new(config)
13
+ return deploy if deploy.id
14
+ end
15
+ nil
16
+ end
17
+
18
+ class EmptyDeploy
19
+
20
+ attr_reader :config
21
+ attr_reader :timestamp
22
+
23
+ def initialize(config)
24
+ @config = config
25
+ @timestamp = Time.now.to_i
26
+ end
27
+
28
+ def id
29
+ git_sha
30
+ end
31
+
32
+ def git_sha
33
+ nil
34
+ end
35
+
36
+ def description
37
+ nil
38
+ end
39
+
40
+ def to_query_string
41
+ URI.encode_www_form(
42
+ timestamp: timestamp,
43
+ deploy_id: id.to_s[0..100], # Keep this sane
44
+ git_sha: git_sha ? git_sha[0..40] : nil, # A valid SHA will never exceed 40
45
+ description: description ? description[0..255] : nil) # Avoid massive descriptions
46
+ end
47
+
48
+ end
49
+
50
+ class DefaultDeploy < EmptyDeploy
51
+ include Logging
52
+
53
+ def initialize(*)
54
+ super
55
+ if description && !id
56
+ warn "The configured deploy will be ignored as an id or git_sha must be provided."
57
+ end
58
+ end
59
+
60
+ def id
61
+ config.get(:'deploy.id') || git_sha
62
+ end
63
+
64
+ def git_sha
65
+ config.get(:'deploy.git_sha')
66
+ end
67
+
68
+ def description
69
+ config.get(:'deploy.description')
70
+ end
71
+
72
+ end
73
+
74
+ class HerokuDeploy < EmptyDeploy
75
+
76
+ def initialize(*)
77
+ super
78
+ @info = get_info
79
+ end
80
+
81
+ def id
82
+ @info ? @info['id'] : nil
83
+ end
84
+
85
+ def git_sha
86
+ @info ? @info['commit'] : nil
87
+ end
88
+
89
+ def description
90
+ @info ? @info['description'] : nil
91
+ end
92
+
93
+ private
94
+
95
+ def get_info
96
+ info_path = config[:'heroku.dyno_info_path']
97
+
98
+ if File.exist?(info_path)
99
+ if info = JSON.parse(File.read(info_path))
100
+ info['release']
101
+ end
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ class GitDeploy < EmptyDeploy
108
+
109
+ attr_reader :git_sha, :description
110
+
111
+ def initialize(*)
112
+ super
113
+ @git_sha, @description = get_info
114
+ end
115
+
116
+ private
117
+
118
+ def get_info
119
+ Dir.chdir(config.root) do
120
+ info = `git log -1 --pretty="%H %s" 2>&1`
121
+ info.split(" ", 2).map(&:strip) if $?.success?
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ DEPLOY_TYPES = [DefaultDeploy, HerokuDeploy, GitDeploy]
128
+
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,21 @@
1
+ require 'zlib'
2
+
3
+ module Skylight::Core
4
+ module Util
5
+ # Provides Gzip compressing support
6
+ module Gzip
7
+
8
+ # Compress a string with Gzip
9
+ #
10
+ # @param str [String] uncompressed string
11
+ # @return [String] compressed string
12
+ def self.compress(str)
13
+ output = StringIO.new
14
+ gz = Zlib::GzipWriter.new(output)
15
+ gz.write(str)
16
+ gz.close
17
+ output.string
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,112 @@
1
+ module Skylight::Core
2
+ module Util
3
+
4
+ # String Inflector methods
5
+ #
6
+ # From https://github.com/rails/rails/blob/f8e5022c73679f41db9bb6743179bab4571fb28e/activesupport/lib/active_support/inflector/methods.rb
7
+ module Inflector
8
+ extend self
9
+
10
+ # Tries to find a constant with the name specified in the argument string.
11
+ #
12
+ # 'Module'.constantize # => Module
13
+ # 'Test::Unit'.constantize # => Test::Unit
14
+ #
15
+ # The name is assumed to be the one of a top-level constant, no matter
16
+ # whether it starts with "::" or not. No lexical context is taken into
17
+ # account:
18
+ #
19
+ # C = 'outside'
20
+ # module M
21
+ # C = 'inside'
22
+ # C # => 'inside'
23
+ # 'C'.constantize # => 'outside', same as ::C
24
+ # end
25
+ #
26
+ # NameError is raised when the name is not in CamelCase or the constant is
27
+ # unknown.
28
+ def constantize(camel_cased_word)
29
+ names = camel_cased_word.split('::')
30
+
31
+ # Trigger a builtin NameError exception including the ill-formed constant in the message.
32
+ Object.const_get(camel_cased_word) if names.empty?
33
+
34
+ # Remove the first blank element in case of '::ClassName' notation.
35
+ names.shift if names.size > 1 && names.first.empty?
36
+
37
+ names.inject(Object) do |constant, name|
38
+ if constant == Object
39
+ constant.const_get(name)
40
+ else
41
+ candidate = constant.const_get(name)
42
+ next candidate if constant.const_defined?(name, false)
43
+ next candidate unless Object.const_defined?(name)
44
+
45
+ # Go down the ancestors to check it it's owned
46
+ # directly before we reach Object or the end of ancestors.
47
+ constant = constant.ancestors.inject do |const, ancestor|
48
+ break const if ancestor == Object
49
+ break ancestor if ancestor.const_defined?(name, false)
50
+ const
51
+ end
52
+
53
+ # owner is in Object, so raise
54
+ constant.const_get(name, false)
55
+ end
56
+ end
57
+ end
58
+
59
+ # Tries to find a constant with the name specified in the argument string.
60
+ #
61
+ # 'Module'.safe_constantize # => Module
62
+ # 'Test::Unit'.safe_constantize # => Test::Unit
63
+ #
64
+ # The name is assumed to be the one of a top-level constant, no matter
65
+ # whether it starts with "::" or not. No lexical context is taken into
66
+ # account:
67
+ #
68
+ # C = 'outside'
69
+ # module M
70
+ # C = 'inside'
71
+ # C # => 'inside'
72
+ # 'C'.safe_constantize # => 'outside', same as ::C
73
+ # end
74
+ #
75
+ # +nil+ is returned when the name is not in CamelCase or the constant (or
76
+ # part of it) is unknown.
77
+ #
78
+ # 'blargle'.safe_constantize # => nil
79
+ # 'UnknownModule'.safe_constantize # => nil
80
+ # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
81
+ def safe_constantize(camel_cased_word)
82
+ constantize(camel_cased_word)
83
+ rescue NameError => e
84
+ raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
85
+ e.name.to_s == camel_cased_word.to_s
86
+ rescue ArgumentError => e
87
+ raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
88
+ end
89
+
90
+ private
91
+
92
+ # Mount a regular expression that will match part by part of the constant.
93
+ #
94
+ # const_regexp("Foo::Bar::Baz") # => /(Foo(::Bar(::Baz)?)?|Bar|Baz)/
95
+ # const_regexp("::") # => /::/
96
+ #
97
+ # NOTE: We also add each part in singly, because sometimes a search for a missing
98
+ # constant like Skylight::Foo::Bar will return an error just saying Foo was missing
99
+ def const_regexp(camel_cased_word) #:nodoc:
100
+ parts = camel_cased_word.split("::")
101
+
102
+ return Regexp.escape(camel_cased_word) if parts.empty?
103
+
104
+ regexp = parts.reverse.inject do |acc, part|
105
+ part.empty? ? acc : "#{part}(::#{acc})?"
106
+ end
107
+
108
+ "(" + ([regexp] + parts[1..-1]).join('|') + ")"
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,127 @@
1
+ require 'logger'
2
+
3
+ module Skylight::Core
4
+ module Util
5
+ # Log both to the specified logger and STDOUT
6
+ class AlertLogger
7
+ def initialize(logger)
8
+ @logger = logger
9
+ end
10
+
11
+ def write(*args)
12
+ STDERR.write *args
13
+
14
+ # Try to avoid writing to STDOUT/STDERR twice
15
+ logger_logdev = @logger.instance_variable_get(:@logdev)
16
+ logger_out = logger_logdev && logger_logdev.respond_to?(:dev) ? logger_logdev.dev : nil
17
+ if logger_out != STDOUT && logger_out != STDERR
18
+ @logger.<<(*args)
19
+ end
20
+ end
21
+
22
+ def close
23
+ end
24
+ end
25
+
26
+ module Logging
27
+
28
+ def self.trace?
29
+ ENV[Skylight::TRACE_ENV_KEY]
30
+ end
31
+
32
+ if trace?
33
+ # The second set is picked up by YARD
34
+ def trace(msg, *args)
35
+ log :debug, msg, *args
36
+ end
37
+
38
+ def t
39
+ log :debug, yield
40
+ end
41
+ else
42
+ # Logs if `ENV[TRACE_ENV_KEY]` is set.
43
+ #
44
+ # @param (see #debug)
45
+ #
46
+ # See {TRACE_ENV_KEY}.
47
+ def trace(msg, *args)
48
+ end
49
+
50
+ # Evaluates and logs the result of the block if `ENV[TRACE_ENV_KEY]` is set
51
+ #
52
+ # @yield block to be evaluted
53
+ # @yieldreturn arguments for {#debug}
54
+ #
55
+ # See {TRACE_ENV_KEY}.
56
+ def t
57
+ end
58
+ end
59
+
60
+ # @param msg (see #log)
61
+ # @param args (see #log)
62
+ def debug(msg, *args)
63
+ log :debug, msg, *args
64
+ end
65
+
66
+ # @param msg (see #log)
67
+ # @param args (see #log)
68
+ def info(msg, *args)
69
+ log :info, msg, *args
70
+ end
71
+
72
+ # @param msg (see #log)
73
+ # @param args (see #log)
74
+ def warn(msg, *args)
75
+ log :warn, msg, *args
76
+ end
77
+
78
+ # @param msg (see #log)
79
+ # @param args (see #log)
80
+ def error(msg, *args)
81
+ log :error, msg, *args
82
+ raise sprintf(msg, *args) if ENV['SKYLIGHT_RAISE_ON_ERROR']
83
+ end
84
+
85
+ alias log_trace trace
86
+ alias log_debug debug
87
+ alias log_info info
88
+ alias log_warn warn
89
+ alias log_error error
90
+
91
+ # Alias for `Kernel#sprintf`
92
+ # @return [String]
93
+ def fmt(*args)
94
+ sprintf(*args)
95
+ end
96
+
97
+ # @param level [String,Symbol] the method on `logger` to use for logging
98
+ # @param msg [String] the message to log
99
+ # @param args [Array] values for `Kernel#sprintf` on `msg`
100
+ def log(level, msg, *args)
101
+ c = if respond_to?(:config)
102
+ config
103
+ elsif self.is_a?(Config)
104
+ self
105
+ end
106
+
107
+ return unless c
108
+
109
+ if logger = c.logger
110
+ return unless logger.respond_to?(level)
111
+
112
+ if args.length > 0
113
+ logger.send level, sprintf("[SKYLIGHT] [#{Skylight::Core::VERSION}] #{msg}", *args)
114
+ else
115
+ logger.send level, "[SKYLIGHT] [#{Skylight::Core::VERSION}] #{msg}"
116
+ end
117
+ end
118
+ rescue Exception => e
119
+ if ENV[Skylight::TRACE_ENV_KEY]
120
+ puts "[ERROR] #{e.message}"
121
+ puts e.backtrace
122
+ end
123
+ end
124
+
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,77 @@
1
+ require 'rbconfig'
2
+
3
+ # Used from extconf and to load libskylight
4
+ module Skylight
5
+ module Core
6
+ module Util
7
+ module Platform
8
+ # Normalize the platform OS
9
+ OS = case os = RbConfig::CONFIG['host_os'].downcase
10
+ when /linux/
11
+ # The official ruby-alpine Docker containers pre-build Ruby. As a result,
12
+ # Ruby doesn't know that it's on a musl-based platform. `ldd` is the
13
+ # only reliable way to detect musl that we've found.
14
+ # See https://github.com/skylightio/skylight-ruby/issues/92
15
+ if ENV['SKYLIGHT_MUSL'] || `ldd --version 2>&1` =~ /musl/
16
+ "linux-musl"
17
+ else
18
+ "linux"
19
+ end
20
+ when /darwin/
21
+ "darwin"
22
+ when /freebsd/
23
+ "freebsd"
24
+ when /netbsd/
25
+ "netbsd"
26
+ when /openbsd/
27
+ "openbsd"
28
+ when /sunos|solaris/
29
+ "solaris"
30
+ when /mingw|mswin/
31
+ "windows"
32
+ else
33
+ os
34
+ end
35
+
36
+ # Normalize the platform CPU
37
+ ARCH = case cpu = RbConfig::CONFIG['host_cpu'].downcase
38
+ when /amd64|x86_64/
39
+ "x86_64"
40
+ when /i?86|x86|i86pc/
41
+ "x86"
42
+ when /ppc|powerpc/
43
+ "powerpc"
44
+ when /^arm/
45
+ "arm"
46
+ else
47
+ cpu
48
+ end
49
+
50
+ LIBEXT = case OS
51
+ when /darwin/
52
+ 'dylib'
53
+ when /linux|bsd|solaris/
54
+ 'so'
55
+ when /windows|cygwin/
56
+ 'dll'
57
+ else
58
+ 'so'
59
+ end
60
+
61
+ TUPLE = "#{ARCH}-#{OS}"
62
+
63
+ def self.tuple
64
+ TUPLE
65
+ end
66
+
67
+ def self.libext
68
+ LIBEXT
69
+ end
70
+
71
+ def self.dlext
72
+ RbConfig::CONFIG['DLEXT']
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,13 @@
1
+ module Skylight::Core
2
+ module Util
3
+ module Proxy
4
+ def self.detect_url(env)
5
+ u = env['HTTP_PROXY'] || env['http_proxy']
6
+ if u && !u.empty?
7
+ u = "http://#{u}" unless u =~ %r[://]
8
+ u
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Skylight::Core
2
+ # @api private
3
+ module Util
4
+ # Used from the main lib
5
+ require 'skylight/core/util/allocation_free'
6
+ require 'skylight/core/util/clock'
7
+ require 'skylight/core/util/deploy'
8
+ require 'skylight/core/util/logging'
9
+
10
+ # Used from the CLI
11
+ autoload :Gzip, 'skylight/core/util/gzip'
12
+ autoload :Inflector, 'skylight/core/util/inflector'
13
+ end
14
+ end