rage-rb 1.17.1 → 1.19.0
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.
- checksums.yaml +4 -4
- data/ARCHITECTURE.md +47 -0
- data/CHANGELOG.md +23 -0
- data/lib/rage/all.rb +1 -0
- data/lib/rage/application.rb +3 -25
- data/lib/rage/cable/cable.rb +2 -3
- data/lib/rage/cli.rb +101 -18
- data/lib/rage/code_loader.rb +8 -0
- data/lib/rage/configuration.rb +525 -195
- data/lib/rage/controller/api.rb +15 -3
- data/lib/rage/deferred/context.rb +48 -0
- data/lib/rage/deferred/deferred.rb +5 -1
- data/lib/rage/deferred/queue.rb +8 -8
- data/lib/rage/deferred/task.rb +29 -23
- data/lib/rage/env.rb +15 -0
- data/lib/rage/events/events.rb +140 -0
- data/lib/rage/events/subscriber.rb +174 -0
- data/lib/rage/fiber.rb +11 -2
- data/lib/rage/fiber_scheduler.rb +2 -2
- data/lib/rage/hooks.rb +1 -0
- data/lib/rage/internal.rb +53 -0
- data/lib/rage/log_processor.rb +117 -0
- data/lib/rage/logger/json_formatter.rb +37 -18
- data/lib/rage/logger/logger.rb +136 -30
- data/lib/rage/logger/text_formatter.rb +21 -2
- data/lib/rage/middleware/fiber_wrapper.rb +8 -0
- data/lib/rage/middleware/reloader.rb +6 -11
- data/lib/rage/request.rb +18 -1
- data/lib/rage/response.rb +1 -1
- data/lib/rage/router/util.rb +8 -0
- data/lib/rage/setup.rb +2 -0
- data/lib/rage/templates/config-environments-production.rb +1 -0
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +51 -0
- data/rage.gemspec +1 -0
- metadata +22 -4
- data/OVERVIEW.md +0 -83
- data/lib/rage/deferred/metadata.rb +0 -43
data/lib/rage/configuration.rb
CHANGED
|
@@ -4,262 +4,363 @@ require "yaml"
|
|
|
4
4
|
require "erb"
|
|
5
5
|
|
|
6
6
|
##
|
|
7
|
-
#
|
|
7
|
+
# Configuration class for Rage framework.
|
|
8
8
|
#
|
|
9
|
+
# Use {Rage.configure Rage.configure} to access and modify the configuration.
|
|
10
|
+
#
|
|
11
|
+
# **Example:**
|
|
9
12
|
# ```ruby
|
|
10
13
|
# Rage.configure do
|
|
11
|
-
# config.
|
|
12
|
-
# config.server.
|
|
13
|
-
# end
|
|
14
|
-
# ```
|
|
15
|
-
#
|
|
16
|
-
# # General Configuration
|
|
17
|
-
#
|
|
18
|
-
# • _config.logger_
|
|
19
|
-
#
|
|
20
|
-
# > The logger that will be used for `Rage.logger` and any related `Rage` logging. Custom loggers should implement Ruby's {https://ruby-doc.org/3.2.2/stdlibs/logger/Logger.html#class-Logger-label-Entries Logger} interface.
|
|
21
|
-
#
|
|
22
|
-
# • _config.log_formatter_
|
|
23
|
-
#
|
|
24
|
-
# > The formatter of the Rage logger. Built in options include `Rage::TextFormatter` and `Rage::JSONFormatter`. Defaults to an instance of `Rage::TextFormatter`.
|
|
25
|
-
#
|
|
26
|
-
# • _config.log_level_
|
|
27
|
-
#
|
|
28
|
-
# > Defines the verbosity of the Rage logger. This option defaults to `:debug` for all environments except production, where it defaults to `:info`. The available log levels are: `:debug`, `:info`, `:warn`, `:error`, `:fatal`, and `:unknown`.
|
|
29
|
-
#
|
|
30
|
-
# • _config.secret_key_base_
|
|
31
|
-
#
|
|
32
|
-
# > The `secret_key_base` is used as the input secret to the application's key generator, which is used to encrypt cookies. Rage will fall back to the `SECRET_KEY_BASE` environment variable if this is not set.
|
|
33
|
-
#
|
|
34
|
-
# • _config.fallback_secret_key_base_
|
|
35
|
-
#
|
|
36
|
-
# > Defines one or several old secrets that need to be rotated. Can accept a single key or an array of keys. Rage will fall back to the `FALLBACK_SECRET_KEY_BASE` environment variable if this is not set.
|
|
37
|
-
#
|
|
38
|
-
# • _config.after_initialize_
|
|
39
|
-
#
|
|
40
|
-
# > Schedule a block of code to run after Rage has finished loading the application code. Use this to reference application-level constants during the initialization process.
|
|
41
|
-
# > ```
|
|
42
|
-
# Rage.config.after_initialize do
|
|
43
|
-
# SUPER_USER = User.find_by!(super: true)
|
|
44
|
-
# end
|
|
45
|
-
# > ```
|
|
46
|
-
#
|
|
47
|
-
# # Middleware Configuration
|
|
48
|
-
#
|
|
49
|
-
# • _config.middleware.use_
|
|
50
|
-
#
|
|
51
|
-
# > Adds a middleware to the top of the middleware stack. **This is the recommended way of adding a middleware.**
|
|
52
|
-
# > ```
|
|
53
|
-
# config.middleware.use Rack::Cors do
|
|
54
|
-
# allow do
|
|
55
|
-
# origins "*"
|
|
56
|
-
# resource "*", headers: :any
|
|
57
|
-
# end
|
|
58
|
-
# end
|
|
59
|
-
# > ```
|
|
60
|
-
#
|
|
61
|
-
# • _config.middleware.insert_before_
|
|
62
|
-
#
|
|
63
|
-
# > Adds middleware at a specified position before another middleware. The position can be either an index or another middleware.
|
|
64
|
-
#
|
|
65
|
-
# > **_❗️Heads up:_** By default, Rage always uses the `Rage::FiberWrapper` middleware, which wraps every request in a separate fiber. Make sure to always have this middleware in the top of the stack. Placing other middlewares in front may lead to undefined behavior.
|
|
66
|
-
#
|
|
67
|
-
# > ```
|
|
68
|
-
# config.middleware.insert_before Rack::Head, Magical::Unicorns
|
|
69
|
-
# config.middleware.insert_before 0, Magical::Unicorns
|
|
70
|
-
# > ```
|
|
71
|
-
#
|
|
72
|
-
# • _config.middleware.insert_after_
|
|
73
|
-
#
|
|
74
|
-
# > Adds middleware at a specified position after another middleware. The position can be either an index or another middleware.
|
|
75
|
-
#
|
|
76
|
-
# > ```
|
|
77
|
-
# config.middleware.insert_after Rack::Head, Magical::Unicorns
|
|
78
|
-
# > ```
|
|
79
|
-
#
|
|
80
|
-
# # Server Configuration
|
|
81
|
-
#
|
|
82
|
-
# _• config.server.max_clients_
|
|
83
|
-
#
|
|
84
|
-
# > Limits the number of simultaneous connections the server can accept. Defaults to the maximum number of open files.
|
|
85
|
-
#
|
|
86
|
-
# > **_❗️Heads up:_** Decreasing this number is almost never a good idea. Depending on your application specifics, you are encouraged to use other methods to limit the number of concurrent connections:
|
|
87
|
-
#
|
|
88
|
-
# > 1. If your application is exposed to the public, you may want to use a cloud rate limiter, like {https://developers.cloudflare.com/waf Cloudflare WAF} or {https://docs.fastly.com/en/ngwaf Fastly WAF}.
|
|
89
|
-
# > 2. Otherwise, consider using tools like {https://github.com/rack/rack-attack Rack::Attack} or {https://github.com/mperham/connection_pool connection_pool}.
|
|
90
|
-
#
|
|
91
|
-
# > ```
|
|
92
|
-
# # Limit the amount of connections your application can accept
|
|
93
|
-
# config.middleware.use Rack::Attack
|
|
94
|
-
# Rack::Attack.throttle("req/ip", limit: 300, period: 5.minutes) do |req|
|
|
95
|
-
# req.ip
|
|
96
|
-
# end
|
|
97
|
-
# #
|
|
98
|
-
# # Limit the amount of connections to a specific resource
|
|
99
|
-
# HTTP = ConnectionPool.new(size: 5, timeout: 5) { Net::HTTP }
|
|
100
|
-
# HTTP.with do |conn|
|
|
101
|
-
# conn.get("/my-resource")
|
|
102
|
-
# end
|
|
103
|
-
# > ```
|
|
104
|
-
#
|
|
105
|
-
# • _config.server.port_
|
|
106
|
-
#
|
|
107
|
-
# > Specifies what port the server will listen on.
|
|
108
|
-
#
|
|
109
|
-
# • _config.server.workers_count_
|
|
110
|
-
#
|
|
111
|
-
# > Specifies the number of server processes to run. Defaults to 1 in development and to the number of available CPU cores in other environments.
|
|
112
|
-
#
|
|
113
|
-
# • _config.server.timeout_
|
|
114
|
-
#
|
|
115
|
-
# > Specifies connection timeout.
|
|
116
|
-
#
|
|
117
|
-
# # Static file server
|
|
118
|
-
#
|
|
119
|
-
# • _config.public_file_server.enabled_
|
|
120
|
-
#
|
|
121
|
-
# > Configures whether Rage should serve static files from the public directory. Defaults to `false`.
|
|
122
|
-
#
|
|
123
|
-
# # Cable Configuration
|
|
124
|
-
#
|
|
125
|
-
# • _config.cable.protocol_
|
|
126
|
-
#
|
|
127
|
-
# > Specifies the protocol the server will use. Supported values include {Rage::Cable::Protocols::ActioncableV1Json :actioncable_v1_json} and {Rage::Cable::Protocols::RawWebSocketJson :raw_websocket_json}. Defaults to {Rage::Cable::Protocols::ActioncableV1Json :actioncable_v1_json}.
|
|
128
|
-
#
|
|
129
|
-
# • _config.cable.allowed_request_origins_
|
|
130
|
-
#
|
|
131
|
-
# > Restricts the server to only accept requests from specified origins. The origins can be instances of strings or regular expressions, against which a check for the match will be performed.
|
|
132
|
-
#
|
|
133
|
-
# • _config.cable.disable_request_forgery_protection_
|
|
134
|
-
#
|
|
135
|
-
# > Allows requests from any origin.
|
|
136
|
-
#
|
|
137
|
-
# # OpenAPI Configuration
|
|
138
|
-
# • _config.openapi.tag_resolver_
|
|
139
|
-
#
|
|
140
|
-
# > Specifies the proc to build tags for API operations. The proc accepts the controller class, the symbol name of the action, and the default tag built by Rage.
|
|
141
|
-
#
|
|
142
|
-
# > ```ruby
|
|
143
|
-
# config.openapi.tag_resolver = proc do |controller, action, default_tag|
|
|
144
|
-
# # ...
|
|
14
|
+
# config.log_level = :warn
|
|
15
|
+
# config.server.port = 8080
|
|
145
16
|
# end
|
|
146
|
-
# > ```
|
|
147
|
-
#
|
|
148
|
-
# # Deferred Configuration
|
|
149
|
-
# • _config.deferred.backend_
|
|
150
|
-
#
|
|
151
|
-
# > Specifies the backend for deferred tasks. Supported values are `:disk`, which uses disk storage, or `nil`, which disables persistence of deferred tasks.
|
|
152
|
-
# > The `:disk` backend accepts the following options:
|
|
153
|
-
# >
|
|
154
|
-
# > - `:path` - the path to the directory where deferred tasks will be stored. Defaults to `storage`.
|
|
155
|
-
# > - `:prefix` - the prefix for the deferred task files. Defaults to `deferred-`.
|
|
156
|
-
# > - `:fsync_frequency` - the frequency of `fsync` calls in seconds. Defaults to `0.5`.
|
|
157
|
-
#
|
|
158
|
-
# > ```ruby
|
|
159
|
-
# config.deferred.backend = :disk, { path: "storage" }
|
|
160
|
-
# > ```
|
|
161
|
-
#
|
|
162
|
-
# • _config.deferred.backpressure_
|
|
163
|
-
#
|
|
164
|
-
# > Enables the backpressure for deferred tasks. The backpressure is used to limit the number of pending tasks in the queue. It accepts a hash with the following options:
|
|
165
|
-
# >
|
|
166
|
-
# > - `:high_water_mark` - the maximum number of pending tasks in the queue. Defaults to `1000`.
|
|
167
|
-
# > - `:low_water_mark` - the minimum number of pending tasks in the queue before the backpressure is released. Defaults to `high_water_mark * 0.8`.
|
|
168
|
-
# > - `:timeout` - the timeout for the backpressure in seconds. Defaults to `2`.
|
|
169
|
-
#
|
|
170
|
-
# > ```ruby
|
|
171
|
-
# config.deferred.backpressure = { high_water_mark: 1000, low_water_mark: 800, timeout: 2 }
|
|
172
|
-
# > ```
|
|
173
|
-
#
|
|
174
|
-
# > Additionally, you can set the backpressure value to `true` to use the default values:
|
|
175
|
-
#
|
|
176
|
-
# > ```ruby
|
|
177
|
-
# config.deferred.backpressure = true
|
|
178
17
|
# ```
|
|
179
18
|
#
|
|
180
|
-
#
|
|
19
|
+
# ## Transient Settings
|
|
181
20
|
#
|
|
182
21
|
# The settings described in this section should be configured using **environment variables** and are either temporary or will become the default in the future.
|
|
183
22
|
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
# • _RAGE_DISABLE_AR_POOL_PATCH_
|
|
189
|
-
#
|
|
190
|
-
# > Disables the `ActiveRecord::ConnectionPool` patch and makes Rage use the original ActiveRecord implementation.
|
|
191
|
-
#
|
|
192
|
-
# • _RAGE_DISABLE_AR_WEAK_CONNECTIONS_
|
|
193
|
-
#
|
|
194
|
-
# > Instructs Rage to not reuse Active Record connections between different fibers.
|
|
23
|
+
# - _RAGE_DISABLE_IO_WRITE_ - disables the `io_write` hook to fix the ["zero-length iov"](https://bugs.ruby-lang.org/issues/19640) error on Ruby < 3.3.
|
|
24
|
+
# - _RAGE_DISABLE_AR_POOL_PATCH_ - disables the `ActiveRecord::ConnectionPool` patch and makes Rage use the original ActiveRecord implementation.
|
|
25
|
+
# - _RAGE_DISABLE_AR_WEAK_CONNECTIONS_ - instructs Rage to not reuse Active Record connections between different fibers. Only applies to Active Record < 7.2.
|
|
195
26
|
#
|
|
196
27
|
class Rage::Configuration
|
|
28
|
+
# @private
|
|
197
29
|
include Hooks
|
|
198
30
|
|
|
199
|
-
|
|
200
|
-
attr_reader :log_formatter, :log_level
|
|
201
|
-
attr_writer :secret_key_base, :fallback_secret_key_base
|
|
202
|
-
|
|
31
|
+
# @private
|
|
203
32
|
# used in DSL
|
|
204
33
|
def config = self
|
|
205
34
|
|
|
35
|
+
# @!group General Configuration
|
|
36
|
+
|
|
37
|
+
# Returns the logger used by Rage.
|
|
38
|
+
# @return [Rage::Logger, nil]
|
|
39
|
+
def logger
|
|
40
|
+
@logger
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Set the logger used by Rage.
|
|
44
|
+
# Accepts a logger object that implements the `#debug`, `#info`, `#warn`, `#error`, `#fatal`, and `#unknown` methods, or `nil`. If set to `nil`, logging will be disabled.
|
|
45
|
+
# `Rage.logger` always returns an instance of {Rage::Logger Rage::Logger}, but if you provide a custom object, it will be used internally by `Rage.logger`.
|
|
46
|
+
#
|
|
47
|
+
# @overload logger=(logger)
|
|
48
|
+
# Set a standard logger
|
|
49
|
+
# @param logger [#debug, #info, #warn, #error, #fatal, #unknown]
|
|
50
|
+
# @example
|
|
51
|
+
# config.logger = Rage::Logger.new(STDOUT)
|
|
52
|
+
# @overload logger=(callable)
|
|
53
|
+
# Set an external logger. This allows you to send Rage's raw structured logging data directly to external observability platforms without serializing it to text first.
|
|
54
|
+
#
|
|
55
|
+
# The external logger receives pre-parsed structured data (severity, tags, context) rather than formatted strings. This differs from `config.log_formatter` in that formatters control how logs are formatted (text vs JSON), while the external logger controls where logs are sent and how they integrate with external platforms.
|
|
56
|
+
# @param callable [ExternalLoggerInterface]
|
|
57
|
+
# @example
|
|
58
|
+
# config.logger = proc do |severity:, tags:, context:, message:, request_info:|
|
|
59
|
+
# # Custom logging logic here
|
|
60
|
+
# end
|
|
61
|
+
# @overload logger=(nil)
|
|
62
|
+
# Disable logging
|
|
63
|
+
# @example
|
|
64
|
+
# config.logger = nil
|
|
65
|
+
def logger=(logger)
|
|
66
|
+
@logger = if logger.nil? || logger.is_a?(Rage::Logger)
|
|
67
|
+
logger
|
|
68
|
+
elsif Rage::Logger::METHODS_MAP.keys.all? { |method| logger.respond_to?(method) }
|
|
69
|
+
Rage::Logger.new(Rage::Logger::External::Static[logger])
|
|
70
|
+
elsif logger.respond_to?(:call)
|
|
71
|
+
Rage::Logger.new(Rage::Logger::External::Dynamic[logger])
|
|
72
|
+
else
|
|
73
|
+
raise ArgumentError, "Invalid logger: must be an instance of `Rage::Logger`, respond to `#call`, or implement all standard Ruby Logger methods (`#debug`, `#info`, `#warn`, `#error`, `#fatal`, `#unknown`)"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns the log formatter used by Rage.
|
|
78
|
+
# @return [#call, nil]
|
|
79
|
+
def log_formatter
|
|
80
|
+
@log_formatter
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Set the log formatter used by Rage.
|
|
84
|
+
# Built in options include {Rage::TextFormatter Rage::TextFormatter} and {Rage::JSONFormatter Rage::JSONFormatter}.
|
|
85
|
+
#
|
|
86
|
+
# @param formatter [#call] a callable object that formats log messages
|
|
87
|
+
# @example
|
|
88
|
+
# config.log_formatter = proc do |severity, datetime, progname, msg|
|
|
89
|
+
# "[#{datetime}] #{severity} -- #{progname}: #{msg}\n"
|
|
90
|
+
# end
|
|
206
91
|
def log_formatter=(formatter)
|
|
207
92
|
raise ArgumentError, "Custom log formatter should respond to `#call`" unless formatter.respond_to?(:call)
|
|
208
93
|
@log_formatter = formatter
|
|
209
94
|
end
|
|
210
95
|
|
|
96
|
+
# Returns the log level used by Rage.
|
|
97
|
+
# @return [Integer, nil]
|
|
98
|
+
def log_level
|
|
99
|
+
@log_level
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Set the log level used by Rage.
|
|
103
|
+
# @param level [:debug, :info, :warn, :error, :fatal, :unknown, Integer] the log level
|
|
104
|
+
# @example
|
|
105
|
+
# config.log_level = :info
|
|
211
106
|
def log_level=(level)
|
|
212
107
|
@log_level = level.is_a?(Symbol) ? Logger.const_get(level.to_s.upcase) : level
|
|
213
108
|
end
|
|
214
109
|
|
|
110
|
+
# The secret key base is used as the input secret to the application's key generator, which is used to encrypt cookies. Rage will fall back to the `SECRET_KEY_BASE` environment variable if this is not set.
|
|
111
|
+
# @param key [String] the secret key base
|
|
112
|
+
def secret_key_base=(key)
|
|
113
|
+
@secret_key_base = key
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Returns the secret key base used for encrypting cookies.
|
|
117
|
+
# @return [String, nil]
|
|
215
118
|
def secret_key_base
|
|
216
119
|
@secret_key_base || ENV["SECRET_KEY_BASE"]
|
|
217
120
|
end
|
|
218
121
|
|
|
122
|
+
# Set one or several old secrets that need to be rotated. Can accept a single key or an array of keys. Rage will fall back to the `FALLBACK_SECRET_KEY_BASE` environment variable if this is not set.
|
|
123
|
+
# @param key [String, Array<String>] the fallback secret key base(s)
|
|
124
|
+
def fallback_secret_key_base=(key)
|
|
125
|
+
@fallback_secret_key_base = key
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Returns the fallback secret key base(s) used for decrypting cookies encrypted with old secrets.
|
|
129
|
+
# @return [Array<String>]
|
|
219
130
|
def fallback_secret_key_base
|
|
220
131
|
Array(@fallback_secret_key_base || ENV["FALLBACK_SECRET_KEY_BASE"])
|
|
221
132
|
end
|
|
222
133
|
|
|
223
|
-
|
|
224
|
-
|
|
134
|
+
# Schedule a block of code to run after Rage has finished loading the application code. Use this to reference application-level constants during the initialization process.
|
|
135
|
+
# @example
|
|
136
|
+
# Rage.config.after_initialize do
|
|
137
|
+
# SUPER_USER = User.find_by!(super: true)
|
|
138
|
+
# end
|
|
139
|
+
def after_initialize(&block)
|
|
140
|
+
push_hook(block, :after_initialize)
|
|
225
141
|
end
|
|
142
|
+
# @!endgroup
|
|
226
143
|
|
|
144
|
+
# @!group Middleware Configuration
|
|
145
|
+
# Allows configuring the middleware stack used by Rage.
|
|
146
|
+
# @return [Rage::Configuration::Middleware]
|
|
227
147
|
def middleware
|
|
228
148
|
@middleware ||= Middleware.new
|
|
229
149
|
end
|
|
150
|
+
# @!endgroup
|
|
230
151
|
|
|
231
|
-
|
|
232
|
-
|
|
152
|
+
# @!group Server Configuration
|
|
153
|
+
# Allows configuring the built-in Rage server.
|
|
154
|
+
# @return [Rage::Configuration::Server]
|
|
155
|
+
def server
|
|
156
|
+
@server ||= Server.new
|
|
233
157
|
end
|
|
158
|
+
# @!endgroup
|
|
234
159
|
|
|
160
|
+
# @!group Static File Server
|
|
161
|
+
# Allows configuring the static file server used by Rage.
|
|
162
|
+
# @return [Rage::Configuration::PublicFileServer]
|
|
235
163
|
def public_file_server
|
|
236
164
|
@public_file_server ||= PublicFileServer.new
|
|
237
165
|
end
|
|
166
|
+
# @!endgroup
|
|
167
|
+
|
|
168
|
+
# @!group Cable Configuration
|
|
169
|
+
# Allows configuring Cable settings.
|
|
170
|
+
# @return [Rage::Configuration::Cable]
|
|
171
|
+
def cable
|
|
172
|
+
@cable ||= Cable.new
|
|
173
|
+
end
|
|
174
|
+
# @!endgroup
|
|
238
175
|
|
|
176
|
+
# @!group OpenAPI Configuration
|
|
177
|
+
# Allows configuring OpenAPI settings.
|
|
178
|
+
# @return [Rage::Configuration::OpenAPI]
|
|
239
179
|
def openapi
|
|
240
180
|
@openapi ||= OpenAPI.new
|
|
241
181
|
end
|
|
182
|
+
# @!endgroup
|
|
242
183
|
|
|
184
|
+
# @!group Deferred Configuration
|
|
185
|
+
# Allows configuring Deferred settings.
|
|
186
|
+
# @return [Rage::Configuration::Deferred]
|
|
243
187
|
def deferred
|
|
244
188
|
@deferred ||= Deferred.new
|
|
245
189
|
end
|
|
190
|
+
# @!endgroup
|
|
246
191
|
|
|
247
|
-
|
|
248
|
-
|
|
192
|
+
# @!group Logging Context and Tags Configuration
|
|
193
|
+
# Allows configuring custom log context objects that will be included in every log entry.
|
|
194
|
+
# @return [Rage::Configuration::LogContext]
|
|
195
|
+
def log_context
|
|
196
|
+
@log_context ||= LogContext.new
|
|
249
197
|
end
|
|
250
198
|
|
|
251
|
-
|
|
252
|
-
|
|
199
|
+
# Allows configuring custom log tags that will be included in every log entry.
|
|
200
|
+
# @return [Rage::Configuration::LogTags]
|
|
201
|
+
def log_tags
|
|
202
|
+
@log_tags ||= LogTags.new
|
|
203
|
+
end
|
|
204
|
+
# @!endgroup
|
|
205
|
+
|
|
206
|
+
# @private
|
|
207
|
+
def internal
|
|
208
|
+
@internal ||= Internal.new
|
|
253
209
|
end
|
|
254
210
|
|
|
211
|
+
# @private
|
|
255
212
|
def run_after_initialize!
|
|
256
213
|
run_hooks_for!(:after_initialize, self)
|
|
257
214
|
end
|
|
258
215
|
|
|
216
|
+
class LogContext
|
|
217
|
+
# @private
|
|
218
|
+
def initialize
|
|
219
|
+
@objects = []
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# @private
|
|
223
|
+
def objects
|
|
224
|
+
@objects.dup
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Add a new custom log context object. Each context object is evaluated independently and the results are merged into the final log entry.
|
|
228
|
+
# @overload <<(hash)
|
|
229
|
+
# Add a static log context entry.
|
|
230
|
+
# @param hash [Hash] a hash representing the log context
|
|
231
|
+
# @example
|
|
232
|
+
# Rage.configure do
|
|
233
|
+
# config.log_context << { version: ENV["APP_VERSION"] }
|
|
234
|
+
# end
|
|
235
|
+
# @overload <<(callable)
|
|
236
|
+
# Add a dynamic log context entry. Dynamic context entries are executed on every log call to capture dynamic state like changing span IDs during request processing.
|
|
237
|
+
# @param callable [#call] a callable object that returns a hash representing the log context or nil
|
|
238
|
+
# @example
|
|
239
|
+
# Rage.configure do
|
|
240
|
+
# config.log_context << proc { { trace_id: MyObservabilitySDK.trace_id } if MyObservabilitySDK.active? }
|
|
241
|
+
# end
|
|
242
|
+
# @note Exceptions from dynamic context callables will cause the entire request to fail. Make sure to handle exceptions inside the callable if necessary.
|
|
243
|
+
def <<(block_or_hash)
|
|
244
|
+
validate_input!(block_or_hash)
|
|
245
|
+
@objects << block_or_hash
|
|
246
|
+
@objects.tap(&:flatten!).tap(&:uniq!)
|
|
247
|
+
|
|
248
|
+
self
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
alias_method :push, :<<
|
|
252
|
+
|
|
253
|
+
# Remove a custom log context object.
|
|
254
|
+
# @param block_or_hash [Hash, #call] the context object to remove
|
|
255
|
+
# @example
|
|
256
|
+
# Rage.configure do
|
|
257
|
+
# config.log_context.delete(MyObservabilitySDK::LOG_CONTEXT)
|
|
258
|
+
# end
|
|
259
|
+
def delete(block_or_hash)
|
|
260
|
+
@objects.delete(block_or_hash)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def validate_input!(obj)
|
|
266
|
+
if obj.is_a?(Array)
|
|
267
|
+
obj.each { |item| validate_input!(item) }
|
|
268
|
+
elsif !obj.is_a?(Hash) && !obj.respond_to?(:call)
|
|
269
|
+
raise ArgumentError, "custom log context has to be a hash, an array of hashes, or respond to `#call`"
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
class LogTags < LogContext
|
|
275
|
+
# @!method <<(block_or_string)
|
|
276
|
+
# Add a new custom log tag. Each tag is evaluated independently and the results are merged into the final log entry.
|
|
277
|
+
# @overload <<(string)
|
|
278
|
+
# Add a static log tag.
|
|
279
|
+
# @param string [String] the log tag
|
|
280
|
+
# @example
|
|
281
|
+
# Rage.configure do
|
|
282
|
+
# config.log_tags << Rage.env
|
|
283
|
+
# end
|
|
284
|
+
# @overload <<(callable)
|
|
285
|
+
# Add a dynamic log tag. Dynamic tags are executed on every log call.
|
|
286
|
+
# @param callable [#call] a callable object that returns a string representing the log tag, an array of log tags, or nil
|
|
287
|
+
# @example
|
|
288
|
+
# Rage.configure do
|
|
289
|
+
# config.log_tags << proc { Current.tenant.slug }
|
|
290
|
+
# end
|
|
291
|
+
# @note Exceptions from dynamic tag callables will cause the entire request to fail. Make sure to handle exceptions inside the callable if necessary.
|
|
292
|
+
|
|
293
|
+
# @!method delete(block_or_string)
|
|
294
|
+
# Remove a custom log tag object.
|
|
295
|
+
# @param block_or_string [String, #call] the tag object to remove
|
|
296
|
+
# @example
|
|
297
|
+
# Rage.configure do
|
|
298
|
+
# config.log_tags.delete(MyObservabilitySDK::LOG_TAGS)
|
|
299
|
+
# end
|
|
300
|
+
|
|
301
|
+
# @private
|
|
302
|
+
private
|
|
303
|
+
|
|
304
|
+
def validate_input!(obj)
|
|
305
|
+
if obj.is_a?(Array)
|
|
306
|
+
obj.each { |item| validate_input!(item) }
|
|
307
|
+
elsif !obj.respond_to?(:to_str) && !obj.respond_to?(:call)
|
|
308
|
+
raise ArgumentError, "custom log tag has to be a string, an array of strings, or respond to `#call`"
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
259
313
|
class Server
|
|
314
|
+
# @!attribute port
|
|
315
|
+
# Specify the port the server will listen on.
|
|
316
|
+
# @return [Integer]
|
|
317
|
+
# @example Change the default port
|
|
318
|
+
# Rage.configure do
|
|
319
|
+
# config.server.port = 3001
|
|
320
|
+
# end
|
|
321
|
+
#
|
|
322
|
+
# @!attribute workers_count
|
|
323
|
+
# Specify the number of worker processes to spawn. Use `-1` to spawn one worker per CPU core.
|
|
324
|
+
# @return [Integer]
|
|
325
|
+
# @example Change the number of worker processes
|
|
326
|
+
# Rage.configure do
|
|
327
|
+
# config.server.workers_count = 4
|
|
328
|
+
# end
|
|
329
|
+
#
|
|
330
|
+
# @!attribute timeout
|
|
331
|
+
# Specify the connection timeout in seconds.
|
|
332
|
+
# @return [Integer]
|
|
333
|
+
# @example Change the connection timeout
|
|
334
|
+
# Rage.configure do
|
|
335
|
+
# config.server.timeout = 30
|
|
336
|
+
# end
|
|
337
|
+
#
|
|
338
|
+
# @!attribute max_clients
|
|
339
|
+
# Limit the number of simultaneous connections the server can accept. Defaults to the maximum number of open files.
|
|
340
|
+
# @return [Integer]
|
|
341
|
+
#
|
|
342
|
+
# @note Decreasing this number is almost never a good idea. Depending on your application specifics, you are encouraged to use other methods to limit the number of concurrent connections:
|
|
343
|
+
#
|
|
344
|
+
# - If your application is exposed to the public, you may want to use a cloud rate limiter, like {https://developers.cloudflare.com/waf Cloudflare WAF} or {https://docs.fastly.com/en/ngwaf Fastly WAF}.
|
|
345
|
+
# - Otherwise, consider using tools like {https://github.com/rack/rack-attack Rack::Attack} or {https://github.com/mperham/connection_pool connection_pool}.
|
|
346
|
+
# @example Limit the amount of connections your application can accept
|
|
347
|
+
# Rage.configure do
|
|
348
|
+
# config.middleware.use Rack::Attack
|
|
349
|
+
# Rack::Attack.throttle("req/ip", limit: 300, period: 5.minutes) do |req|
|
|
350
|
+
# req.ip
|
|
351
|
+
# end
|
|
352
|
+
# end
|
|
353
|
+
# @example Limit the amount of connections to a specific resource
|
|
354
|
+
# HTTP = ConnectionPool.new(size: 5, timeout: 5) { Net::HTTP }
|
|
355
|
+
# HTTP.with do |conn|
|
|
356
|
+
# conn.get("/my-resource")
|
|
357
|
+
# end
|
|
260
358
|
attr_accessor :port, :workers_count, :timeout, :max_clients
|
|
359
|
+
|
|
360
|
+
# @private
|
|
261
361
|
attr_reader :threads_count
|
|
262
362
|
|
|
363
|
+
# @private
|
|
263
364
|
def initialize
|
|
264
365
|
@threads_count = 1
|
|
265
366
|
@workers_count = Rage.env.development? ? 1 : -1
|
|
@@ -268,16 +369,47 @@ class Rage::Configuration
|
|
|
268
369
|
end
|
|
269
370
|
|
|
270
371
|
class Middleware
|
|
372
|
+
# @private
|
|
271
373
|
attr_reader :middlewares
|
|
272
374
|
|
|
375
|
+
# @private
|
|
273
376
|
def initialize
|
|
274
377
|
@middlewares = [[Rage::FiberWrapper]]
|
|
275
378
|
end
|
|
276
379
|
|
|
380
|
+
# Add a new middleware to the end of the stack.
|
|
381
|
+
# @note This is the recommended way of adding a middleware.
|
|
382
|
+
# @param new_middleware [Class] the middleware class
|
|
383
|
+
# @param args [Array] arguments passed to the middleware initializer
|
|
384
|
+
# @param block [Proc] an optional block passed to the middleware initializer
|
|
385
|
+
# @example
|
|
386
|
+
# Rage.configure do
|
|
387
|
+
# config.middleware.use Rack::Cors do
|
|
388
|
+
# allow do
|
|
389
|
+
# origins "*"
|
|
390
|
+
# resource "*", headers: :any
|
|
391
|
+
# end
|
|
392
|
+
# end
|
|
393
|
+
# end
|
|
277
394
|
def use(new_middleware, *args, &block)
|
|
278
395
|
insert_after(@middlewares.length - 1, new_middleware, *args, &block)
|
|
279
396
|
end
|
|
280
397
|
|
|
398
|
+
# Insert a new middleware before an existing middleware in the stack.
|
|
399
|
+
# @note Rage always uses the `Rage::FiberWrapper` middleware, which wraps every request in a separate fiber. Make sure to always have this middleware in the top of the stack. Placing other middlewares in front may lead to undefined behavior.
|
|
400
|
+
# @param existing_middleware [Class, Integer] the existing middleware class or its index in the stack
|
|
401
|
+
# @param new_middleware [Class] the new middleware class
|
|
402
|
+
# @param args [Array] arguments passed to the middleware initializer
|
|
403
|
+
# @param block [Proc] an optional block passed to the middleware initializer
|
|
404
|
+
# @example
|
|
405
|
+
# Rage.configure do
|
|
406
|
+
# config.middleware.insert_before Rack::Runtime, Rack::Cors do
|
|
407
|
+
# allow do
|
|
408
|
+
# origins "*"
|
|
409
|
+
# resource "*", headers: :any
|
|
410
|
+
# end
|
|
411
|
+
# end
|
|
412
|
+
# end
|
|
281
413
|
def insert_before(existing_middleware, new_middleware, *args, &block)
|
|
282
414
|
index = find_middleware_index(existing_middleware)
|
|
283
415
|
if index == 0 && @middlewares[0][0] == Rage::FiberWrapper
|
|
@@ -286,11 +418,28 @@ class Rage::Configuration
|
|
|
286
418
|
@middlewares = (@middlewares[0...index] + [[new_middleware, args, block]] + @middlewares[index..]).uniq(&:first)
|
|
287
419
|
end
|
|
288
420
|
|
|
421
|
+
# Insert a new middleware after an existing middleware in the stack.
|
|
422
|
+
# @param existing_middleware [Class, Integer] the existing middleware class or its index in the stack
|
|
423
|
+
# @param new_middleware [Class] the new middleware class
|
|
424
|
+
# @param args [Array] arguments passed to the middleware initializer
|
|
425
|
+
# @param block [Proc] an optional block passed to the middleware initializer
|
|
426
|
+
# @example
|
|
427
|
+
# Rage.configure do
|
|
428
|
+
# config.middleware.insert_after Rack::Runtime, Rack::Cors do
|
|
429
|
+
# allow do
|
|
430
|
+
# origins "*"
|
|
431
|
+
# resource "*", headers: :any
|
|
432
|
+
# end
|
|
433
|
+
# end
|
|
434
|
+
# end
|
|
289
435
|
def insert_after(existing_middleware, new_middleware, *args, &block)
|
|
290
436
|
index = find_middleware_index(existing_middleware)
|
|
291
437
|
@middlewares = (@middlewares[0..index] + [[new_middleware, args, block]] + @middlewares[index + 1..]).uniq(&:first)
|
|
292
438
|
end
|
|
293
439
|
|
|
440
|
+
# Check if a middleware is included in the stack.
|
|
441
|
+
# @param middleware [Class, Integer] the middleware class or its index in the stack
|
|
442
|
+
# @return [Boolean]
|
|
294
443
|
def include?(middleware)
|
|
295
444
|
!!find_middleware_index(middleware) rescue false
|
|
296
445
|
end
|
|
@@ -312,9 +461,24 @@ class Rage::Configuration
|
|
|
312
461
|
end
|
|
313
462
|
|
|
314
463
|
class Cable
|
|
464
|
+
# @!attribute allowed_request_origins
|
|
465
|
+
# Restrict the server to only accept requests from specified origins. The origins can be strings or regular expressions. Defaults to `/localhost/` in development and test environments.
|
|
466
|
+
# @return [Array<Regexp>, Regexp, Array<String>, String, nil]
|
|
467
|
+
# @example
|
|
468
|
+
# Rage.configure do
|
|
469
|
+
# config.cable.allowed_request_origins = [/example\.com/, "myapp.com"]
|
|
470
|
+
# end
|
|
471
|
+
#
|
|
472
|
+
# @!attribute disable_request_forgery_protection
|
|
473
|
+
# Disable request forgery protection for WebSocket connections to allow requests from any origin.
|
|
474
|
+
# @return [Boolean]
|
|
475
|
+
# @example
|
|
476
|
+
# Rage.configure do
|
|
477
|
+
# config.cable.disable_request_forgery_protection = true
|
|
478
|
+
# end
|
|
315
479
|
attr_accessor :allowed_request_origins, :disable_request_forgery_protection
|
|
316
|
-
attr_reader :protocol
|
|
317
480
|
|
|
481
|
+
# @private
|
|
318
482
|
def initialize
|
|
319
483
|
@protocol = Rage::Cable::Protocols::ActioncableV1Json
|
|
320
484
|
@allowed_request_origins = if Rage.env.development? || Rage.env.test?
|
|
@@ -322,6 +486,22 @@ class Rage::Configuration
|
|
|
322
486
|
end
|
|
323
487
|
end
|
|
324
488
|
|
|
489
|
+
# Returns the protocol the server will use.
|
|
490
|
+
# @return [Class] the protocol class
|
|
491
|
+
def protocol
|
|
492
|
+
@protocol
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# Specify the protocol the server will use. Supported values include {Rage::Cable::Protocols::ActioncableV1Json :actioncable_v1_json} and {Rage::Cable::Protocols::RawWebSocketJson :raw_websocket_json}. Defaults to {Rage::Cable::Protocols::ActioncableV1Json :actioncable_v1_json}.
|
|
496
|
+
# @param protocol [:actioncable_v1_json, :raw_websocket_json] the protocol symbol
|
|
497
|
+
# @example Use the built-in ActionCable V1 JSON protocol
|
|
498
|
+
# Rage.configure do
|
|
499
|
+
# config.cable.protocol = :actioncable_v1_json
|
|
500
|
+
# end
|
|
501
|
+
# @example Use the built-in Raw WebSocket JSON protocol
|
|
502
|
+
# Rage.configure do
|
|
503
|
+
# config.cable.protocol = :raw_websocket_json
|
|
504
|
+
# end
|
|
325
505
|
def protocol=(protocol)
|
|
326
506
|
@protocol = case protocol
|
|
327
507
|
when Class
|
|
@@ -350,6 +530,7 @@ class Rage::Configuration
|
|
|
350
530
|
end
|
|
351
531
|
end
|
|
352
532
|
|
|
533
|
+
# @private
|
|
353
534
|
def config
|
|
354
535
|
@config ||= begin
|
|
355
536
|
config_file = Rage.root.join("config/cable.yml")
|
|
@@ -363,10 +544,12 @@ class Rage::Configuration
|
|
|
363
544
|
end
|
|
364
545
|
end
|
|
365
546
|
|
|
547
|
+
# @private
|
|
366
548
|
def adapter_config
|
|
367
549
|
config.except(:adapter)
|
|
368
550
|
end
|
|
369
551
|
|
|
552
|
+
# @private
|
|
370
553
|
def adapter
|
|
371
554
|
case config[:adapter]
|
|
372
555
|
when "redis"
|
|
@@ -376,20 +559,54 @@ class Rage::Configuration
|
|
|
376
559
|
end
|
|
377
560
|
|
|
378
561
|
class PublicFileServer
|
|
562
|
+
# @!attribute enabled
|
|
563
|
+
# Configure whether Rage should serve static files from the `public` directory. Defaults to `false`.
|
|
564
|
+
# @return [Boolean] whether the static file server is enabled
|
|
565
|
+
# @example
|
|
566
|
+
# Rage.configure do
|
|
567
|
+
# config.public_file_server.enabled = true
|
|
568
|
+
# end
|
|
379
569
|
attr_accessor :enabled
|
|
380
570
|
end
|
|
381
571
|
|
|
382
572
|
class OpenAPI
|
|
383
|
-
|
|
573
|
+
# Specify the rules to customize how OpenAPI tags are generated for API operations.
|
|
574
|
+
# The method accepts a callable object that receives the controller class, the action name (as a symbol), and the original tag generated by Rage.
|
|
575
|
+
# The callable should return a string or an array of strings representing the tags to use for the API operation.
|
|
576
|
+
# This enables grouping endpoints in the OpenAPI documentation according to your application's needs.
|
|
577
|
+
# @param tag_resolver [#call] a callable object that resolves OpenAPI tags
|
|
578
|
+
# @example
|
|
579
|
+
# Rage.configure do
|
|
580
|
+
# config.openapi.tag_resolver = proc do |controller_class, action_name, default_tag|
|
|
581
|
+
# if controller_class.name.start_with?("Admin::")
|
|
582
|
+
# [default_tag, "Admin"]
|
|
583
|
+
# else
|
|
584
|
+
# [default_tag, "Public"]
|
|
585
|
+
# end
|
|
586
|
+
# end
|
|
587
|
+
# end
|
|
588
|
+
def tag_resolver=(tag_resolver)
|
|
589
|
+
unless tag_resolver.respond_to?(:call)
|
|
590
|
+
raise ArgumentError, "Custom tag resolver should respond to `#call`"
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
@tag_resolver = tag_resolver
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# Returns the OpenAPI tag resolver used by Rage.
|
|
597
|
+
# @return [#call, nil]
|
|
598
|
+
def tag_resolver
|
|
599
|
+
@tag_resolver
|
|
600
|
+
end
|
|
384
601
|
end
|
|
385
602
|
|
|
386
603
|
class Deferred
|
|
387
|
-
|
|
388
|
-
|
|
604
|
+
# @private
|
|
389
605
|
def initialize
|
|
390
606
|
@configured = false
|
|
391
607
|
end
|
|
392
608
|
|
|
609
|
+
# Returns the backend instance used by `Rage::Deferred`.
|
|
393
610
|
def backend
|
|
394
611
|
unless @backend_class
|
|
395
612
|
@backend_class = Rage::Deferred::Backends::Disk
|
|
@@ -399,6 +616,27 @@ class Rage::Configuration
|
|
|
399
616
|
@backend_class.new(**@backend_options)
|
|
400
617
|
end
|
|
401
618
|
|
|
619
|
+
# Specify the backend used to persist deferred tasks. Supported values are `:disk`, which uses disk storage, or `nil`, which disables persistence of deferred tasks.
|
|
620
|
+
# @overload backend=(disk, options = {})
|
|
621
|
+
# Use the disk backend.
|
|
622
|
+
# @param options [Hash] additional backend options
|
|
623
|
+
# @option options [Pathname, String] :path the directory where deferred tasks will be stored. Defaults to `storage/`
|
|
624
|
+
# @option options [String] :prefix the prefix used for deferred task files. Defaults to `deferred-`
|
|
625
|
+
# @option options [Integer] :fsync_frequency the frequency of `fsync` calls in seconds. Defaults to `0.5`
|
|
626
|
+
# @example Use the disk backend with default options
|
|
627
|
+
# Rage.configure do
|
|
628
|
+
# config.deferred.backend = :disk
|
|
629
|
+
# end
|
|
630
|
+
# @example Use the disk backend with custom options
|
|
631
|
+
# Rage.configure do
|
|
632
|
+
# config.deferred.backend = :disk, path: "my_storage", fsync_frequency: 1000
|
|
633
|
+
# end
|
|
634
|
+
# @overload backend=(nil)
|
|
635
|
+
# Disable persistence of deferred tasks.
|
|
636
|
+
# @example
|
|
637
|
+
# Rage.configure do
|
|
638
|
+
# config.deferred.backend = nil
|
|
639
|
+
# end
|
|
402
640
|
def backend=(config)
|
|
403
641
|
@configured = true
|
|
404
642
|
|
|
@@ -422,6 +660,7 @@ class Rage::Configuration
|
|
|
422
660
|
class Backpressure
|
|
423
661
|
attr_reader :high_water_mark, :low_water_mark, :timeout, :sleep_interval, :timeout_iterations
|
|
424
662
|
|
|
663
|
+
# @private
|
|
425
664
|
def initialize(high_water_mark = nil, low_water_mark = nil, timeout = nil)
|
|
426
665
|
@high_water_mark = high_water_mark || 1_000
|
|
427
666
|
@low_water_mark = low_water_mark || (@high_water_mark * 0.8).round
|
|
@@ -432,6 +671,38 @@ class Rage::Configuration
|
|
|
432
671
|
end
|
|
433
672
|
end
|
|
434
673
|
|
|
674
|
+
# Returns the backpressure configuration used by `Rage::Deferred`.
|
|
675
|
+
# @return [Backpressure, nil]
|
|
676
|
+
def backpressure
|
|
677
|
+
@backpressure
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
# Configure backpressure settings for `Rage::Deferred`. Backpressure is used to limit the number of pending tasks in the queue and is disabled by default.
|
|
681
|
+
#
|
|
682
|
+
# @overload backpressure=(true)
|
|
683
|
+
# Enable backpressure with default settings.
|
|
684
|
+
# @example
|
|
685
|
+
# Rage.configure do
|
|
686
|
+
# config.deferred.backpressure = true
|
|
687
|
+
# end
|
|
688
|
+
#
|
|
689
|
+
# @overload backpressure=(false)
|
|
690
|
+
# Disable backpressure.
|
|
691
|
+
# @example
|
|
692
|
+
# Rage.configure do
|
|
693
|
+
# config.deferred.backpressure = false
|
|
694
|
+
# end
|
|
695
|
+
#
|
|
696
|
+
# @overload backpressure=(config)
|
|
697
|
+
# Enable backpressure with custom settings.
|
|
698
|
+
# @param config [Hash] backpressure configuration
|
|
699
|
+
# @option config [Integer] :high_water_mark the maximum number of deferred tasks allowed in the queue before applying backpressure. Defaults to `1000`.
|
|
700
|
+
# @option config [Integer] :low_water_mark the minimum number of deferred tasks in the queue at which backpressure is lifted. Defaults to `80%` of `:high_water_mark`.
|
|
701
|
+
# @option config [Integer] :timeout the maximum time in seconds to wait for the queue size to drop below `:low_water_mark` before raising the {Rage::Deferred::PushTimeout Rage::Deferred::PushTimeout} exception. Defaults to 2 seconds.
|
|
702
|
+
# @example
|
|
703
|
+
# Rage.configure do
|
|
704
|
+
# config.deferred.backpressure = { high_water_mark: 2000, low_water_mark: 1500, timeout: 5 }
|
|
705
|
+
# end
|
|
435
706
|
def backpressure=(config)
|
|
436
707
|
@configured = true
|
|
437
708
|
|
|
@@ -451,18 +722,22 @@ class Rage::Configuration
|
|
|
451
722
|
@backpressure = Backpressure.new(high_water_mark, low_water_mark, timeout)
|
|
452
723
|
end
|
|
453
724
|
|
|
725
|
+
# @private
|
|
454
726
|
def default_disk_storage_path
|
|
455
727
|
Pathname.new("storage")
|
|
456
728
|
end
|
|
457
729
|
|
|
730
|
+
# @private
|
|
458
731
|
def default_disk_storage_prefix
|
|
459
732
|
"deferred-"
|
|
460
733
|
end
|
|
461
734
|
|
|
735
|
+
# @private
|
|
462
736
|
def has_default_disk_storage?
|
|
463
737
|
default_disk_storage_path.glob("#{default_disk_storage_prefix}*").any?
|
|
464
738
|
end
|
|
465
739
|
|
|
740
|
+
# @private
|
|
466
741
|
def configured?
|
|
467
742
|
@configured
|
|
468
743
|
end
|
|
@@ -502,6 +777,14 @@ class Rage::Configuration
|
|
|
502
777
|
class Internal
|
|
503
778
|
attr_accessor :rails_mode
|
|
504
779
|
|
|
780
|
+
def initialized!
|
|
781
|
+
@initialized = true
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
def initialized?
|
|
785
|
+
!!@initialized
|
|
786
|
+
end
|
|
787
|
+
|
|
505
788
|
def patch_ar_pool?
|
|
506
789
|
!ENV["RAGE_DISABLE_AR_POOL_PATCH"] && !Rage.env.test?
|
|
507
790
|
end
|
|
@@ -531,5 +814,52 @@ class Rage::Configuration
|
|
|
531
814
|
else
|
|
532
815
|
@logger = Rage::Logger.new(nil)
|
|
533
816
|
end
|
|
817
|
+
|
|
818
|
+
if @log_formatter && @logger.external_logger.is_a?(Rage::Logger::External::Dynamic)
|
|
819
|
+
puts "WARNING: changing the log formatter via `config.log_formatter=` has no effect when using a custom external logger."
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
if @log_context
|
|
823
|
+
Rage.__log_processor.add_custom_context(@log_context.objects)
|
|
824
|
+
@logger.dynamic_context = Rage.__log_processor.dynamic_context
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
if @log_tags
|
|
828
|
+
Rage.__log_processor.add_custom_tags(@log_tags.objects)
|
|
829
|
+
@logger.dynamic_tags = Rage.__log_processor.dynamic_tags
|
|
830
|
+
end
|
|
534
831
|
end
|
|
535
832
|
end
|
|
833
|
+
|
|
834
|
+
# @!parse [ruby]
|
|
835
|
+
# # @note This class does not exist at runtime and is used for documentation purposes only. Do not inherit external loggers from it.
|
|
836
|
+
# class ExternalLoggerInterface
|
|
837
|
+
# # Called whenever a log entry is created.
|
|
838
|
+
# #
|
|
839
|
+
# # Rage automatically detects which parameters your external logger's `#call` method accepts, and only passes those parameters. You can omit any of the described parameters in your implementation.
|
|
840
|
+
# #
|
|
841
|
+
# # @param severity [:debug, :info, :warn, :error, :fatal, :unknown] the log severity
|
|
842
|
+
# # @param tags [Array] the log tags submitted via {Rage::Logger#tagged Rage::Logger#tagged}. The first tag is always the request ID
|
|
843
|
+
# # @param context [Hash] the log context submitted via {Rage::Logger#with_context Rage::Logger#with_context}
|
|
844
|
+
# # @param message [String, nil] the log message. For request logs generated by Rage, this is always `nil`
|
|
845
|
+
# # @param request_info [Hash, nil] request-specific information. The value is `nil` for non-request logs; for request logs, contains the following keys:
|
|
846
|
+
# # @option request_info [Hash] :env the Rack env object
|
|
847
|
+
# # @option request_info [Hash] :params the request parameters
|
|
848
|
+
# # @option request_info [Array] :response the Rack response object
|
|
849
|
+
# # @option request_info [Float] :duration the duration of the request in milliseconds
|
|
850
|
+
# # @example
|
|
851
|
+
# # Rage.configure do
|
|
852
|
+
# # config.logger = proc do |severity:, tags:, context:, message:, request_info:|
|
|
853
|
+
# # data = context.merge(tags:)
|
|
854
|
+
# #
|
|
855
|
+
# # if request_info
|
|
856
|
+
# # data[:path] = request_info[:env]["PATH_INFO"]
|
|
857
|
+
# # MyLoggingSDK.info("Request completed", data)
|
|
858
|
+
# # else
|
|
859
|
+
# # MyLoggingSDK.public_send(severity, message, data)
|
|
860
|
+
# # end
|
|
861
|
+
# # end
|
|
862
|
+
# # end
|
|
863
|
+
# def call(severity:, tags:, context:, message:, request_info:)
|
|
864
|
+
# end
|
|
865
|
+
# end
|