skylight 3.1.4 → 5.3.4

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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +465 -294
  3. data/CLA.md +1 -1
  4. data/CONTRIBUTING.md +11 -3
  5. data/ERRORS.md +3 -0
  6. data/LICENSE.md +8 -18
  7. data/README.md +1 -2
  8. data/bin/skylight +1 -1
  9. data/ext/extconf.rb +118 -122
  10. data/ext/libskylight.yml +8 -6
  11. data/ext/skylight_native.c +56 -100
  12. data/lib/skylight/api.rb +41 -27
  13. data/lib/skylight/cli/doctor.rb +68 -70
  14. data/lib/skylight/cli/helpers.rb +3 -5
  15. data/lib/skylight/cli/merger.rb +99 -92
  16. data/lib/skylight/cli.rb +40 -43
  17. data/lib/skylight/config.rb +656 -201
  18. data/lib/skylight/data/cacert.pem +730 -1023
  19. data/lib/skylight/deprecation.rb +17 -0
  20. data/lib/skylight/errors.rb +34 -16
  21. data/lib/skylight/extensions/source_location.rb +291 -0
  22. data/lib/skylight/extensions.rb +95 -0
  23. data/lib/skylight/formatters/http.rb +18 -0
  24. data/lib/skylight/gc.rb +99 -0
  25. data/lib/skylight/helpers.rb +82 -39
  26. data/lib/skylight/instrumenter.rb +339 -9
  27. data/lib/skylight/middleware.rb +147 -1
  28. data/lib/skylight/native.rb +71 -23
  29. data/lib/skylight/native_ext_fetcher.rb +39 -47
  30. data/lib/skylight/normalizers/action_controller/process_action.rb +68 -0
  31. data/lib/skylight/normalizers/action_controller/send_file.rb +51 -0
  32. data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
  33. data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
  34. data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
  35. data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
  36. data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
  37. data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
  38. data/lib/skylight/normalizers/active_job/perform.rb +87 -0
  39. data/lib/skylight/normalizers/active_model_serializers/render.rb +32 -0
  40. data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
  41. data/lib/skylight/normalizers/active_record/sql.rb +20 -0
  42. data/lib/skylight/normalizers/active_storage.rb +28 -0
  43. data/lib/skylight/normalizers/active_support/cache.rb +11 -0
  44. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  45. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  46. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  47. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  48. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  49. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  50. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  51. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  52. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  53. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  54. data/lib/skylight/normalizers/coach/handler_finish.rb +44 -0
  55. data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
  56. data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
  57. data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
  58. data/lib/skylight/normalizers/default.rb +24 -0
  59. data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
  60. data/lib/skylight/normalizers/faraday/request.rb +38 -0
  61. data/lib/skylight/normalizers/grape/endpoint.rb +28 -0
  62. data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
  63. data/lib/skylight/normalizers/grape/endpoint_run.rb +39 -0
  64. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +20 -0
  65. data/lib/skylight/normalizers/grape/format_response.rb +20 -0
  66. data/lib/skylight/normalizers/graphiti/render.rb +22 -0
  67. data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
  68. data/lib/skylight/normalizers/graphql/base.rb +127 -0
  69. data/lib/skylight/normalizers/render.rb +79 -0
  70. data/lib/skylight/normalizers/sequel/sql.rb +12 -0
  71. data/lib/skylight/normalizers/shrine.rb +32 -0
  72. data/lib/skylight/normalizers/sql.rb +41 -0
  73. data/lib/skylight/normalizers.rb +157 -0
  74. data/lib/skylight/probes/action_controller.rb +52 -0
  75. data/lib/skylight/probes/action_dispatch/request_id.rb +33 -0
  76. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +30 -0
  77. data/lib/skylight/probes/action_dispatch.rb +2 -0
  78. data/lib/skylight/probes/action_view.rb +42 -0
  79. data/lib/skylight/probes/active_job.rb +27 -0
  80. data/lib/skylight/probes/active_job_enqueue.rb +35 -0
  81. data/lib/skylight/probes/active_model_serializers.rb +50 -0
  82. data/lib/skylight/probes/active_record_async.rb +96 -0
  83. data/lib/skylight/probes/delayed_job.rb +144 -0
  84. data/lib/skylight/probes/elasticsearch.rb +45 -0
  85. data/lib/skylight/probes/excon/middleware.rb +65 -0
  86. data/lib/skylight/probes/excon.rb +25 -0
  87. data/lib/skylight/probes/faraday.rb +23 -0
  88. data/lib/skylight/probes/graphql.rb +38 -0
  89. data/lib/skylight/probes/httpclient.rb +44 -0
  90. data/lib/skylight/probes/middleware.rb +135 -0
  91. data/lib/skylight/probes/mongo.rb +169 -0
  92. data/lib/skylight/probes/mongoid.rb +6 -0
  93. data/lib/skylight/probes/net_http.rb +54 -0
  94. data/lib/skylight/probes/rack_builder.rb +37 -0
  95. data/lib/skylight/probes/redis.rb +68 -0
  96. data/lib/skylight/probes/sequel.rb +29 -0
  97. data/lib/skylight/probes/sinatra.rb +66 -0
  98. data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
  99. data/lib/skylight/probes/tilt.rb +25 -0
  100. data/lib/skylight/probes.rb +172 -0
  101. data/lib/skylight/railtie.rb +172 -15
  102. data/lib/skylight/sidekiq.rb +47 -0
  103. data/lib/skylight/sinatra.rb +2 -2
  104. data/lib/skylight/subscriber.rb +130 -0
  105. data/lib/skylight/test.rb +147 -0
  106. data/lib/skylight/trace.rb +331 -15
  107. data/lib/skylight/user_config.rb +60 -0
  108. data/lib/skylight/util/allocation_free.rb +26 -0
  109. data/lib/skylight/util/clock.rb +57 -0
  110. data/lib/skylight/util/component.rb +47 -9
  111. data/lib/skylight/util/deploy.rb +24 -40
  112. data/lib/skylight/util/gzip.rb +20 -0
  113. data/lib/skylight/util/hostname.rb +4 -4
  114. data/lib/skylight/util/http.rb +62 -71
  115. data/lib/skylight/util/instrumenter_method.rb +26 -0
  116. data/lib/skylight/util/logging.rb +136 -0
  117. data/lib/skylight/util/lru_cache.rb +36 -0
  118. data/lib/skylight/util/platform.rb +74 -0
  119. data/lib/skylight/util/proxy.rb +13 -0
  120. data/lib/skylight/util/ssl.rb +4 -28
  121. data/lib/skylight/util.rb +12 -0
  122. data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
  123. data/lib/skylight/version.rb +5 -1
  124. data/lib/skylight/vm/gc.rb +60 -0
  125. data/lib/skylight.rb +213 -24
  126. metadata +171 -53
@@ -1,33 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
1
5
  module Skylight
2
6
  module Util
3
7
  class Component
4
-
5
8
  attr_accessor :environment, :name
6
9
 
7
- NAME_FORMAT = /\A[a-z0-9-]+\z/
8
- DEFAULT_NAME = 'web'.freeze
9
- DEFAULT_ENVIRONMENT = 'production'.freeze
10
+ NAME_FORMAT = /\A[a-zA-Z0-9_-]+\z/.freeze
11
+ DEFAULT_NAME = "web"
12
+ WORKER_NAME = "worker"
13
+ DEFAULT_ENVIRONMENT = "production"
10
14
 
11
- def initialize(environment, name)
15
+ def initialize(environment, name, force_worker: false)
12
16
  @environment = environment || DEFAULT_ENVIRONMENT
13
- @name = name || DEFAULT_NAME
17
+ @name = resolve_name(name, force_worker)
14
18
 
15
19
  raise ArgumentError, "environment can't be blank" if @environment.empty?
16
- validate_string!(@environment, 'environment')
17
- validate_string!(@name, 'name')
20
+
21
+ validate_string!(@environment, "environment")
22
+ validate_string!(@name, "name")
18
23
  end
19
24
 
20
25
  def to_s
21
26
  "#{name}:#{environment}"
22
27
  end
23
28
 
29
+ def to_encoded_s
30
+ @to_encoded_s ||= URI.encode_www_form_component(to_s)
31
+ end
32
+
33
+ def web?
34
+ name == DEFAULT_NAME
35
+ end
36
+
37
+ def worker?
38
+ !web?
39
+ end
40
+
41
+ # keys here should match those from the main config
42
+ def as_json(*)
43
+ { component: name, env: environment }
44
+ end
45
+
24
46
  private
25
47
 
48
+ def program_name
49
+ $PROGRAM_NAME
50
+ end
51
+
52
+ def argv
53
+ ARGV
54
+ end
55
+
56
+ def resolve_name(given_name, force_worker)
57
+ # don't allow workers to be called 'web'
58
+ return WORKER_NAME if force_worker && (given_name.nil? || given_name == DEFAULT_NAME)
59
+ return DEFAULT_NAME if given_name.nil?
60
+
61
+ given_name
62
+ end
63
+
26
64
  def validate_string!(string, kind)
27
65
  return true if string =~ NAME_FORMAT
66
+
28
67
  raise ArgumentError, "#{kind} can only contain lowercase letters, numbers, and dashes"
29
68
  end
30
69
  end
31
70
  end
32
71
  end
33
-
@@ -1,11 +1,9 @@
1
- require 'json'
2
- require 'skylight/core/util/logging'
1
+ require "json"
2
+ require "skylight/util/logging"
3
3
 
4
4
  module Skylight
5
5
  module Util
6
-
7
6
  module Deploy
8
-
9
7
  def self.build(config)
10
8
  DEPLOY_TYPES.each do |type|
11
9
  deploy = type.new(config)
@@ -15,9 +13,7 @@ module Skylight
15
13
  end
16
14
 
17
15
  class EmptyDeploy
18
-
19
- attr_reader :config
20
- attr_reader :timestamp
16
+ attr_reader :config, :timestamp
21
17
 
22
18
  def initialize(config)
23
19
  @config = config
@@ -38,75 +34,66 @@ module Skylight
38
34
 
39
35
  def to_query_hash
40
36
  hash = {
41
- timestamp: timestamp,
42
- deploy_id: id.to_s[0..100] # Keep this sane
37
+ timestamp: timestamp,
38
+ deploy_id: id.to_s[0..100] # Keep this sane
43
39
  }
44
40
  hash[:git_sha] = git_sha.to_s[0..40] if git_sha # A valid SHA will never exceed 40
45
41
  hash[:description] = description[0..255] if description # Avoid massive descriptions
46
42
  hash
47
43
  end
48
-
49
44
  end
50
45
 
51
46
  class DefaultDeploy < EmptyDeploy
52
- include Core::Util::Logging
47
+ include Util::Logging
53
48
 
54
49
  def initialize(*)
55
50
  super
56
- if description && !id
57
- warn "The configured deploy will be ignored as an id or git_sha must be provided."
58
- end
51
+ warn "The configured deploy will be ignored as an id or git_sha must be provided." if description && !id
59
52
  end
60
53
 
61
54
  def id
62
- config.get(:'deploy.id') || git_sha
55
+ config.get(:"deploy.id") || git_sha
63
56
  end
64
57
 
65
58
  def git_sha
66
- config.get(:'deploy.git_sha')
59
+ config.get(:"deploy.git_sha")
67
60
  end
68
61
 
69
62
  def description
70
- config.get(:'deploy.description')
63
+ config.get(:"deploy.description")
71
64
  end
72
-
73
65
  end
74
66
 
75
67
  class HerokuDeploy < EmptyDeploy
76
-
77
68
  def initialize(*)
78
69
  super
79
70
  @info = get_info
80
71
  end
81
72
 
82
73
  def id
83
- @info ? @info['id'] : nil
74
+ @info ? @info["id"] : nil
84
75
  end
85
76
 
86
77
  def git_sha
87
- @info ? @info['commit'] : nil
78
+ @info ? @info["commit"] : nil
88
79
  end
89
80
 
90
81
  def description
91
- @info ? @info['description'] : nil
82
+ @info ? @info["description"] : nil
92
83
  end
93
84
 
94
85
  private
95
86
 
96
- def get_info
97
- info_path = config[:'heroku.dyno_info_path']
87
+ def get_info
88
+ info_path = config[:"heroku.dyno_info_path"]
98
89
 
99
- if File.exist?(info_path)
100
- if info = JSON.parse(File.read(info_path))
101
- info['release']
102
- end
103
- end
90
+ if File.exist?(info_path) && (info = JSON.parse(File.read(info_path)))
91
+ info["release"]
104
92
  end
105
-
93
+ end
106
94
  end
107
95
 
108
96
  class GitDeploy < EmptyDeploy
109
-
110
97
  attr_reader :git_sha, :description
111
98
 
112
99
  def initialize(*)
@@ -116,18 +103,15 @@ module Skylight
116
103
 
117
104
  private
118
105
 
119
- def get_info
120
- Dir.chdir(config.root) do
121
- info = `git log -1 --pretty="%H %s" 2>&1`
122
- info.split(" ", 2).map(&:strip) if $?.success?
123
- end
106
+ def get_info
107
+ Dir.chdir(config.root) do
108
+ info = `git log -1 --pretty="%H %s" 2>&1`
109
+ info.split(" ", 2).map(&:strip) if $CHILD_STATUS.success?
124
110
  end
125
-
111
+ end
126
112
  end
127
113
 
128
- DEPLOY_TYPES = [DefaultDeploy, HerokuDeploy, GitDeploy]
129
-
114
+ DEPLOY_TYPES = [DefaultDeploy, HerokuDeploy, GitDeploy].freeze
130
115
  end
131
-
132
116
  end
133
117
  end
@@ -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
@@ -1,13 +1,13 @@
1
- require 'socket'
2
- require 'securerandom'
1
+ require "socket"
2
+ require "securerandom"
3
3
 
4
4
  module Skylight
5
5
  module Util
6
6
  module Hostname
7
7
  def self.default_hostname
8
- if hostname = Socket.gethostname
8
+ if (hostname = Socket.gethostname)
9
9
  hostname.strip!
10
- hostname = nil if hostname == ''
10
+ hostname = nil if hostname == ""
11
11
  end
12
12
 
13
13
  hostname || "gen-#{SecureRandom.uuid}"
@@ -1,70 +1,68 @@
1
- require 'uri'
2
- require 'json'
3
- require 'openssl'
4
- require 'net/http'
5
- require 'net/https'
6
- require 'skylight/core/util/gzip'
7
- require 'skylight/util/ssl'
1
+ require "uri"
2
+ require "json"
3
+ require "openssl"
4
+ require "net/http"
5
+ require "net/https"
6
+ require "skylight/util/gzip"
7
+ require "skylight/util/ssl"
8
8
 
9
9
  module Skylight
10
10
  module Util
11
11
  class HTTP
12
- CONTENT_ENCODING = 'content-encoding'.freeze
13
- CONTENT_LENGTH = 'content-length'.freeze
14
- CONTENT_TYPE = 'content-type'.freeze
15
- ACCEPT = 'Accept'.freeze
16
- X_VERSION_HDR = 'x-skylight-agent-version'.freeze
17
- APPLICATION_JSON = 'application/json'.freeze
18
- AUTHORIZATION = 'authorization'.freeze
19
- DEFLATE = 'deflate'.freeze
20
- GZIP = 'gzip'.freeze
21
-
22
- include Core::Util::Logging
12
+ CONTENT_ENCODING = "content-encoding".freeze
13
+ CONTENT_LENGTH = "content-length".freeze
14
+ CONTENT_TYPE = "content-type".freeze
15
+ ACCEPT = "Accept".freeze
16
+ X_VERSION_HDR = "x-skylight-agent-version".freeze
17
+ APPLICATION_JSON = "application/json".freeze
18
+ AUTHORIZATION = "authorization".freeze
19
+ DEFLATE = "deflate".freeze
20
+ GZIP = "gzip".freeze
21
+
22
+ include Logging
23
23
 
24
24
  attr_accessor :authentication
25
25
  attr_reader :host, :port, :config
26
26
 
27
- READ_EXCEPTIONS = [Timeout::Error, EOFError]
28
- # This doesn't exist on Ruby 1.9.3
29
- READ_EXCEPTIONS << Net::ReadTimeout if defined?(Net::ReadTimeout)
30
- READ_EXCEPTIONS.freeze
27
+ READ_EXCEPTIONS = [Timeout::Error, EOFError, Net::ReadTimeout].freeze
31
28
 
32
29
  class StartError < StandardError
33
30
  attr_reader :original
34
31
 
35
- def initialize(e)
36
- @original = e
37
- super e.inspect
32
+ def initialize(error)
33
+ @original = error
34
+ super error.inspect
38
35
  end
39
-
40
36
  end
41
37
 
42
- class ReadResponseError < StandardError; end
38
+ class ReadResponseError < StandardError
39
+ end
43
40
 
44
41
  def initialize(config, service = :auth, opts = {})
45
42
  @config = config
46
43
 
47
- unless url = config["#{service}_url"]
44
+ unless (url = config["#{service}_url"])
48
45
  raise ArgumentError, "no URL specified for #{service}"
49
46
  end
50
47
 
51
48
  url = URI.parse(url)
52
49
 
53
- @ssl = url.scheme == 'https'
50
+ @ssl = url.scheme == "https"
54
51
  @host = url.host
55
52
  @port = url.port
56
53
 
57
- if proxy_url = config[:proxy_url]
54
+ if (proxy_url = config[:proxy_url])
58
55
  proxy_url = URI.parse(proxy_url)
59
- @proxy_addr, @proxy_port = proxy_url.host, proxy_url.port
60
- @proxy_user, @proxy_pass = (proxy_url.userinfo || '').split(/:/)
56
+ @proxy_addr = proxy_url.host
57
+ @proxy_port = proxy_url.port
58
+ @proxy_user, @proxy_pass = (proxy_url.userinfo || "").split(/:/)
61
59
  end
62
60
 
63
61
  @open_timeout = get_timeout(:connect, config, service, opts)
64
62
  @read_timeout = get_timeout(:read, config, service, opts)
65
63
 
66
64
  @deflate = config["#{service}_http_deflate"]
67
- @authentication = config[:'authentication']
65
+ @authentication = config[:authentication]
68
66
  end
69
67
 
70
68
  def get(endpoint, hdrs = {})
@@ -82,25 +80,22 @@ module Skylight
82
80
  execute(request, body)
83
81
  end
84
82
 
85
- private
83
+ private
86
84
 
87
85
  def get_timeout(type, config, service, opts)
88
- config.duration_ms("#{service}_http_#{type}_timeout") ||
89
- opts[:timeout] || 15
86
+ config.duration_ms("#{service}_http_#{type}_timeout") || opts[:timeout] || 15
90
87
  end
91
88
 
92
- def build_request(type, endpoint, hdrs, length=nil)
89
+ def build_request(type, endpoint, hdrs, length = nil)
93
90
  headers = {}
94
91
 
95
- headers[CONTENT_LENGTH] = length.to_s if length
96
- headers[AUTHORIZATION] = authentication if authentication
97
- headers[ACCEPT] = APPLICATION_JSON
98
- headers[X_VERSION_HDR] = VERSION
92
+ headers[CONTENT_LENGTH] = length.to_s if length
93
+ headers[AUTHORIZATION] = authentication if authentication
94
+ headers[ACCEPT] = APPLICATION_JSON
95
+ headers[X_VERSION_HDR] = VERSION
99
96
  headers[CONTENT_ENCODING] = GZIP if length && @deflate
100
97
 
101
- hdrs.each do |k, v|
102
- headers[k] = v
103
- end
98
+ hdrs.each { |k, v| headers[k] = v }
104
99
 
105
100
  type.new(endpoint, headers)
106
101
  end
@@ -108,7 +103,7 @@ module Skylight
108
103
  def do_request(http, req)
109
104
  begin
110
105
  client = http.start
111
- rescue => e
106
+ rescue StandardError => e
112
107
  # TODO: Retry here
113
108
  raise StartError, e
114
109
  end
@@ -121,20 +116,24 @@ module Skylight
121
116
 
122
117
  yield res
123
118
  ensure
124
- client.finish if client
119
+ client&.finish
125
120
  end
126
121
 
127
- def execute(req, body=nil)
128
- t { fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
129
- @host, @port, req.path, body && body.bytesize }
122
+ def execute(req, body = nil)
123
+ t do
124
+ fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
125
+ @host,
126
+ @port,
127
+ req.path,
128
+ body && body.bytesize
129
+ end
130
130
 
131
131
  if body
132
132
  body = Gzip.compress(body) if @deflate
133
133
  req.body = body
134
134
  end
135
135
 
136
- http = Net::HTTP.new(@host, @port,
137
- @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
136
+ http = Net::HTTP.new(@host, @port, @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
138
137
 
139
138
  http.open_timeout = @open_timeout
140
139
  http.read_timeout = @read_timeout
@@ -142,9 +141,7 @@ module Skylight
142
141
  if @ssl
143
142
  http.use_ssl = true
144
143
 
145
- unless SSL.ca_cert_file?
146
- http.ca_file = SSL.ca_cert_file_or_default
147
- end
144
+ http.ca_file = SSL.ca_cert_file_or_default unless SSL.ca_cert_file?
148
145
 
149
146
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
150
147
  end
@@ -167,7 +164,7 @@ module Skylight
167
164
  attr_reader :status, :headers, :body, :exception
168
165
 
169
166
  def initialize(status, headers, body)
170
- @status = status
167
+ @status = status
171
168
  @headers = headers
172
169
 
173
170
  if (headers[CONTENT_TYPE] || "").include?(APPLICATION_JSON)
@@ -190,16 +187,10 @@ module Skylight
190
187
  end
191
188
 
192
189
  def get(key)
193
- return nil unless Hash === body
194
-
195
- res = body
196
- key.split('.').each do |part|
197
- return unless res = res[part]
198
- end
199
- res
190
+ body.dig(*key.split(".")) if body.is_a?(Hash)
200
191
  end
201
192
 
202
- def respond_to_missing?(name, include_all=false)
193
+ def respond_to_missing?(name, include_all = false)
203
194
  super || body.respond_to?(name, include_all)
204
195
  end
205
196
 
@@ -212,16 +203,16 @@ module Skylight
212
203
  end
213
204
  end
214
205
 
215
- class ErrorResponse < Struct.new(:request, :exception)
216
- def status
217
- nil
218
- end
206
+ ErrorResponse =
207
+ Struct.new(:request, :exception) do
208
+ def status
209
+ nil
210
+ end
219
211
 
220
- def success?
221
- false
212
+ def success?
213
+ false
214
+ end
222
215
  end
223
- end
224
-
225
216
  end
226
217
  end
227
218
  end
@@ -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) # def mute(*args)
8
+ unless instrumenter # unless instrumenter
9
+ return yield if block_given? # return yield if block_given?
10
+ return # return
11
+ end # end
12
+ #
13
+ instrumenter.#{name}(*args) { yield } # instrumenter.mute(*args) { yield }
14
+ end # end
15
+ RUBY
16
+ else
17
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
18
+ def #{name}(*args) # def config(*args)
19
+ instrumenter&.#{name}(*args) # instrumenter&.config(*args)
20
+ end # end
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,136 @@
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
+ @logger.<<(*args) if logger_out != $stdout && logger_out != $stderr
18
+ end
19
+
20
+ def close; end
21
+ end
22
+
23
+ module Logging
24
+ def log_context
25
+ {}
26
+ end
27
+
28
+ def trace?
29
+ !!ENV[-"SKYLIGHT_ENABLE_TRACE_LOGS"]
30
+ end
31
+
32
+ def raise_on_error?
33
+ !!ENV[-"SKYLIGHT_RAISE_ON_ERROR"]
34
+ end
35
+
36
+ # Logs if tracing
37
+ #
38
+ # @param (see #debug)
39
+ #
40
+ # See {trace?}.
41
+ def trace(msg, *args)
42
+ return unless trace?
43
+
44
+ log :debug, msg, *args
45
+ end
46
+
47
+ # Evaluates and logs the result of the block if tracing
48
+ #
49
+ # @yield block to be evaluted
50
+ # @yieldreturn arguments for {#debug}
51
+ #
52
+ # See {trace?}.
53
+ def t
54
+ return unless trace?
55
+
56
+ log :debug, yield
57
+ end
58
+
59
+ # @param msg (see #log)
60
+ # @param args (see #log)
61
+ def debug(msg, *args)
62
+ log :debug, msg, *args
63
+ end
64
+
65
+ # @param msg (see #log)
66
+ # @param args (see #log)
67
+ def info(msg, *args)
68
+ log :info, msg, *args
69
+ end
70
+
71
+ # @param msg (see #log)
72
+ # @param args (see #log)
73
+ def warn(msg, *args)
74
+ log :warn, msg, *args
75
+ end
76
+
77
+ # @param msg (see #log)
78
+ # @param args (see #log)
79
+ def error(msg, *args)
80
+ log :error, msg, *args
81
+ raise format(msg, *args) if raise_on_error?
82
+ end
83
+
84
+ alias log_trace trace
85
+ alias log_debug debug
86
+ alias log_info info
87
+ alias log_warn warn
88
+ alias log_error error
89
+
90
+ # Alias for `Kernel#sprintf`
91
+ # @return [String]
92
+ def fmt(*args)
93
+ sprintf(*args)
94
+ end
95
+
96
+ def config_for_logging
97
+ if respond_to?(:config)
98
+ config
99
+ elsif is_a?(Skylight::Config)
100
+ self
101
+ end
102
+ end
103
+
104
+ # @param level [String,Symbol] the method on `logger` to use for logging
105
+ # @param msg [String] the message to log
106
+ # @param args [Array] values for `Kernel#sprintf` on `msg`
107
+ def log(level, msg, *args)
108
+ c = config_for_logging
109
+ logger = c ? c.logger : nil
110
+
111
+ msg = log_context.map { |(k, v)| "#{k}=#{v}; " }.join << msg
112
+
113
+ if logger
114
+ if logger.respond_to?(level)
115
+ if args.empty?
116
+ logger.send level, msg
117
+ else
118
+ logger.send level, format(msg, *args)
119
+ end
120
+ return # rubocop:disable Style/RedundantReturn
121
+ else
122
+ Kernel.warn "Invalid logger"
123
+ end
124
+ # Fallback to stderr for warn and error levels
125
+ elsif %i[warn error].include?(level)
126
+ $stderr.puts format("[SKYLIGHT] #{msg}", *args)
127
+ end
128
+ rescue Exception => e
129
+ if trace?
130
+ puts "[ERROR] #{e.message}"
131
+ puts e.backtrace
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end