skylight 3.1.4 → 5.3.4

Sign up to get free protection for your applications and to get access to all the features.
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