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
@@ -135,14 +135,24 @@ module Skylight
135
135
  title = (opts[:title] || title).to_s
136
136
  desc = opts[:description].to_s if opts[:description]
137
137
 
138
+ # NOTE: The source location logic happens before we have have a config so we can'
139
+ # check if source locations are enabled. However, it only happens once so the potential impact
140
+ # should be minimal. This would more appropriately belong to Extensions::SourceLocation,
141
+ # but as that is a runtime concern, and this happens at compile time, there isn't currently
142
+ # a clean way to turn this on and off. The absence of the extension will cause the
143
+ # source_file and source_line to be removed from the trace span before it is submitted.
144
+ source_file, source_line = klass.instance_method(name).source_location
145
+
138
146
  klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
139
147
  alias_method :"before_instrument_#{name}", :"#{name}"
140
148
 
141
149
  def #{name}(*args, &blk)
142
150
  span = Skylight.instrument(
143
- category: :"#{category}",
151
+ category: :"#{category}",
144
152
  title: #{title.inspect},
145
- description: #{desc.inspect})
153
+ description: #{desc.inspect},
154
+ source_file: #{source_file.inspect},
155
+ source_line: #{source_line.inspect})
146
156
 
147
157
  meta = {}
148
158
  begin
@@ -154,6 +164,12 @@ module Skylight
154
164
  Skylight.done(span, meta) if span
155
165
  end
156
166
  end
167
+
168
+ if protected_method_defined?(:"before_instrument_#{name}")
169
+ protected :"#{name}"
170
+ elsif private_method_defined?(:"before_instrument_#{name}")
171
+ private :"#{name}"
172
+ end
157
173
  RUBY
158
174
  end
159
175
 
@@ -1,10 +1,111 @@
1
+ require "strscan"
2
+ require "securerandom"
3
+ require "skylight/util/logging"
4
+ require "skylight/extensions"
5
+
1
6
  module Skylight
2
- class Instrumenter < Core::Instrumenter
3
- def self.trace_class
4
- Trace
7
+ # @api private
8
+ class Instrumenter
9
+ KEY = :__skylight_current_trace
10
+
11
+ include Util::Logging
12
+
13
+ class TraceInfo
14
+ def initialize(key = KEY)
15
+ @key = key
16
+ @muted_key = "#{key}_muted"
17
+ end
18
+
19
+ def current
20
+ Thread.current[@key]
21
+ end
22
+
23
+ def current=(trace)
24
+ Thread.current[@key] = trace
25
+ end
26
+
27
+ # NOTE: This should only be set by the instrumenter, and only
28
+ # in the context of a `mute` block. Do not try to turn this
29
+ # flag on and off directly.
30
+ def muted=(val)
31
+ Thread.current[@muted_key] = val
32
+ end
33
+
34
+ def muted?
35
+ !!Thread.current[@muted_key]
36
+ end
37
+ end
38
+
39
+ attr_reader :uuid, :config, :gc, :extensions
40
+
41
+ def self.native_new(_uuid, _config_env)
42
+ raise "not implemented"
43
+ end
44
+
45
+ def self.new(config)
46
+ config.validate!
47
+
48
+ uuid = SecureRandom.uuid
49
+ inst = native_new(uuid, config.to_native_env)
50
+ inst.send(:initialize, uuid, config)
51
+ inst
52
+ end
53
+
54
+ def initialize(uuid, config)
55
+ @uuid = uuid
56
+ @gc = config.gc
57
+ @config = config
58
+ @subscriber = Skylight::Subscriber.new(config, self)
59
+
60
+ @trace_info = @config[:trace_info] || TraceInfo.new(KEY)
61
+ @mutex = Mutex.new
62
+ @extensions = Skylight::Extensions::Collection.new(@config)
63
+
64
+ enable_extension!(:source_location) if @config.enable_source_locations?
65
+ end
66
+
67
+ def enable_extension!(name)
68
+ @mutex.synchronize { extensions.enable!(name) }
69
+ end
70
+
71
+ def disable_extension!(name)
72
+ @mutex.synchronize { extensions.disable!(name) }
73
+ end
74
+
75
+ def extension_enabled?(name)
76
+ extensions.enabled?(name)
77
+ end
78
+
79
+ def log_context
80
+ @log_context ||= { inst: uuid }
81
+ end
82
+
83
+ def native_start
84
+ raise "not implemented"
5
85
  end
6
86
 
7
- def check_install!
87
+ def native_stop
88
+ raise "not implemented"
89
+ end
90
+
91
+ def native_track_desc(_endpoint, _description)
92
+ raise "not implemented"
93
+ end
94
+
95
+ def native_submit_trace(_trace)
96
+ raise "not implemented"
97
+ end
98
+
99
+ def current_trace
100
+ @trace_info.current
101
+ end
102
+
103
+ def current_trace=(trace)
104
+ t { "setting current_trace=#{trace ? trace.uuid : 'nil'}; thread=#{Thread.current.object_id}" }
105
+ @trace_info.current = trace
106
+ end
107
+
108
+ def validate_installation
8
109
  # Warn if there was an error installing Skylight.
9
110
 
10
111
  if defined?(Skylight.check_install_errors)
@@ -13,24 +114,233 @@ module Skylight
13
114
 
14
115
  if !Skylight.native? && defined?(Skylight.warn_skylight_native_missing)
15
116
  Skylight.warn_skylight_native_missing(config)
16
- return
117
+ return false
118
+ end
119
+
120
+ true
121
+ end
122
+
123
+ def muted=(val)
124
+ @trace_info.muted = val
125
+ end
126
+
127
+ def muted?
128
+ @trace_info.muted?
129
+ end
130
+
131
+ def mute
132
+ old_muted = muted?
133
+ self.muted = true
134
+ yield if block_given?
135
+ ensure
136
+ self.muted = old_muted
137
+ end
138
+
139
+ def unmute
140
+ old_muted = muted?
141
+ self.muted = false
142
+ yield if block_given?
143
+ ensure
144
+ self.muted = old_muted
145
+ end
146
+
147
+ def silence_warnings(context)
148
+ @warnings_silenced || @mutex.synchronize do
149
+ @warnings_silenced ||= {}
17
150
  end
151
+
152
+ @warnings_silenced[context] = true
18
153
  end
19
154
 
20
- def process_sql(sql)
21
- Skylight.lex_sql(sql)
22
- rescue SqlLexError => e
23
- if config[:log_sql_parse_errors]
24
- config.logger.error "[#{e.formatted_code}] Failed to extract binds from SQL query. " \
25
- "It's likely that this query uses more advanced syntax than we currently support. " \
26
- "sql=#{sql.inspect}"
155
+ def warnings_silenced?(context)
156
+ @warnings_silenced && @warnings_silenced[context]
157
+ end
158
+
159
+ alias disable mute
160
+ alias disabled? muted?
161
+
162
+ def start!
163
+ # We do this here since we can't report these issues via Gem install without stopping install entirely.
164
+ return unless validate_installation
165
+
166
+ t { "starting instrumenter" }
167
+
168
+ unless config.validate_with_server
169
+ log_error "invalid config"
170
+ return
27
171
  end
172
+
173
+ t { "starting native instrumenter" }
174
+ unless native_start
175
+ warn "failed to start instrumenter"
176
+ return
177
+ end
178
+
179
+ config.gc.enable
180
+ @subscriber.register!
181
+
182
+ ActiveSupport::Notifications.instrument("started_instrumenter.skylight", instrumenter: self)
183
+
184
+ self
185
+ rescue Exception => e
186
+ log_error "failed to start instrumenter; msg=%s; config=%s", e.message, config.inspect
187
+ t { e.backtrace.join("\n") }
28
188
  nil
29
189
  end
30
190
 
31
- def handle_instrumenter_error(trace, e)
32
- poison! if e.is_a?(Skylight::InstrumenterUnrecoverableError)
33
- super
191
+ def shutdown
192
+ @subscriber.unregister!
193
+ native_stop
194
+ end
195
+
196
+ def trace(endpoint, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil)
197
+ # If a trace is already in progress, continue with that one
198
+ if (trace = @trace_info.current)
199
+ return yield(trace) if block_given?
200
+
201
+ return trace
202
+ end
203
+
204
+ begin
205
+ trace = Trace.new(self, endpoint, Skylight::Util::Clock.nanos, cat, title, desc,
206
+ meta: meta, segment: segment, component: component)
207
+ rescue Exception => e
208
+ log_error e.message
209
+ t { e.backtrace.join("\n") }
210
+ return
211
+ end
212
+
213
+ @trace_info.current = trace
214
+ return trace unless block_given?
215
+
216
+ begin
217
+ yield trace
218
+ ensure
219
+ @trace_info.current = nil
220
+ t { "instrumenter submitting trace; trace=#{trace.uuid}" }
221
+ trace.submit
222
+ end
223
+ end
224
+
225
+ def instrument(cat, title = nil, desc = nil, meta = nil)
226
+ raise ArgumentError, "cat is required" unless cat
227
+
228
+ if muted?
229
+ return yield if block_given?
230
+
231
+ return
232
+ end
233
+
234
+ unless (trace = @trace_info.current)
235
+ return yield if block_given?
236
+
237
+ return
238
+ end
239
+
240
+ cat = cat.to_s
241
+
242
+ unless Skylight::CATEGORY_REGEX.match?(cat)
243
+ warn "invalid skylight instrumentation category; trace=%s; value=%s", trace.uuid, cat
244
+ return yield if block_given?
245
+
246
+ return
247
+ end
248
+
249
+ cat = "other.#{cat}" unless Skylight::TIER_REGEX.match?(cat)
250
+
251
+ unless (sp = trace.instrument(cat, title, desc, meta))
252
+ return yield if block_given?
253
+
254
+ return
255
+ end
256
+
257
+ return sp unless block_given?
258
+
259
+ begin
260
+ yield sp
261
+ rescue Exception => e
262
+ meta ||= {}
263
+ meta[:exception] = [e.class.name, e.message]
264
+ meta[:exception_object] = e
265
+ raise e
266
+ ensure
267
+ trace.done(sp, meta)
268
+ end
269
+ end
270
+
271
+ def broken!
272
+ return unless (trace = @trace_info.current)
273
+
274
+ trace.broken!
275
+ end
276
+
277
+ def poison!
278
+ @poisoned = true
279
+ end
280
+
281
+ def poisoned?
282
+ @poisoned
283
+ end
284
+
285
+ def done(span, meta = nil)
286
+ return unless (trace = @trace_info.current)
287
+
288
+ trace.done(span, meta)
289
+ end
290
+
291
+ def process(trace)
292
+ t { fmt "processing trace=#{trace.uuid}" }
293
+
294
+ if ignore?(trace)
295
+ t { fmt "ignoring trace=#{trace.uuid}" }
296
+ return false
297
+ end
298
+
299
+ begin
300
+ finalize_endpoint_segment(trace)
301
+ native_submit_trace(trace)
302
+ true
303
+ rescue => e
304
+ handle_instrumenter_error(trace, e)
305
+ end
306
+ end
307
+
308
+ def handle_instrumenter_error(trace, err)
309
+ poison! if err.is_a?(Skylight::InstrumenterUnrecoverableError)
310
+
311
+ warn "failed to submit trace to worker; trace=%s, err=%s", trace.uuid, err
312
+ t { "BACKTRACE:\n#{err.backtrace.join("\n")}" }
313
+
314
+ false
315
+ end
316
+
317
+ def ignore?(trace)
318
+ config.ignored_endpoints.include?(trace.endpoint)
319
+ end
320
+
321
+ # Because GraphQL can return multiple results, each of which
322
+ # may have their own success/error states, we need to set the
323
+ # skylight segment as follows:
324
+ #
325
+ # - when all queries have errors: "error"
326
+ # - when some queries have errors: "<rendered format>+error"
327
+ # - when no queries have errors: "<rendered format>"
328
+ #
329
+ # <rendered format> will be determined by the Rails controller as usual.
330
+ # See Instrumenter#finalize_endpoint_segment for the actual segment/error assignment.
331
+ def finalize_endpoint_segment(trace)
332
+ return unless (segment = trace.segment)
333
+
334
+ segment = case trace.compound_response_error_status
335
+ when :all
336
+ "error"
337
+ when :partial
338
+ "#{segment}+error"
339
+ else
340
+ segment
341
+ end
342
+
343
+ trace.endpoint += "<sk-segment>#{segment}</sk-segment>"
34
344
  end
35
345
  end
36
346
  end
@@ -1,4 +1,141 @@
1
+ require "securerandom"
2
+
1
3
  module Skylight
2
- class Middleware < Core::Middleware
4
+ # @api private
5
+ class Middleware
6
+ class BodyProxy
7
+ def initialize(body, &block)
8
+ @body = body
9
+ @block = block
10
+ @closed = false
11
+ end
12
+
13
+ def respond_to_missing?(*args)
14
+ return false if args.first.to_s !~ /^to_ary$/
15
+
16
+ @body.respond_to?(*args)
17
+ end
18
+
19
+ def close
20
+ return if @closed
21
+
22
+ @closed = true
23
+ begin
24
+ @body.close if @body.respond_to? :close
25
+ ensure
26
+ @block.call
27
+ end
28
+ end
29
+
30
+ def closed?
31
+ @closed
32
+ end
33
+
34
+ # N.B. This method is a special case to address the bug described by
35
+ # https://github.com/rack/rack/issues/434.
36
+ # We are applying this special case for #each only. Future bugs of this
37
+ # class will be handled by requesting users to patch their ruby
38
+ # implementation, to save adding too many methods in this class.
39
+ def each(*args, &block)
40
+ @body.each(*args, &block)
41
+ end
42
+
43
+ def method_missing(*args, &block)
44
+ super if args.first.to_s =~ /^to_ary$/
45
+ @body.__send__(*args, &block)
46
+ end
47
+ end
48
+
49
+ def self.with_after_close(resp, debug_identifier: "unknown", &block)
50
+ unless resp.respond_to?(:to_ary)
51
+ if resp.respond_to?(:to_a)
52
+ Skylight.warn("Rack response from \"#{debug_identifier}\" cannot be implicitly converted to an array. " \
53
+ "This is in violation of the Rack SPEC and will raise an error in future versions.")
54
+ resp = resp.to_a
55
+ else
56
+ Skylight.error("Rack response from \"#{debug_identifier}\" cannot be converted to an array. This is in " \
57
+ "violation of the Rack SPEC and may cause problems with Skylight operation.")
58
+ return resp
59
+ end
60
+ end
61
+
62
+ status, headers, body = resp
63
+ [status, headers, BodyProxy.new(body, &block)]
64
+ end
65
+
66
+ include Skylight::Util::Logging
67
+
68
+ # For Util::Logging
69
+ attr_reader :config
70
+
71
+ def initialize(app, opts = {})
72
+ @app = app
73
+ @config = opts[:config]
74
+ end
75
+
76
+ def call(env)
77
+ set_request_id(env)
78
+
79
+ if Skylight.tracing?
80
+ error "Already instrumenting. Make sure the Skylight Rack Middleware hasn't been added more than once."
81
+ end
82
+
83
+ if env["REQUEST_METHOD"] == "HEAD"
84
+ t { "middleware skipping HEAD" }
85
+ @app.call(env)
86
+ else
87
+ begin
88
+ t { "middleware beginning trace" }
89
+ trace = Skylight.trace(endpoint_name(env), "app.rack.request", nil, meta: endpoint_meta(env), component: :web)
90
+ t { "middleware began trace=#{trace ? trace.uuid : nil}" }
91
+
92
+ resp = @app.call(env)
93
+
94
+ if trace
95
+ Middleware.with_after_close(resp, debug_identifier: "Rack App: #{@app.class}") { trace.submit }
96
+ else
97
+ resp
98
+ end
99
+ rescue Exception => e
100
+ t { "middleware exception: #{e}\n#{e.backtrace.join("\n")}" }
101
+ trace&.submit
102
+ raise
103
+ end
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ def log_context
110
+ # Don't cache this, it will change
111
+ { request_id: @current_request_id, inst: Skylight.instrumenter&.uuid }
112
+ end
113
+
114
+ # Allow for overwriting
115
+ def endpoint_name(_env)
116
+ "Rack"
117
+ end
118
+
119
+ def endpoint_meta(_env)
120
+ nil
121
+ end
122
+
123
+ # Request ID code based on ActionDispatch::RequestId
124
+ def set_request_id(env)
125
+ existing_request_id = env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"]
126
+ @current_request_id = env["skylight.request_id"] = make_request_id(existing_request_id)
127
+ end
128
+
129
+ def make_request_id(request_id)
130
+ if request_id && !request_id.empty?
131
+ request_id.gsub(/[^\w\-]/, "".freeze)[0...255]
132
+ else
133
+ internal_request_id
134
+ end
135
+ end
136
+
137
+ def internal_request_id
138
+ SecureRandom.uuid
139
+ end
3
140
  end
4
141
  end