skylight 4.3.2 → 5.0.0.beta

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -5
  3. data/CONTRIBUTING.md +1 -7
  4. data/ext/extconf.rb +4 -3
  5. data/ext/libskylight.yml +5 -6
  6. data/ext/skylight_native.c +22 -99
  7. data/lib/skylight.rb +204 -14
  8. data/lib/skylight/api.rb +7 -3
  9. data/lib/skylight/cli.rb +4 -3
  10. data/lib/skylight/cli/doctor.rb +3 -2
  11. data/lib/skylight/cli/merger.rb +6 -4
  12. data/lib/skylight/config.rb +603 -126
  13. data/lib/skylight/deprecation.rb +15 -0
  14. data/lib/skylight/errors.rb +17 -2
  15. data/lib/skylight/extensions.rb +99 -0
  16. data/lib/skylight/extensions/source_location.rb +249 -0
  17. data/lib/skylight/fanout.rb +0 -0
  18. data/lib/skylight/formatters/http.rb +19 -0
  19. data/lib/skylight/gc.rb +109 -0
  20. data/lib/skylight/helpers.rb +18 -2
  21. data/lib/skylight/instrumenter.rb +325 -15
  22. data/lib/skylight/middleware.rb +138 -1
  23. data/lib/skylight/native.rb +51 -1
  24. data/lib/skylight/native_ext_fetcher.rb +2 -1
  25. data/lib/skylight/normalizers.rb +151 -0
  26. data/lib/skylight/normalizers/action_controller/process_action.rb +69 -0
  27. data/lib/skylight/normalizers/action_controller/send_file.rb +50 -0
  28. data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
  29. data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
  30. data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
  31. data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
  32. data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
  33. data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
  34. data/lib/skylight/normalizers/active_job/perform.rb +81 -0
  35. data/lib/skylight/normalizers/active_model_serializers/render.rb +28 -0
  36. data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
  37. data/lib/skylight/normalizers/active_record/sql.rb +12 -0
  38. data/lib/skylight/normalizers/active_storage.rb +30 -0
  39. data/lib/skylight/normalizers/active_support/cache.rb +22 -0
  40. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  41. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  42. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  43. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  44. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  45. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  46. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  47. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  48. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  49. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  50. data/lib/skylight/normalizers/coach/handler_finish.rb +46 -0
  51. data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
  52. data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
  53. data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
  54. data/lib/skylight/normalizers/default.rb +32 -0
  55. data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
  56. data/lib/skylight/normalizers/faraday/request.rb +40 -0
  57. data/lib/skylight/normalizers/grape/endpoint.rb +34 -0
  58. data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
  59. data/lib/skylight/normalizers/grape/endpoint_run.rb +41 -0
  60. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +22 -0
  61. data/lib/skylight/normalizers/grape/format_response.rb +20 -0
  62. data/lib/skylight/normalizers/graphiti/render.rb +22 -0
  63. data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
  64. data/lib/skylight/normalizers/graphql/base.rb +131 -0
  65. data/lib/skylight/normalizers/render.rb +81 -0
  66. data/lib/skylight/normalizers/sequel/sql.rb +12 -0
  67. data/lib/skylight/normalizers/sql.rb +44 -0
  68. data/lib/skylight/probes.rb +153 -0
  69. data/lib/skylight/probes/action_controller.rb +48 -0
  70. data/lib/skylight/probes/action_dispatch.rb +2 -0
  71. data/lib/skylight/probes/action_dispatch/request_id.rb +29 -0
  72. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +28 -0
  73. data/lib/skylight/probes/action_view.rb +43 -0
  74. data/lib/skylight/probes/active_job.rb +29 -0
  75. data/lib/skylight/probes/active_job_enqueue.rb +37 -0
  76. data/lib/skylight/probes/active_model_serializers.rb +54 -0
  77. data/lib/skylight/probes/delayed_job.rb +62 -0
  78. data/lib/skylight/probes/elasticsearch.rb +38 -0
  79. data/lib/skylight/probes/excon.rb +25 -0
  80. data/lib/skylight/probes/excon/middleware.rb +66 -0
  81. data/lib/skylight/probes/faraday.rb +23 -0
  82. data/lib/skylight/probes/graphql.rb +43 -0
  83. data/lib/skylight/probes/httpclient.rb +44 -0
  84. data/lib/skylight/probes/middleware.rb +125 -0
  85. data/lib/skylight/probes/mongo.rb +163 -0
  86. data/lib/skylight/probes/mongoid.rb +13 -0
  87. data/lib/skylight/probes/net_http.rb +55 -0
  88. data/lib/skylight/probes/redis.rb +60 -0
  89. data/lib/skylight/probes/sequel.rb +33 -0
  90. data/lib/skylight/probes/sinatra.rb +63 -0
  91. data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
  92. data/lib/skylight/probes/tilt.rb +27 -0
  93. data/lib/skylight/railtie.rb +162 -18
  94. data/lib/skylight/sidekiq.rb +43 -0
  95. data/lib/skylight/subscriber.rb +110 -0
  96. data/lib/skylight/test.rb +146 -0
  97. data/lib/skylight/trace.rb +301 -10
  98. data/lib/skylight/user_config.rb +61 -0
  99. data/lib/skylight/util.rb +12 -0
  100. data/lib/skylight/util/allocation_free.rb +26 -0
  101. data/lib/skylight/util/clock.rb +56 -0
  102. data/lib/skylight/util/component.rb +5 -2
  103. data/lib/skylight/util/deploy.rb +4 -4
  104. data/lib/skylight/util/gzip.rb +20 -0
  105. data/lib/skylight/util/http.rb +4 -10
  106. data/lib/skylight/util/instrumenter_method.rb +26 -0
  107. data/lib/skylight/util/logging.rb +138 -0
  108. data/lib/skylight/util/lru_cache.rb +42 -0
  109. data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
  110. data/lib/skylight/version.rb +5 -1
  111. data/lib/skylight/vm/gc.rb +68 -0
  112. metadata +110 -11
@@ -0,0 +1,56 @@
1
+ module Skylight
2
+ module Util
3
+ # A more precise clock
4
+ class Clock
5
+ def self.use_native!
6
+ class_eval do
7
+ def tick
8
+ native_hrtime
9
+ end
10
+ end
11
+ end
12
+
13
+ # rubocop:disable Lint/DuplicateMethods
14
+ def tick
15
+ now = Time.now
16
+ now.to_i * 1_000_000_000 + now.usec * 1_000
17
+ end
18
+ # rubocop:enable Lint/DuplicateMethods
19
+
20
+ # TODO: rename to secs
21
+ def absolute_secs
22
+ Time.now.to_i
23
+ end
24
+
25
+ # TODO: remove
26
+ def nanos
27
+ tick
28
+ end
29
+
30
+ # TODO: remove
31
+ def secs
32
+ nanos / 1_000_000_000
33
+ end
34
+
35
+ class << self
36
+ def absolute_secs
37
+ default.absolute_secs
38
+ end
39
+
40
+ def nanos
41
+ default.nanos
42
+ end
43
+
44
+ def secs
45
+ default.secs
46
+ end
47
+
48
+ def default
49
+ @default ||= Clock.new
50
+ end
51
+
52
+ attr_writer :default
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "uri"
3
4
 
4
5
  module Skylight
@@ -6,7 +7,7 @@ module Skylight
6
7
  class Component
7
8
  attr_accessor :environment, :name
8
9
 
9
- NAME_FORMAT = /\A[a-zA-Z0-9_-]+\z/
10
+ NAME_FORMAT = /\A[a-zA-Z0-9_-]+\z/.freeze
10
11
  DEFAULT_NAME = "web"
11
12
  WORKER_NAME = "worker"
12
13
  DEFAULT_ENVIRONMENT = "production"
@@ -16,6 +17,7 @@ module Skylight
16
17
  @name = resolve_name(name, force_worker)
17
18
 
18
19
  raise ArgumentError, "environment can't be blank" if @environment.empty?
20
+
19
21
  validate_string!(@environment, "environment")
20
22
  validate_string!(@name, "name")
21
23
  end
@@ -40,7 +42,7 @@ module Skylight
40
42
  def as_json(*)
41
43
  {
42
44
  component: name,
43
- env: environment
45
+ env: environment
44
46
  }
45
47
  end
46
48
 
@@ -64,6 +66,7 @@ module Skylight
64
66
 
65
67
  def validate_string!(string, kind)
66
68
  return true if string =~ NAME_FORMAT
69
+
67
70
  raise ArgumentError, "#{kind} can only contain lowercase letters, numbers, and dashes"
68
71
  end
69
72
  end
@@ -1,5 +1,5 @@
1
1
  require "json"
2
- require "skylight/core/util/logging"
2
+ require "skylight/util/logging"
3
3
 
4
4
  module Skylight
5
5
  module Util
@@ -35,8 +35,8 @@ module Skylight
35
35
 
36
36
  def to_query_hash
37
37
  hash = {
38
- timestamp: timestamp,
39
- deploy_id: id.to_s[0..100] # Keep this sane
38
+ timestamp: timestamp,
39
+ deploy_id: id.to_s[0..100] # Keep this sane
40
40
  }
41
41
  hash[:git_sha] = git_sha.to_s[0..40] if git_sha # A valid SHA will never exceed 40
42
42
  hash[:description] = description[0..255] if description # Avoid massive descriptions
@@ -45,7 +45,7 @@ module Skylight
45
45
  end
46
46
 
47
47
  class DefaultDeploy < EmptyDeploy
48
- include Core::Util::Logging
48
+ include Util::Logging
49
49
 
50
50
  def initialize(*)
51
51
  super
@@ -0,0 +1,20 @@
1
+ require "zlib"
2
+
3
+ module Skylight
4
+ module Util
5
+ # Provides Gzip compressing support
6
+ module Gzip
7
+ # Compress a string with Gzip
8
+ #
9
+ # @param str [String] uncompressed string
10
+ # @return [String] compressed string
11
+ def self.compress(str)
12
+ output = StringIO.new
13
+ gz = Zlib::GzipWriter.new(output)
14
+ gz.write(str)
15
+ gz.close
16
+ output.string
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,7 +3,7 @@ require "json"
3
3
  require "openssl"
4
4
  require "net/http"
5
5
  require "net/https"
6
- require "skylight/core/util/gzip"
6
+ require "skylight/util/gzip"
7
7
  require "skylight/util/ssl"
8
8
 
9
9
  module Skylight
@@ -19,7 +19,7 @@ module Skylight
19
19
  DEFLATE = "deflate".freeze
20
20
  GZIP = "gzip".freeze
21
21
 
22
- include Core::Util::Logging
22
+ include Logging
23
23
 
24
24
  attr_accessor :authentication
25
25
  attr_reader :host, :port, :config
@@ -118,7 +118,7 @@ module Skylight
118
118
 
119
119
  yield res
120
120
  ensure
121
- client.finish if client
121
+ client&.finish
122
122
  end
123
123
 
124
124
  def execute(req, body = nil)
@@ -189,13 +189,7 @@ module Skylight
189
189
  end
190
190
 
191
191
  def get(key)
192
- return nil unless body.is_a?(Hash)
193
-
194
- res = body
195
- key.split(".").each do |part|
196
- return unless (res = res[part])
197
- end
198
- res
192
+ body.dig(*key.split(".")) if body.is_a?(Hash)
199
193
  end
200
194
 
201
195
  def respond_to_missing?(name, include_all = false)
@@ -0,0 +1,26 @@
1
+ module Skylight
2
+ module Util
3
+ module InstrumenterMethod
4
+ def instrumenter_method(name, block: false)
5
+ if block
6
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
7
+ def #{name}(*args)
8
+ unless instrumenter
9
+ return yield if block_given?
10
+ return
11
+ end
12
+
13
+ instrumenter.#{name}(*args) { yield }
14
+ end
15
+ RUBY
16
+ else
17
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
18
+ def #{name}(*args)
19
+ instrumenter&.#{name}(*args)
20
+ end
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,138 @@
1
+ require "logger"
2
+
3
+ module Skylight
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&.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; end
23
+ end
24
+
25
+ module Logging
26
+ def log_context
27
+ {}
28
+ end
29
+
30
+ def trace?
31
+ !!ENV[-"SKYLIGHT_ENABLE_TRACE_LOGS"]
32
+ end
33
+
34
+ def raise_on_error?
35
+ !!ENV[-"SKYLIGHT_RAISE_ON_ERROR"]
36
+ end
37
+
38
+ # Logs if tracing
39
+ #
40
+ # @param (see #debug)
41
+ #
42
+ # See {trace?}.
43
+ def trace(msg, *args)
44
+ return unless trace?
45
+
46
+ log :debug, msg, *args
47
+ end
48
+
49
+ # Evaluates and logs the result of the block if tracing
50
+ #
51
+ # @yield block to be evaluted
52
+ # @yieldreturn arguments for {#debug}
53
+ #
54
+ # See {trace?}.
55
+ def t
56
+ return unless trace?
57
+
58
+ log :debug, yield
59
+ end
60
+
61
+ # @param msg (see #log)
62
+ # @param args (see #log)
63
+ def debug(msg, *args)
64
+ log :debug, msg, *args
65
+ end
66
+
67
+ # @param msg (see #log)
68
+ # @param args (see #log)
69
+ def info(msg, *args)
70
+ log :info, msg, *args
71
+ end
72
+
73
+ # @param msg (see #log)
74
+ # @param args (see #log)
75
+ def warn(msg, *args)
76
+ log :warn, msg, *args
77
+ end
78
+
79
+ # @param msg (see #log)
80
+ # @param args (see #log)
81
+ def error(msg, *args)
82
+ log :error, msg, *args
83
+ raise format(msg, *args) if raise_on_error?
84
+ end
85
+
86
+ alias log_trace trace
87
+ alias log_debug debug
88
+ alias log_info info
89
+ alias log_warn warn
90
+ alias log_error error
91
+
92
+ # Alias for `Kernel#sprintf`
93
+ # @return [String]
94
+ def fmt(*args)
95
+ sprintf(*args)
96
+ end
97
+
98
+ def config_for_logging
99
+ if respond_to?(:config)
100
+ config
101
+ elsif is_a?(Skylight::Config)
102
+ self
103
+ end
104
+ end
105
+
106
+ # @param level [String,Symbol] the method on `logger` to use for logging
107
+ # @param msg [String] the message to log
108
+ # @param args [Array] values for `Kernel#sprintf` on `msg`
109
+ def log(level, msg, *args)
110
+ c = config_for_logging
111
+ logger = c ? c.logger : nil
112
+
113
+ msg = log_context.map { |(k, v)| "#{k}=#{v}; " }.join << msg
114
+
115
+ if logger
116
+ if logger.respond_to?(level)
117
+ if !args.empty?
118
+ logger.send level, format(msg, *args)
119
+ else
120
+ logger.send level, msg
121
+ end
122
+ return # rubocop:disable Style/RedundantReturn
123
+ else
124
+ Kernel.warn "Invalid logger"
125
+ end
126
+ # Fallback to stderr for warn and error levels
127
+ elsif %i[warn error].include?(level)
128
+ $stderr.puts format("[SKYLIGHT] #{msg}", *args)
129
+ end
130
+ rescue Exception => e
131
+ if trace?
132
+ puts "[ERROR] #{e.message}"
133
+ puts e.backtrace
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,42 @@
1
+ # Based on code by Sam Saffron: https://stackoverflow.com/a/16161783/181916
2
+ module Skylight
3
+ module Util
4
+ class LruCache
5
+ def initialize(max_size)
6
+ @max_size = max_size
7
+ @data = {}
8
+ end
9
+
10
+ def max_size=(size)
11
+ raise ArgumentError, :max_size if @max_size < 1
12
+
13
+ @max_size = size
14
+ while @data.size > @max_size
15
+ @data.shift
16
+ end
17
+ end
18
+
19
+ # Individual hash operations here are atomic in MRI.
20
+ def fetch(key)
21
+ found = true
22
+ value = @data.delete(key) { found = false }
23
+
24
+ if !found && block_given?
25
+ value = yield
26
+ end
27
+
28
+ @data[key] = value if value
29
+
30
+ if !found && value && @data.length > @max_size
31
+ @data.shift
32
+ end
33
+
34
+ value
35
+ end
36
+
37
+ def clear
38
+ @data.clear
39
+ end
40
+ end
41
+ end
42
+ end
@@ -11,7 +11,7 @@ class Thor
11
11
  # class Default < Thor
12
12
  # include Thor::RakeCompat
13
13
  #
14
- # RSpec::Core::RakeTask.new(:spec) do |t|
14
+ # RSpec::RakeTask.new(:spec) do |t|
15
15
  # t.spec_opts = ['--options', "./.rspec"]
16
16
  # t.spec_files = FileList['spec/**/*_spec.rb']
17
17
  # end
@@ -1,3 +1,7 @@
1
1
  module Skylight
2
- VERSION = "4.3.2".freeze
2
+ # pre-release versions should be given here as "5.0.0-alpha"
3
+ # for compatibility with semver when it is parsed by the rust agent.
4
+ # This string will be transformed in the gemspec to "5.0.0.alpha"
5
+ # to conform with rubygems.
6
+ VERSION = "5.0.0-beta".freeze
3
7
  end
@@ -0,0 +1,68 @@
1
+ module Skylight
2
+ # @api private
3
+ module VM
4
+ if defined?(JRUBY_VERSION)
5
+
6
+ # This doesn't quite work as we would like it. I believe that the GC
7
+ # statistics includes time that is not stop-the-world, this does not
8
+ # necessarily take time away from the application.
9
+ #
10
+ # require 'java'
11
+ # class GC
12
+ # def initialize
13
+ # @factory = Java::JavaLangManagement::ManagementFactory
14
+ # end
15
+ #
16
+ # def enable
17
+ # end
18
+ #
19
+ # def total_time
20
+ # res = 0.0
21
+ #
22
+ # @factory.garbage_collector_mx_beans.each do |mx|
23
+ # res += (mx.collection_time.to_f / 1_000.0)
24
+ # end
25
+ #
26
+ # res
27
+ # end
28
+ # end
29
+
30
+ elsif defined?(::GC::Profiler)
31
+
32
+ class GC
33
+ def initialize
34
+ @total = 0
35
+ end
36
+
37
+ def enable
38
+ ::GC::Profiler.enable
39
+ end
40
+
41
+ def total_time
42
+ # Reported in seconds
43
+ run = (::GC::Profiler.total_time * 1_000_000).to_i
44
+
45
+ if run > 0
46
+ ::GC::Profiler.clear
47
+ end
48
+
49
+ @total += run
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ # Fallback
56
+ unless defined?(VM::GC)
57
+
58
+ class GC
59
+ def enable; end
60
+
61
+ def total_time
62
+ 0
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+ end