prefab-cloud-ruby 1.1.2 → 1.2.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/CHANGELOG.md +4 -0
- data/VERSION +1 -1
- data/lib/prefab/client.rb +12 -10
- data/lib/prefab/config_client.rb +24 -25
- data/lib/prefab/config_loader.rb +4 -2
- data/lib/prefab/config_value_unwrapper.rb +2 -0
- data/lib/prefab/context_shape_aggregator.rb +4 -2
- data/lib/prefab/criteria_evaluator.rb +2 -1
- data/lib/prefab/evaluation_summary_aggregator.rb +4 -2
- data/lib/prefab/example_contexts_aggregator.rb +4 -2
- data/lib/prefab/internal_logger.rb +16 -13
- data/lib/prefab/log_path_aggregator.rb +4 -2
- data/lib/prefab/logger_client.rb +40 -5
- data/lib/prefab/options.rb +3 -1
- data/lib/prefab/periodic_sync.rb +3 -6
- data/lib/prefab/prefab.rb +56 -0
- data/lib/prefab/sse_logger.rb +21 -5
- data/lib/prefab/yaml_config_parser.rb +4 -2
- data/lib/prefab-cloud-ruby.rb +1 -0
- data/prefab-cloud-ruby.gemspec +6 -3
- data/test/support/common_helpers.rb +11 -9
- data/test/support/mock_base_client.rb +1 -3
- data/test/test_client.rb +12 -3
- data/test/test_context_shape_aggregator.rb +2 -2
- data/test/test_criteria_evaluator.rb +2 -13
- data/test/test_log_path_aggregator.rb +1 -1
- data/test/test_logger.rb +103 -5
- data/test/test_logger_initialization.rb +12 -0
- data/test/test_prefab.rb +12 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5bfaef7482dfdd1946bd0b0dee2cd0f366f5661e275bb8b906fb207d9b241d14
|
4
|
+
data.tar.gz: ed36c67d8039d2250fab5c6b1ff08da23cde51f696c256daaa943639205f2d0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 209f9cc828ec0eccd3b34162d9513c53695bf36838b60601103fd699748fad78af28cb622c1d057ea63162abd143f4db176395726a52517426885483789c7a3c
|
7
|
+
data.tar.gz: 3d368dc2aab1e941607e437d889dd51c8d5aaf9e65208d81552f930cfa34db0eff02fba1b7f2e7a3a08ab87a23e11fad67a0634861e586649fbc466a4f2d3629
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.2.0 - 2023-10-30
|
4
|
+
- Add `Prefab.get('key')` style usage after a `Prefab.init()` call (#151)
|
5
|
+
- Add `add_context_keys` and `with_context_keys` method for LoggerClient (#145)
|
6
|
+
|
3
7
|
## 1.1.2 - 2023-10-13
|
4
8
|
|
5
9
|
- Add `cloud.prefab.client.criteria_evaluator` `debug` logging of evaluations (#150)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/lib/prefab/client.rb
CHANGED
@@ -6,6 +6,7 @@ module Prefab
|
|
6
6
|
class Client
|
7
7
|
MAX_SLEEP_SEC = 10
|
8
8
|
BASE_SLEEP_SEC = 0.5
|
9
|
+
LOG = Prefab::InternalLogger.new(Client)
|
9
10
|
|
10
11
|
attr_reader :namespace, :interceptor, :api_key, :prefab_api_url, :options, :instance_hash
|
11
12
|
|
@@ -14,15 +15,19 @@ module Prefab
|
|
14
15
|
@namespace = @options.namespace
|
15
16
|
@stubs = {}
|
16
17
|
@instance_hash = UUID.new.generate
|
18
|
+
Prefab::LoggerClient.new(@options.logdev, formatter: @options.log_formatter,
|
19
|
+
prefix: @options.log_prefix,
|
20
|
+
log_path_aggregator: log_path_aggregator
|
21
|
+
)
|
17
22
|
|
18
23
|
if @options.local_only?
|
19
|
-
|
24
|
+
LOG.debug 'Prefab Running in Local Mode'
|
20
25
|
else
|
21
26
|
@api_key = @options.api_key
|
22
27
|
raise Prefab::Errors::InvalidApiKeyError, @api_key if @api_key.nil? || @api_key.empty? || api_key.count('-') < 1
|
23
28
|
|
24
29
|
@prefab_api_url = @options.prefab_api_url
|
25
|
-
|
30
|
+
LOG.debug "Prefab Connecting to: #{@prefab_api_url}"
|
26
31
|
end
|
27
32
|
|
28
33
|
context.clear
|
@@ -54,9 +59,7 @@ module Prefab
|
|
54
59
|
end
|
55
60
|
|
56
61
|
def log
|
57
|
-
|
58
|
-
prefix: @options.log_prefix,
|
59
|
-
log_path_aggregator: log_path_aggregator)
|
62
|
+
Prefab::LoggerClient.instance
|
60
63
|
end
|
61
64
|
|
62
65
|
def context_shape_aggregator
|
@@ -99,10 +102,6 @@ module Prefab
|
|
99
102
|
resolver.on_update(&block)
|
100
103
|
end
|
101
104
|
|
102
|
-
def log_internal(level, msg, path = nil, **tags)
|
103
|
-
log.log_internal msg, path, nil, level, tags
|
104
|
-
end
|
105
|
-
|
106
105
|
def enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED)
|
107
106
|
feature_flag_client.feature_is_on_for?(feature_name, jit_context)
|
108
107
|
end
|
@@ -133,7 +132,10 @@ module Prefab
|
|
133
132
|
# $prefab.set_rails_loggers
|
134
133
|
# end
|
135
134
|
def fork
|
136
|
-
|
135
|
+
log_options = self.log.context_keys.to_a # get keys pre-fork
|
136
|
+
Prefab::Client.new(@options.for_fork).tap do |client|
|
137
|
+
client.log.add_context_keys(*log_options)
|
138
|
+
end
|
137
139
|
end
|
138
140
|
|
139
141
|
private
|
data/lib/prefab/config_client.rb
CHANGED
@@ -8,11 +8,12 @@ module Prefab
|
|
8
8
|
STALE_CACHE_WARN_HOURS = 5
|
9
9
|
AUTH_USER = 'authuser'
|
10
10
|
LOGGING_KEY_PREFIX = "#{Prefab::LoggerClient::BASE_KEY}#{Prefab::LoggerClient::SEP}".freeze
|
11
|
+
LOG = Prefab::InternalLogger.new(ConfigClient)
|
11
12
|
|
12
13
|
def initialize(base_client, timeout)
|
13
14
|
@base_client = base_client
|
14
15
|
@options = base_client.options
|
15
|
-
|
16
|
+
LOG.debug 'Initialize ConfigClient'
|
16
17
|
@timeout = timeout
|
17
18
|
|
18
19
|
@stream_lock = Concurrent::ReadWriteLock.new
|
@@ -23,9 +24,9 @@ module Prefab
|
|
23
24
|
@config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
|
24
25
|
|
25
26
|
@initialization_lock = Concurrent::ReadWriteLock.new
|
26
|
-
|
27
|
+
LOG.debug 'Initialize ConfigClient: AcquireWriteLock'
|
27
28
|
@initialization_lock.acquire_write_lock
|
28
|
-
|
29
|
+
LOG.debug 'Initialize ConfigClient: AcquiredWriteLock'
|
29
30
|
@initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
|
30
31
|
|
31
32
|
if @options.local_only?
|
@@ -96,8 +97,7 @@ module Prefab
|
|
96
97
|
raise Prefab::Errors::InitializationTimeoutError.new(@options.initialization_timeout_sec, key)
|
97
98
|
end
|
98
99
|
|
99
|
-
@
|
100
|
-
"Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have"
|
100
|
+
LOG.warn("Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have")
|
101
101
|
@initialization_lock.release_write_lock
|
102
102
|
end
|
103
103
|
|
@@ -117,7 +117,7 @@ module Prefab
|
|
117
117
|
|
118
118
|
return if success
|
119
119
|
|
120
|
-
|
120
|
+
LOG.warn 'No success loading checkpoints'
|
121
121
|
end
|
122
122
|
|
123
123
|
def load_checkpoint_api_cdn
|
@@ -138,11 +138,11 @@ module Prefab
|
|
138
138
|
cache_configs(configs)
|
139
139
|
true
|
140
140
|
else
|
141
|
-
|
141
|
+
LOG.info "Checkpoint #{source} failed to load. Response #{resp.status}"
|
142
142
|
false
|
143
143
|
end
|
144
144
|
rescue StandardError => e
|
145
|
-
|
145
|
+
LOG.warn "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
|
146
146
|
false
|
147
147
|
end
|
148
148
|
|
@@ -167,11 +167,9 @@ module Prefab
|
|
167
167
|
@config_loader.set(config, source)
|
168
168
|
end
|
169
169
|
if @config_loader.highwater_mark > starting_highwater_mark
|
170
|
-
@
|
171
|
-
"Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
|
170
|
+
LOG.debug("Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{project_id} environment: #{project_env_id} and namespace: '#{@namespace}'")
|
172
171
|
else
|
173
|
-
@
|
174
|
-
"Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.", 'load_configs'
|
172
|
+
LOG.debug("Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.")
|
175
173
|
end
|
176
174
|
@config_resolver.update
|
177
175
|
finish_init!(source, project_id)
|
@@ -196,9 +194,9 @@ module Prefab
|
|
196
194
|
f.flock(File::LOCK_EX)
|
197
195
|
f.write(PrefabProto::Configs.encode_json(configs))
|
198
196
|
end
|
199
|
-
|
197
|
+
LOG.debug "Cached configs to #{cache_path}"
|
200
198
|
rescue => e
|
201
|
-
|
199
|
+
LOG.debug "Failed to cache configs to #{cache_path} #{e}"
|
202
200
|
end
|
203
201
|
|
204
202
|
def load_cache
|
@@ -210,11 +208,11 @@ module Prefab
|
|
210
208
|
|
211
209
|
hours_old = ((Time.now - File.mtime(f)) / 60 / 60).round(2)
|
212
210
|
if hours_old > STALE_CACHE_WARN_HOURS
|
213
|
-
|
211
|
+
LOG.info "Stale Cache Load: #{hours_old} hours old"
|
214
212
|
end
|
215
213
|
end
|
216
214
|
rescue => e
|
217
|
-
|
215
|
+
LOG.debug "Failed to read cached configs at #{cache_path}. #{e}"
|
218
216
|
false
|
219
217
|
end
|
220
218
|
|
@@ -222,13 +220,13 @@ module Prefab
|
|
222
220
|
def start_checkpointing_thread
|
223
221
|
Thread.new do
|
224
222
|
loop do
|
225
|
-
load_checkpoint
|
226
|
-
|
227
223
|
started_at = Time.now
|
228
224
|
delta = @checkpoint_freq_secs - (Time.now - started_at)
|
229
225
|
sleep(delta) if delta > 0
|
226
|
+
|
227
|
+
load_checkpoint
|
230
228
|
rescue StandardError => e
|
231
|
-
|
229
|
+
LOG.debug "Issue Checkpointing #{e.message}"
|
232
230
|
end
|
233
231
|
end
|
234
232
|
end
|
@@ -236,9 +234,10 @@ module Prefab
|
|
236
234
|
def finish_init!(source, project_id)
|
237
235
|
return unless @initialization_lock.write_locked?
|
238
236
|
|
239
|
-
|
237
|
+
LOG.debug "Unlocked Config via #{source}"
|
240
238
|
@initialization_lock.release_write_lock
|
241
|
-
|
239
|
+
|
240
|
+
Prefab::LoggerClient.instance.config_client = self
|
242
241
|
presenter = Prefab::ConfigClientPresenter.new(
|
243
242
|
size: @config_resolver.local_store.size,
|
244
243
|
source: source,
|
@@ -246,8 +245,8 @@ module Prefab
|
|
246
245
|
project_env_id: @config_resolver.project_env_id,
|
247
246
|
api_key_id: @base_client.options.api_key_id
|
248
247
|
)
|
249
|
-
|
250
|
-
|
248
|
+
LOG.info presenter.to_s
|
249
|
+
LOG.debug to_s
|
251
250
|
end
|
252
251
|
|
253
252
|
def start_sse_streaming_connection_thread(start_at_id)
|
@@ -259,11 +258,11 @@ module Prefab
|
|
259
258
|
'X-PrefabCloud-Client-Version' => "prefab-cloud-ruby-#{Prefab::VERSION}"
|
260
259
|
}
|
261
260
|
url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
|
262
|
-
|
261
|
+
LOG.debug "SSE Streaming Connect to #{url} start_at #{start_at_id}"
|
263
262
|
@streaming_thread = SSE::Client.new(url,
|
264
263
|
headers: headers,
|
265
264
|
read_timeout: SSE_READ_TIMEOUT,
|
266
|
-
logger: Prefab::SseLogger.new
|
265
|
+
logger: Prefab::SseLogger.new) do |client|
|
267
266
|
client.on_event do |event|
|
268
267
|
configs = PrefabProto::Configs.decode(Base64.decode64(event.data))
|
269
268
|
load_configs(configs, :sse)
|
data/lib/prefab/config_loader.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Prefab
|
4
4
|
class ConfigLoader
|
5
|
+
LOG = Prefab::InternalLogger.new(ConfigLoader)
|
6
|
+
|
5
7
|
attr_reader :highwater_mark
|
6
8
|
|
7
9
|
def initialize(base_client)
|
@@ -29,8 +31,8 @@ module Prefab
|
|
29
31
|
@api_config.delete(config.key)
|
30
32
|
else
|
31
33
|
if @api_config[config.key]
|
32
|
-
|
33
|
-
"Replace #{config.key} with value from #{source} #{@api_config[config.key][:config].id} -> #{config.id}"
|
34
|
+
LOG.debug(
|
35
|
+
"Replace #{config.key} with value from #{source} #{@api_config[config.key][:config].id} -> #{config.id}")
|
34
36
|
end
|
35
37
|
@api_config[config.key] = { source: source, config: config }
|
36
38
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Prefab
|
4
4
|
class ConfigValueUnwrapper
|
5
|
+
LOG = Prefab::InternalLogger.new(ConfigValueUnwrapper)
|
5
6
|
attr_reader :value, :weighted_value_index
|
6
7
|
|
7
8
|
def initialize(value, weighted_value_index = nil)
|
@@ -16,6 +17,7 @@ module Prefab
|
|
16
17
|
when :string_list
|
17
18
|
value.string_list.values
|
18
19
|
else
|
20
|
+
LOG.error "Unknown type: #{config_value.type}"
|
19
21
|
raise "Unknown type: #{config_value.type}"
|
20
22
|
end
|
21
23
|
end
|
@@ -6,6 +6,8 @@ module Prefab
|
|
6
6
|
class ContextShapeAggregator
|
7
7
|
include Prefab::PeriodicSync
|
8
8
|
|
9
|
+
LOG = Prefab::InternalLogger.new(ContextShapeAggregator)
|
10
|
+
|
9
11
|
attr_reader :data
|
10
12
|
|
11
13
|
def initialize(client:, max_shapes:, sync_interval:)
|
@@ -43,7 +45,7 @@ module Prefab
|
|
43
45
|
|
44
46
|
def flush(to_ship, _)
|
45
47
|
pool.post do
|
46
|
-
|
48
|
+
LOG.debug "Uploading context shapes for #{to_ship.values.size}"
|
47
49
|
|
48
50
|
shapes = PrefabProto::ContextShapes.new(
|
49
51
|
shapes: to_ship.map do |name, shape|
|
@@ -56,7 +58,7 @@ module Prefab
|
|
56
58
|
|
57
59
|
result = post('/api/v1/context-shapes', shapes)
|
58
60
|
|
59
|
-
|
61
|
+
LOG.debug "Uploaded #{to_ship.values.size} shapes: #{result.status}"
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
@@ -7,6 +7,7 @@ module Prefab
|
|
7
7
|
# This class evaluates a config's criteria. `evaluate` returns the value of
|
8
8
|
# the first match based on the provided properties.
|
9
9
|
class CriteriaEvaluator
|
10
|
+
LOG = Prefab::InternalLogger.new(CriteriaEvaluator)
|
10
11
|
NAMESPACE_KEY = 'NAMESPACE'
|
11
12
|
NO_MATCHING_ROWS = [].freeze
|
12
13
|
|
@@ -21,7 +22,7 @@ module Prefab
|
|
21
22
|
def evaluate(properties)
|
22
23
|
rtn = evaluate_for_env(@project_env_id, properties) ||
|
23
24
|
evaluate_for_env(0, properties)
|
24
|
-
|
25
|
+
LOG.debug "Eval Key #{@config.key} Result #{rtn&.value} with #{properties.to_h}" unless @config.config_type == :LOG_LEVEL
|
25
26
|
rtn
|
26
27
|
end
|
27
28
|
|
@@ -9,6 +9,8 @@ module Prefab
|
|
9
9
|
class EvaluationSummaryAggregator
|
10
10
|
include Prefab::PeriodicSync
|
11
11
|
|
12
|
+
LOG = Prefab::InternalLogger.new(EvaluationSummaryAggregator)
|
13
|
+
|
12
14
|
attr_reader :data
|
13
15
|
|
14
16
|
def initialize(client:, max_keys:, sync_interval:)
|
@@ -47,7 +49,7 @@ module Prefab
|
|
47
49
|
|
48
50
|
def flush(to_ship, start_at_was)
|
49
51
|
pool.post do
|
50
|
-
|
52
|
+
LOG.debug "Flushing #{to_ship.size} summaries"
|
51
53
|
|
52
54
|
summaries_proto = PrefabProto::ConfigEvaluationSummaries.new(
|
53
55
|
start: start_at_was,
|
@@ -57,7 +59,7 @@ module Prefab
|
|
57
59
|
|
58
60
|
result = post('/api/v1/telemetry', events(summaries_proto))
|
59
61
|
|
60
|
-
|
62
|
+
LOG.debug "Uploaded #{to_ship.size} summaries: #{result.status}"
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
@@ -10,6 +10,8 @@ module Prefab
|
|
10
10
|
class ExampleContextsAggregator
|
11
11
|
include Prefab::PeriodicSync
|
12
12
|
|
13
|
+
LOG = Prefab::InternalLogger.new(ExampleContextsAggregator)
|
14
|
+
|
13
15
|
attr_reader :data, :cache
|
14
16
|
|
15
17
|
ONE_HOUR = 60 * 60
|
@@ -43,11 +45,11 @@ module Prefab
|
|
43
45
|
|
44
46
|
def flush(to_ship, _)
|
45
47
|
pool.post do
|
46
|
-
|
48
|
+
LOG.debug "Flushing #{to_ship.size} examples"
|
47
49
|
|
48
50
|
result = post('/api/v1/telemetry', events(to_ship))
|
49
51
|
|
50
|
-
|
52
|
+
LOG.debug "Uploaded #{to_ship.size} examples: #{result.status}"
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
@@ -2,29 +2,32 @@
|
|
2
2
|
|
3
3
|
module Prefab
|
4
4
|
class InternalLogger < ::Logger
|
5
|
-
def initialize(path
|
6
|
-
|
7
|
-
|
5
|
+
def initialize(path)
|
6
|
+
if path.is_a?(Class)
|
7
|
+
@path = path.name.split('::').last.downcase
|
8
|
+
else
|
9
|
+
@path = path
|
10
|
+
end
|
8
11
|
end
|
9
12
|
|
10
|
-
def debug
|
11
|
-
|
13
|
+
def debug msg
|
14
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::DEBUG, msg, @path
|
12
15
|
end
|
13
16
|
|
14
|
-
def info
|
15
|
-
|
17
|
+
def info msg
|
18
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::INFO, msg, @path
|
16
19
|
end
|
17
20
|
|
18
|
-
def warn
|
19
|
-
|
21
|
+
def warn msg
|
22
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::WARN, msg, @path
|
20
23
|
end
|
21
24
|
|
22
|
-
def error
|
23
|
-
|
25
|
+
def error msg
|
26
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::ERROR, msg, @path
|
24
27
|
end
|
25
28
|
|
26
|
-
def fatal
|
27
|
-
|
29
|
+
def fatal msg
|
30
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::FATAL, msg, @path
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
@@ -4,6 +4,8 @@ require_relative 'periodic_sync'
|
|
4
4
|
|
5
5
|
module Prefab
|
6
6
|
class LogPathAggregator
|
7
|
+
LOG = Prefab::InternalLogger.new(LogPathAggregator)
|
8
|
+
|
7
9
|
include Prefab::PeriodicSync
|
8
10
|
|
9
11
|
INCREMENT = ->(count) { (count || 0) + 1 }
|
@@ -41,7 +43,7 @@ module Prefab
|
|
41
43
|
|
42
44
|
def flush(to_ship, start_at_was)
|
43
45
|
pool.post do
|
44
|
-
|
46
|
+
LOG.debug "Uploading stats for #{to_ship.size} paths"
|
45
47
|
|
46
48
|
aggregate = Hash.new { |h, k| h[k] = PrefabProto::Logger.new }
|
47
49
|
|
@@ -60,7 +62,7 @@ module Prefab
|
|
60
62
|
|
61
63
|
result = post('/api/v1/known-loggers', loggers)
|
62
64
|
|
63
|
-
|
65
|
+
LOG.debug "Uploaded #{to_ship.size} paths: #{result.status}"
|
64
66
|
end
|
65
67
|
end
|
66
68
|
end
|
data/lib/prefab/logger_client.rb
CHANGED
@@ -17,7 +17,13 @@ module Prefab
|
|
17
17
|
PrefabProto::LogLevel::FATAL => ::Logger::FATAL
|
18
18
|
}.freeze
|
19
19
|
|
20
|
-
def
|
20
|
+
def self.instance
|
21
|
+
@@shared_instance ||= LoggerClient.new($stdout)
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :context_keys
|
25
|
+
|
26
|
+
def initialize(logdev, log_path_aggregator: nil, formatter: Options::DEFAULT_LOG_FORMATTER, prefix: nil)
|
21
27
|
super(logdev)
|
22
28
|
self.formatter = formatter
|
23
29
|
@config_client = BootstrappingConfigClient.new
|
@@ -25,9 +31,28 @@ module Prefab
|
|
25
31
|
@recurse_check = Concurrent::Map.new(initial_capacity: 2)
|
26
32
|
@prefix = "#{prefix}#{prefix && '.'}"
|
27
33
|
|
34
|
+
@context_keys = Concurrent::Set.new
|
35
|
+
|
28
36
|
@log_path_aggregator = log_path_aggregator
|
37
|
+
@@shared_instance = self
|
29
38
|
end
|
30
39
|
|
40
|
+
def add_context_keys(*keys)
|
41
|
+
@context_keys += keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def with_context_keys(*keys)
|
45
|
+
@context_keys += keys
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
@context_keys -= keys
|
49
|
+
end
|
50
|
+
|
51
|
+
def internal_logger(path=nil)
|
52
|
+
InternalLogger.new(path, self)
|
53
|
+
end
|
54
|
+
|
55
|
+
# InternalLoggers Will Call This
|
31
56
|
def add_internal(severity, message, progname, loc, log_context={}, &block)
|
32
57
|
path_loc = get_loc_path(loc)
|
33
58
|
path = @prefix + path_loc
|
@@ -37,7 +62,7 @@ module Prefab
|
|
37
62
|
log(message, path, progname, severity, log_context, &block)
|
38
63
|
end
|
39
64
|
|
40
|
-
def log_internal(message, path,
|
65
|
+
def log_internal(severity, message, path, log_context={}, &block)
|
41
66
|
return if @recurse_check[local_log_id]
|
42
67
|
@recurse_check[local_log_id] = true
|
43
68
|
|
@@ -47,7 +72,7 @@ module Prefab
|
|
47
72
|
INTERNAL_PREFIX
|
48
73
|
end
|
49
74
|
begin
|
50
|
-
log(message, path,
|
75
|
+
log(message, path, nil, severity, log_context, &block)
|
51
76
|
ensure
|
52
77
|
@recurse_check[local_log_id] = false
|
53
78
|
end
|
@@ -55,7 +80,6 @@ module Prefab
|
|
55
80
|
|
56
81
|
def log(message, path, progname, severity, log_context={})
|
57
82
|
severity ||= ::Logger::UNKNOWN
|
58
|
-
|
59
83
|
return true if @logdev.nil? || severity < level_of(path) || @silences[local_log_id]
|
60
84
|
|
61
85
|
progname = @progname if progname.nil?
|
@@ -70,7 +94,7 @@ module Prefab
|
|
70
94
|
end
|
71
95
|
|
72
96
|
@logdev.write(
|
73
|
-
format_message(format_severity(severity), Time.now, progname, message, path, log_context)
|
97
|
+
format_message(format_severity(severity), Time.now, progname, message, path, stringify_keys(log_context.merge(fetch_context_for_context_keys)))
|
74
98
|
)
|
75
99
|
true
|
76
100
|
end
|
@@ -138,6 +162,17 @@ module Prefab
|
|
138
162
|
|
139
163
|
NO_DEFAULT = nil
|
140
164
|
|
165
|
+
def stringify_keys(hash)
|
166
|
+
Hash[hash.map { |k, v| [k.to_s, v] }]
|
167
|
+
end
|
168
|
+
|
169
|
+
def fetch_context_for_context_keys
|
170
|
+
context = Prefab::Context.current.to_h
|
171
|
+
Hash[@context_keys.map do |key|
|
172
|
+
[key, context.dig(*key.split("."))]
|
173
|
+
end]
|
174
|
+
end
|
175
|
+
|
141
176
|
# Find the closest match to 'log_level.path' in config
|
142
177
|
def level_of(path)
|
143
178
|
closest_log_level_match = nil
|
data/lib/prefab/options.rb
CHANGED
@@ -29,7 +29,9 @@ module Prefab
|
|
29
29
|
|
30
30
|
progname = (progname.nil? || progname.empty?) ? path : "#{progname}: #{path}"
|
31
31
|
|
32
|
-
formatted_log_context = log_context.sort.map
|
32
|
+
formatted_log_context = log_context.sort.map do |k, v|
|
33
|
+
v.nil? ? nil : "#{k}=#{v}"
|
34
|
+
end.compact.join(" ")
|
33
35
|
"#{severity.ljust(5)} #{datetime}:#{' ' if progname}#{progname} #{msg}#{log_context.any? ? " " + formatted_log_context : ""}\n"
|
34
36
|
}
|
35
37
|
|
data/lib/prefab/periodic_sync.rb
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
module Prefab
|
4
4
|
module PeriodicSync
|
5
|
+
LOG = Prefab::InternalLogger.new("periodsync")
|
5
6
|
def sync
|
6
7
|
return if @data.size.zero?
|
7
8
|
|
8
|
-
|
9
|
+
LOG.debug "Syncing #{@data.size} items"
|
9
10
|
|
10
11
|
start_at_was = @start_at
|
11
12
|
@start_at = Prefab::TimeHelpers.now_in_ms
|
@@ -36,7 +37,7 @@ module Prefab
|
|
36
37
|
@sync_interval = calculate_sync_interval(sync_interval)
|
37
38
|
|
38
39
|
Thread.new do
|
39
|
-
|
40
|
+
LOG.debug "Initialized #{@name} instance_hash=#{@client.instance_hash}"
|
40
41
|
|
41
42
|
loop do
|
42
43
|
sleep @sync_interval.call
|
@@ -45,10 +46,6 @@ module Prefab
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
|
-
def log_internal(message)
|
49
|
-
@client.log.log_internal message, @name, nil, ::Logger::DEBUG
|
50
|
-
end
|
51
|
-
|
52
49
|
def pool
|
53
50
|
@pool ||= Concurrent::ThreadPoolExecutor.new(
|
54
51
|
fallback_policy: :discard,
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Prefab
|
4
|
+
@@lock = Concurrent::ReadWriteLock.new
|
5
|
+
|
6
|
+
def self.init(options = Prefab::Options.new)
|
7
|
+
unless @singleton.nil?
|
8
|
+
Prefab::LoggerClient.instance.warn 'Prefab already initialized.'
|
9
|
+
return @singleton
|
10
|
+
end
|
11
|
+
|
12
|
+
@@lock.with_write_lock {
|
13
|
+
@singleton = Prefab::Client.new(options)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.fork
|
18
|
+
ensure_initialized
|
19
|
+
@@lock.with_write_lock {
|
20
|
+
@singleton = @singleton.fork
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.set_rails_loggers
|
25
|
+
ensure_initialized
|
26
|
+
@singleton.set_rails_loggers
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get(key, properties = NO_DEFAULT_PROVIDED)
|
30
|
+
ensure_initialized
|
31
|
+
@singleton.get(key, properties)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED)
|
35
|
+
ensure_initialized
|
36
|
+
@singleton.enabled?(feature_name, jit_context)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.with_context(properties, &block)
|
40
|
+
ensure_initialized
|
41
|
+
@singleton.with_context(properties, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.instance
|
45
|
+
ensure_initialized
|
46
|
+
@singleton
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def self.ensure_initialized
|
52
|
+
if not defined? @singleton or @singleton.nil?
|
53
|
+
raise "Use Prefab.initialize before calling Prefab.get"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/prefab/sse_logger.rb
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Prefab
|
4
|
-
class SseLogger <
|
5
|
-
def initialize(
|
6
|
-
|
4
|
+
class SseLogger < ::Logger
|
5
|
+
def initialize()
|
6
|
+
@path = "sse"
|
7
|
+
end
|
8
|
+
|
9
|
+
def debug(progname = nil, &block)
|
10
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::DEBUG, progname, @path, &block
|
11
|
+
end
|
12
|
+
|
13
|
+
def info(progname = nil, &block)
|
14
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::INFO, progname, @path, &block
|
7
15
|
end
|
8
16
|
|
9
17
|
# The SSE::Client warns on a perfectly normal stream disconnect, recast to info
|
10
|
-
def warn(progname = nil)
|
11
|
-
|
18
|
+
def warn(progname = nil, &block)
|
19
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::INFO, progname, @path, &block
|
20
|
+
end
|
21
|
+
|
22
|
+
def error(progname = nil, &block)
|
23
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::ERROR, progname, @path, &block
|
24
|
+
end
|
25
|
+
|
26
|
+
def fatal(progname = nil, &block)
|
27
|
+
Prefab::LoggerClient.instance.log_internal ::Logger::FATAL, progname, @path, &block
|
12
28
|
end
|
13
29
|
end
|
14
30
|
end
|
@@ -2,6 +2,8 @@ require 'yaml'
|
|
2
2
|
|
3
3
|
module Prefab
|
4
4
|
class YAMLConfigParser
|
5
|
+
LOG = Prefab::InternalLogger.new(YAMLConfigParser)
|
6
|
+
|
5
7
|
def initialize(file, client)
|
6
8
|
@file = file
|
7
9
|
@client = client
|
@@ -21,10 +23,10 @@ module Prefab
|
|
21
23
|
|
22
24
|
def load
|
23
25
|
if File.exist?(@file)
|
24
|
-
|
26
|
+
LOG.info "Load #{@file}"
|
25
27
|
YAML.load_file(@file)
|
26
28
|
else
|
27
|
-
|
29
|
+
LOG.info "No file #{@file}"
|
28
30
|
{}
|
29
31
|
end
|
30
32
|
end
|
data/lib/prefab-cloud-ruby.rb
CHANGED
data/prefab-cloud-ruby.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: prefab-cloud-ruby 1.
|
5
|
+
# stub: prefab-cloud-ruby 1.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "prefab-cloud-ruby".freeze
|
9
|
-
s.version = "1.
|
9
|
+
s.version = "1.2.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Jeff Dwyer".freeze]
|
14
|
-
s.date = "2023-10-
|
14
|
+
s.date = "2023-10-30"
|
15
15
|
s.description = "Feature Flags, Live Config, and Dynamic Log Levels as a service".freeze
|
16
16
|
s.email = "jdwyer@prefab.cloud".freeze
|
17
17
|
s.executables = ["console".freeze]
|
@@ -65,6 +65,7 @@ Gem::Specification.new do |s|
|
|
65
65
|
"lib/prefab/murmer3.rb",
|
66
66
|
"lib/prefab/options.rb",
|
67
67
|
"lib/prefab/periodic_sync.rb",
|
68
|
+
"lib/prefab/prefab.rb",
|
68
69
|
"lib/prefab/rate_limit_cache.rb",
|
69
70
|
"lib/prefab/resolved_config_presenter.rb",
|
70
71
|
"lib/prefab/sse_logger.rb",
|
@@ -100,7 +101,9 @@ Gem::Specification.new do |s|
|
|
100
101
|
"test/test_local_config_parser.rb",
|
101
102
|
"test/test_log_path_aggregator.rb",
|
102
103
|
"test/test_logger.rb",
|
104
|
+
"test/test_logger_initialization.rb",
|
103
105
|
"test/test_options.rb",
|
106
|
+
"test/test_prefab.rb",
|
104
107
|
"test/test_rate_limit_cache.rb",
|
105
108
|
"test/test_weighted_value_resolver.rb"
|
106
109
|
]
|
@@ -44,24 +44,26 @@ module CommonHelpers
|
|
44
44
|
}.freeze
|
45
45
|
|
46
46
|
def new_client(overrides = {})
|
47
|
-
$logs ||= StringIO.new
|
48
47
|
|
49
48
|
config = overrides.delete(:config)
|
50
49
|
project_env_id = overrides.delete(:project_env_id)
|
51
50
|
|
52
|
-
|
53
|
-
**DEFAULT_NEW_CLIENT_OPTIONS.merge(
|
54
|
-
overrides.merge(logdev: $logs)
|
55
|
-
)
|
56
|
-
)
|
57
|
-
|
58
|
-
Prefab::Client.new(options).tap do |client|
|
51
|
+
Prefab::Client.new(prefab_options(overrides)).tap do |client|
|
59
52
|
inject_config(client, config) if config
|
60
53
|
|
61
54
|
client.resolver.project_env_id = project_env_id if project_env_id
|
62
55
|
end
|
63
56
|
end
|
64
57
|
|
58
|
+
def prefab_options(overrides = {})
|
59
|
+
$logs ||= StringIO.new
|
60
|
+
Prefab::Options.new(
|
61
|
+
**DEFAULT_NEW_CLIENT_OPTIONS.merge(
|
62
|
+
overrides.merge(logdev: $logs)
|
63
|
+
)
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
65
67
|
def string_list(values)
|
66
68
|
PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: values))
|
67
69
|
end
|
@@ -145,7 +147,7 @@ module CommonHelpers
|
|
145
147
|
end
|
146
148
|
|
147
149
|
def assert_only_expected_logs
|
148
|
-
assert_equal "WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints\n", $logs.string
|
150
|
+
assert_equal "WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client.configclient No success loading checkpoints\n", $logs.string
|
149
151
|
# mark nil to indicate we handled it
|
150
152
|
$logs = nil
|
151
153
|
end
|
@@ -9,8 +9,8 @@ class MockBaseClient
|
|
9
9
|
def initialize(options = Prefab::Options.new)
|
10
10
|
@options = options
|
11
11
|
@namespace = namespace
|
12
|
-
@logger = Prefab::LoggerClient.new($stdout)
|
13
12
|
@config_client = MockConfigClient.new
|
13
|
+
Prefab::LoggerClient.new(options.logdev)
|
14
14
|
@posts = []
|
15
15
|
end
|
16
16
|
|
@@ -30,8 +30,6 @@ class MockBaseClient
|
|
30
30
|
@logger
|
31
31
|
end
|
32
32
|
|
33
|
-
def log_internal(level, msg, path = nil, **tags); end
|
34
|
-
|
35
33
|
def context_shape_aggregator; end
|
36
34
|
|
37
35
|
def evaluation_summary_aggregator; end
|
data/test/test_client.rb
CHANGED
@@ -185,7 +185,7 @@ class TestClient < Minitest::Test
|
|
185
185
|
collect_evaluation_summaries: true).evaluation_summary_aggregator.class
|
186
186
|
|
187
187
|
assert_logged [
|
188
|
-
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints"
|
188
|
+
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client.configclient No success loading checkpoints"
|
189
189
|
]
|
190
190
|
end
|
191
191
|
|
@@ -395,7 +395,7 @@ class TestClient < Minitest::Test
|
|
395
395
|
values: [
|
396
396
|
PrefabProto::ConditionalValue.new(
|
397
397
|
criteria: [PrefabProto::Criterion.new(operator: PrefabProto::Criterion::CriterionOperator::ALWAYS_TRUE)],
|
398
|
-
value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::
|
398
|
+
value: PrefabProto::ConfigValue.new(log_level: PrefabProto::LogLevel::INFO)
|
399
399
|
)
|
400
400
|
]
|
401
401
|
)
|
@@ -405,12 +405,21 @@ class TestClient < Minitest::Test
|
|
405
405
|
client = new_client(config: config, project_env_id: PROJECT_ENV_ID,
|
406
406
|
collect_evaluation_summaries: true, allow_telemetry_in_local_mode: true)
|
407
407
|
|
408
|
-
assert_equal :
|
408
|
+
assert_equal :INFO, client.get(config.key, IRRELEVANT)
|
409
409
|
|
410
410
|
# nothing is summarized for log levels
|
411
411
|
assert_summary client, {}
|
412
412
|
end
|
413
413
|
|
414
|
+
def test_fork_includes_logger_context_keys
|
415
|
+
client = new_client
|
416
|
+
client.log.add_context_keys "user.name"
|
417
|
+
|
418
|
+
forked = client.fork
|
419
|
+
|
420
|
+
assert forked.log.context_keys.to_a == %w(user.name)
|
421
|
+
end
|
422
|
+
|
414
423
|
private
|
415
424
|
|
416
425
|
def basic_value_config
|
@@ -124,8 +124,8 @@ class TestContextShapeAggregator < Minitest::Test
|
|
124
124
|
|
125
125
|
|
126
126
|
assert_logged [
|
127
|
-
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints",
|
128
|
-
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client Couldn't Initialize In 0. Key some.key. Returning what we have"
|
127
|
+
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client.configclient No success loading checkpoints",
|
128
|
+
"WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client.configclient Couldn't Initialize In 0. Key some.key. Returning what we have"
|
129
129
|
]
|
130
130
|
end
|
131
131
|
|
@@ -710,22 +710,11 @@ class TestCriteriaEvaluator < Minitest::Test
|
|
710
710
|
FakeResolver.new(config, @base_client)
|
711
711
|
end
|
712
712
|
|
713
|
-
class FakeLogger
|
714
|
-
def info(msg)
|
715
|
-
# loudly complain about unexpected log messages
|
716
|
-
raise msg
|
717
|
-
end
|
718
|
-
|
719
|
-
def log_internal(*args); end
|
720
|
-
end
|
721
|
-
|
722
713
|
class FakeBaseClient
|
723
|
-
def
|
724
|
-
|
714
|
+
def initialize
|
715
|
+
Prefab::LoggerClient.new($stdout)
|
725
716
|
end
|
726
717
|
|
727
|
-
def log_internal(level, msg, path = nil, **tags); end
|
728
|
-
|
729
718
|
def evaluation_summary_aggregator
|
730
719
|
@evaluation_summary_aggregator ||= Prefab::EvaluationSummaryAggregator.new(client: self, max_keys: 9999, sync_interval: 9999)
|
731
720
|
end
|
@@ -47,7 +47,7 @@ class TestLogPathAggregator < Minitest::Test
|
|
47
47
|
]], requests
|
48
48
|
|
49
49
|
assert_logged [
|
50
|
-
'WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client No success loading checkpoints',
|
50
|
+
'WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client.configclient No success loading checkpoints',
|
51
51
|
'ERROR 2023-08-09 15:18:12 -0400: test.test_log_path_aggregator.test_sync here is a message'
|
52
52
|
]
|
53
53
|
end
|
data/test/test_logger.rb
CHANGED
@@ -87,22 +87,22 @@ class TestLogger < Minitest::Test
|
|
87
87
|
|
88
88
|
def test_log_internal
|
89
89
|
prefab, io = captured_logger
|
90
|
-
prefab.log.log_internal('test message', 'test.path'
|
90
|
+
prefab.log.log_internal(::Logger::WARN, 'test message', 'test.path')
|
91
91
|
assert_logged io, 'WARN', "cloud.prefab.client.test.path", "test message"
|
92
92
|
end
|
93
93
|
|
94
94
|
def test_log_internal_unknown
|
95
95
|
prefab, io = captured_logger
|
96
|
-
prefab.log.log_internal('test message', 'test.path'
|
96
|
+
prefab.log.log_internal(::Logger::UNKNOWN, 'test message', 'test.path')
|
97
97
|
assert_logged io, 'ANY', "cloud.prefab.client.test.path", "test message"
|
98
98
|
end
|
99
99
|
|
100
100
|
def test_log_internal_silencing
|
101
101
|
prefab, io = captured_logger
|
102
102
|
prefab.log.silence do
|
103
|
-
prefab.log.log_internal('should not log', 'test.path'
|
103
|
+
prefab.log.log_internal(::Logger::WARN, 'should not log', 'test.path')
|
104
104
|
end
|
105
|
-
prefab.log.log_internal('should log', 'test.path'
|
105
|
+
prefab.log.log_internal(::Logger::WARN, 'should log', 'test.path')
|
106
106
|
assert_logged io, 'WARN', "cloud.prefab.client.test.path", "should log"
|
107
107
|
refute_logged io, 'should not log'
|
108
108
|
end
|
@@ -404,6 +404,35 @@ class TestLogger < Minitest::Test
|
|
404
404
|
assert_logged io, 'ERROR', 'test.test_logger.test_logging_with_a_block', message
|
405
405
|
end
|
406
406
|
|
407
|
+
def test_add_context_keys
|
408
|
+
assert @logger.context_keys.empty?
|
409
|
+
@logger.add_context_keys("user.name", "role.admin", "company.name")
|
410
|
+
|
411
|
+
assert @logger.context_keys.to_a == %w(user.name role.admin company.name)
|
412
|
+
end
|
413
|
+
|
414
|
+
def test_context_keys_are_a_set
|
415
|
+
@logger.add_context_keys("user.name", "role.admin", "company.name")
|
416
|
+
|
417
|
+
assert @logger.context_keys.to_a == %w(user.name role.admin company.name)
|
418
|
+
|
419
|
+
@logger.add_context_keys("user.name", "user.role")
|
420
|
+
|
421
|
+
assert @logger.context_keys.to_a == %w(user.name role.admin company.name user.role)
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_with_context_keys
|
425
|
+
@logger.add_context_keys("company.name")
|
426
|
+
|
427
|
+
assert @logger.context_keys.to_a == %w(company.name)
|
428
|
+
|
429
|
+
@logger.with_context_keys("user.name", "role.admin") do
|
430
|
+
assert @logger.context_keys.to_a == %w(company.name user.name role.admin)
|
431
|
+
end
|
432
|
+
|
433
|
+
assert @logger.context_keys.to_a == %w(company.name)
|
434
|
+
end
|
435
|
+
|
407
436
|
def test_structured_logging
|
408
437
|
prefab, io = captured_logger
|
409
438
|
message = 'HELLO'
|
@@ -428,7 +457,7 @@ class TestLogger < Minitest::Test
|
|
428
457
|
def test_structured_internal_logging
|
429
458
|
prefab, io = captured_logger
|
430
459
|
|
431
|
-
prefab.log.log_internal('test', 'test.path',
|
460
|
+
prefab.log.log_internal(::Logger::WARN, 'test', 'test.path', user: "michael")
|
432
461
|
|
433
462
|
assert_logged io, 'WARN', 'cloud.prefab.client.test.path', "test user=michael"
|
434
463
|
end
|
@@ -444,6 +473,75 @@ class TestLogger < Minitest::Test
|
|
444
473
|
assert_logged io, 'ERROR', 'test.test_logger.test_structured_block_logger', "#{message} user=michael"
|
445
474
|
end
|
446
475
|
|
476
|
+
def test_structured_logger_with_context_keys
|
477
|
+
prefab, io = captured_logger
|
478
|
+
|
479
|
+
prefab.with_context({user: {name: "michael", job: "developer", admin: false}, company: { name: "Prefab" }}) do
|
480
|
+
|
481
|
+
prefab.log.add_context_keys "user.name", "company.name", "user.admin"
|
482
|
+
|
483
|
+
prefab.log.error "UH OH"
|
484
|
+
|
485
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_logger_with_context_keys',
|
486
|
+
"UH OH company.name=Prefab user.admin=false user.name=michael"
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def test_structured_logger_with_context_keys_ignores_nils
|
491
|
+
prefab, io = captured_logger
|
492
|
+
|
493
|
+
prefab.with_context({user: {name: "michael", job: "developer"}, company: { name: "Prefab" }}) do
|
494
|
+
|
495
|
+
prefab.log.add_context_keys "user.name", "company.name", "user.admin"
|
496
|
+
|
497
|
+
prefab.log.error "UH OH"
|
498
|
+
|
499
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_logger_with_context_keys_ignores_nils',
|
500
|
+
"UH OH company.name=Prefab user.name=michael"
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
def test_structured_logger_with_context_keys_and_log_hash
|
505
|
+
prefab, io = captured_logger
|
506
|
+
|
507
|
+
prefab.with_context({user: {name: "michael", job: "developer", admin: false}, company: { name: "Prefab" }}) do
|
508
|
+
|
509
|
+
prefab.log.add_context_keys "user.name", "company.name", "user.admin"
|
510
|
+
|
511
|
+
prefab.log.error "UH OH", user_id: 6
|
512
|
+
|
513
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_logger_with_context_keys_and_log_hash',
|
514
|
+
"UH OH company.name=Prefab user.admin=false user.name=michael user_id=6"
|
515
|
+
end
|
516
|
+
|
517
|
+
end
|
518
|
+
|
519
|
+
def test_structured_logger_with_context_keys_block
|
520
|
+
prefab, io = captured_logger
|
521
|
+
|
522
|
+
prefab.with_context({user: {name: "michael", job: "developer", admin: false}, company: { name: "Prefab" }}) do
|
523
|
+
|
524
|
+
prefab.log.add_context_keys "user.name"
|
525
|
+
|
526
|
+
prefab.log.error "UH OH"
|
527
|
+
|
528
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_logger_with_context_keys_block',
|
529
|
+
'UH OH user.name=michael'
|
530
|
+
|
531
|
+
prefab.log.with_context_keys("company.name") do
|
532
|
+
prefab.log.error "UH OH"
|
533
|
+
|
534
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_logger_with_context_keys_block',
|
535
|
+
'UH OH company.name=Prefab user.name=michael'
|
536
|
+
end
|
537
|
+
|
538
|
+
prefab.log.error "UH OH"
|
539
|
+
|
540
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_logger_with_context_keys_block',
|
541
|
+
'UH OH user.name=michael'
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
447
545
|
private
|
448
546
|
|
449
547
|
def assert_logged(logged_io, level, path, message)
|
data/test/test_prefab.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class TestPrefab < Minitest::Test
|
6
|
+
def test_get
|
7
|
+
Prefab.init(prefab_options)
|
8
|
+
assert_equal 'default', Prefab.get('does.not.exist', 'default')
|
9
|
+
assert_equal 'test sample value', Prefab.get('sample')
|
10
|
+
assert_equal 123, Prefab.get('sample_int')
|
11
|
+
end
|
12
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prefab-cloud-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Dwyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -224,6 +224,7 @@ files:
|
|
224
224
|
- lib/prefab/murmer3.rb
|
225
225
|
- lib/prefab/options.rb
|
226
226
|
- lib/prefab/periodic_sync.rb
|
227
|
+
- lib/prefab/prefab.rb
|
227
228
|
- lib/prefab/rate_limit_cache.rb
|
228
229
|
- lib/prefab/resolved_config_presenter.rb
|
229
230
|
- lib/prefab/sse_logger.rb
|
@@ -259,7 +260,9 @@ files:
|
|
259
260
|
- test/test_local_config_parser.rb
|
260
261
|
- test/test_log_path_aggregator.rb
|
261
262
|
- test/test_logger.rb
|
263
|
+
- test/test_logger_initialization.rb
|
262
264
|
- test/test_options.rb
|
265
|
+
- test/test_prefab.rb
|
263
266
|
- test/test_rate_limit_cache.rb
|
264
267
|
- test/test_weighted_value_resolver.rb
|
265
268
|
homepage: http://github.com/prefab-cloud/prefab-cloud-ruby
|