prefab-cloud-ruby 1.5.1 → 1.6.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -2
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +3 -0
  5. data/README.md +24 -15
  6. data/VERSION +1 -1
  7. data/lib/prefab/client.rb +2 -7
  8. data/lib/prefab/config_client.rb +12 -16
  9. data/lib/prefab/config_loader.rb +1 -1
  10. data/lib/prefab/config_value_unwrapper.rb +1 -1
  11. data/lib/prefab/context_shape_aggregator.rb +1 -2
  12. data/lib/prefab/criteria_evaluator.rb +3 -3
  13. data/lib/prefab/evaluation_summary_aggregator.rb +1 -2
  14. data/lib/prefab/example_contexts_aggregator.rb +1 -2
  15. data/lib/prefab/feature_flag_client.rb +2 -1
  16. data/lib/prefab/internal_logger.rb +36 -10
  17. data/lib/prefab/log_path_aggregator.rb +1 -2
  18. data/lib/prefab/logger_client.rb +34 -213
  19. data/lib/prefab/options.rb +0 -44
  20. data/lib/prefab/periodic_sync.rb +2 -1
  21. data/lib/prefab/prefab.rb +23 -1
  22. data/lib/prefab/yaml_config_parser.rb +1 -1
  23. data/lib/prefab-cloud-ruby.rb +2 -5
  24. data/prefab-cloud-ruby.gemspec +6 -8
  25. data/test/support/common_helpers.rb +14 -13
  26. data/test/support/mock_base_client.rb +0 -1
  27. data/test/test_client.rb +1 -10
  28. data/test/test_config_client.rb +1 -2
  29. data/test/test_context_shape_aggregator.rb +2 -5
  30. data/test/test_criteria_evaluator.rb +0 -4
  31. data/test/test_integration.rb +1 -1
  32. data/test/test_internal_logger.rb +25 -0
  33. data/test/test_log_path_aggregator.rb +5 -10
  34. data/test/test_logger.rb +57 -453
  35. data/test/test_logger_initialization.rb +1 -1
  36. metadata +18 -8
  37. data/lib/prefab/log_subscribers/action_controller_subscriber.rb +0 -55
  38. data/lib/prefab/logging/formatter_base.rb +0 -21
  39. data/lib/prefab/sse_logger.rb +0 -14
  40. data/lib/prefab/static_logger.rb +0 -29
  41. data/test/test_action_controller.rb +0 -81
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e17e7f322495febf8d3039c415dbca2efb862e91387d6836b8097efcf60c83ba
4
- data.tar.gz: b4fc3d100d9429bcc6b82898e036f3a357513c23d6499ab0b92e7c474b64205a
3
+ metadata.gz: 216fce2170e0fc185a3a79d2f9b7ffa4830e966fc203dc97bcb9b69aa4d79fd0
4
+ data.tar.gz: c2e4ac474cd6b83b33042a9a0cc3ef95d3f96b3f2c3044884831215d4f3af418
5
5
  SHA512:
6
- metadata.gz: d2963c32a38aa86440c94b025102d1e08a6043bba1b4253a015ce15d04432fedba7c19076a7d865d1ad26b561c88ddc1d78ed8884c950f7844a56a3d9845d631
7
- data.tar.gz: 145759236becc8a6fe6e7f949a44ed2192f460f946ebcdb7367ac7f6fa25cd5cf9038872dfc31e5a21c15298e9de38115df3b2673ca8bf6f2466567a27de669a
6
+ metadata.gz: be331123d75a902d6a48a4fa0fb59eb3dbf74254d9671ab0f3b8d3507a6ac2958815e014494288c9e61c2469110dbfc8c374be7d958e3e7dd95142b7ced01cd1
7
+ data.tar.gz: c73394980ed15e9edf8e3b513bca62922bded72aacc3efc75f2a1915701c89c75aad7c70c534cc350c68bf25bfe77b123a819ea39e0bfc589d45eeca0bf9d209
data/CHANGELOG.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Changelog
2
2
 
3
- ## 1.5.1 - 2024-02-22
3
+ ## Unreleased
4
4
 
5
- - Fix: Send context shapes by default (#174)
5
+ - Use semantic_logger for internal logging (#173)
6
+ - Remove Prefab::LoggerClient as a logger for end users (#173)
7
+ - Provide log_filter for end users (#173)
6
8
 
7
9
  ## 1.5.0 - 2024-02-12
8
10
 
data/Gemfile CHANGED
@@ -10,6 +10,8 @@ gem 'uuid'
10
10
  gem 'activesupport', '>= 4'
11
11
  gem 'actionpack', '>= 4'
12
12
 
13
+ gem 'semantic_logger'
14
+
13
15
  group :development do
14
16
  gem 'benchmark-ips'
15
17
  gem 'bundler'
data/Gemfile.lock CHANGED
@@ -146,6 +146,8 @@ GEM
146
146
  rdoc (6.3.3)
147
147
  ruby-progressbar (1.11.0)
148
148
  ruby2_keywords (0.0.4)
149
+ semantic_logger (4.15.0)
150
+ concurrent-ruby (~> 1.0)
149
151
  semver2 (3.4.2)
150
152
  simplecov (0.18.5)
151
153
  docile (~> 1.1)
@@ -180,6 +182,7 @@ DEPENDENCIES
180
182
  minitest-focus
181
183
  minitest-reporters
182
184
  rdoc
185
+ semantic_logger
183
186
  simplecov
184
187
  timecop
185
188
  uuid
data/README.md CHANGED
@@ -33,40 +33,49 @@ See full documentation https://docs.prefab.cloud/docs/ruby-sdk/ruby
33
33
  Many ruby web servers fork. When the process is forked, the current realtime update stream is disconnected. If you're using Puma or Unicorn, do the following.
34
34
 
35
35
  ```ruby
36
- #config/initializers/prefab.rb
37
- $prefab = Prefab::Client.new
38
- $prefab.set_rails_loggers
36
+ #config/application.rb
37
+ Prefab.init # reads PREFAB_API_KEY env var
39
38
  ```
40
39
 
41
40
  ```ruby
42
41
  #puma.rb
43
42
  on_worker_boot do
44
- $prefab = $prefab.fork
45
- $prefab.set_rails_loggers
43
+ Prefab.fork
46
44
  end
47
45
  ```
48
46
 
49
47
  ```ruby
50
48
  # unicorn.rb
51
49
  after_fork do |server, worker|
52
- $prefab = $prefab.fork
53
- $prefab.set_rails_loggers
50
+ Prefab.fork
54
51
  end
55
52
  ```
56
53
 
57
54
  ## Logging & Debugging
58
55
 
59
- In classpath or ~/.prefab.default.config.yaml set
56
+ To use dynamic logging. Install https://logger.rocketjob.io/rails.html and then add Prefab as a dynamic filter.
60
57
 
61
58
  ```
62
- log-level:
63
- cloud.prefab: debug
59
+ gem "amazing_print"
60
+ gem "rails_semantic_logger"
64
61
  ```
62
+ ```ruby
63
+ #application.rb
64
+ SemanticLogger.default_level = :trace # Prefab will take over the filtering
65
+ SemanticLogger.add_appender(
66
+ io: $stdout,
67
+ formatter: Rails.env.development? ? :default : :json,
68
+ filter: Prefab.log_filter,
69
+ )
70
+ Prefab.init
71
+ ````
65
72
 
66
- To debug issues before this config file has been read, set env var
67
-
68
- ```
69
- PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL=debug
73
+ ```ruby
74
+ #puma.rb
75
+ on_worker_boot do
76
+ SemanticLogger.reopen
77
+ Prefab.fork
78
+ end
70
79
  ```
71
80
 
72
81
  ## Contributing to prefab-cloud-ruby
@@ -91,4 +100,4 @@ REMOTE_BRANCH=main LOCAL_BRANCH=main bundle exec rake release
91
100
 
92
101
  ## Copyright
93
102
 
94
- Copyright (c) 2023 Jeff Dwyer. See LICENSE.txt for further details.
103
+ Copyright (c) 2024 Prefab, Inc. See LICENSE.txt for further details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.1
1
+ 1.6.0.pre1
data/lib/prefab/client.rb CHANGED
@@ -4,9 +4,9 @@ require 'uuid'
4
4
 
5
5
  module Prefab
6
6
  class Client
7
+ LOG = Prefab::InternalLogger.new(self)
7
8
  MAX_SLEEP_SEC = 10
8
9
  BASE_SLEEP_SEC = 0.5
9
- LOG = Prefab::InternalLogger.new(Client)
10
10
 
11
11
  attr_reader :namespace, :interceptor, :api_key, :prefab_api_url, :options, :instance_hash
12
12
 
@@ -15,10 +15,6 @@ module Prefab
15
15
  @namespace = @options.namespace
16
16
  @stubs = {}
17
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
- )
22
18
 
23
19
  if @options.local_only?
24
20
  LOG.debug 'Prefab Running in Local Mode'
@@ -55,13 +51,12 @@ module Prefab
55
51
 
56
52
  def log_path_aggregator
57
53
  return nil if @options.collect_max_paths <= 0
58
-
59
54
  @log_path_aggregator ||= LogPathAggregator.new(client: self, max_paths: @options.collect_max_paths,
60
55
  sync_interval: @options.collect_sync_interval)
61
56
  end
62
57
 
63
58
  def log
64
- Prefab::LoggerClient.instance
59
+ @log ||= Prefab::LoggerClient.new(client: self, log_path_aggregator: log_path_aggregator)
65
60
  end
66
61
 
67
62
  def context_shape_aggregator
@@ -2,14 +2,12 @@
2
2
 
3
3
  module Prefab
4
4
  class ConfigClient
5
+ LOG = Prefab::InternalLogger.new(self)
5
6
  RECONNECT_WAIT = 5
6
7
  DEFAULT_CHECKPOINT_FREQ_SEC = 60
7
8
  SSE_READ_TIMEOUT = 300
8
9
  STALE_CACHE_WARN_HOURS = 5
9
10
  AUTH_USER = 'authuser'
10
- LOGGING_KEY_PREFIX = "#{Prefab::LoggerClient::BASE_KEY}#{Prefab::LoggerClient::SEP}".freeze
11
- LOG = Prefab::InternalLogger.new(ConfigClient)
12
-
13
11
  def initialize(base_client, timeout)
14
12
  @base_client = base_client
15
13
  @options = base_client.options
@@ -23,11 +21,7 @@ module Prefab
23
21
  @config_loader = Prefab::ConfigLoader.new(@base_client)
24
22
  @config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
25
23
 
26
- @initialization_lock = Concurrent::ReadWriteLock.new
27
- LOG.debug 'Initialize ConfigClient: AcquireWriteLock'
28
- @initialization_lock.acquire_write_lock
29
- LOG.debug 'Initialize ConfigClient: AcquiredWriteLock'
30
- @initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
24
+ @initialization_lock = Concurrent::CountDownLatch.new(1)
31
25
 
32
26
  if @options.local_only?
33
27
  finish_init!(:local_only, nil)
@@ -77,6 +71,10 @@ module Prefab
77
71
  end
78
72
  end
79
73
 
74
+ def initialized?
75
+ @initialization_lock.count <= 0
76
+ end
77
+
80
78
  private
81
79
 
82
80
  def raw(key)
@@ -93,14 +91,13 @@ module Prefab
93
91
 
94
92
  def _get(key, properties)
95
93
  # wait timeout sec for the initialization to be complete
96
- @initialized_future.value(@options.initialization_timeout_sec)
97
- if @initialized_future.incomplete?
94
+ success = @initialization_lock.wait(@options.initialization_timeout_sec)
95
+ if !success
98
96
  unless @options.on_init_failure == Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN
99
97
  raise Prefab::Errors::InitializationTimeoutError.new(@options.initialization_timeout_sec, key)
100
98
  end
101
99
 
102
100
  LOG.warn("Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have")
103
- @initialization_lock.release_write_lock
104
101
  end
105
102
 
106
103
  @config_resolver.get key, properties
@@ -144,7 +141,7 @@ module Prefab
144
141
  false
145
142
  end
146
143
  rescue Faraday::ConnectionFailed => e
147
- if @initialization_lock.write_locked?
144
+ if !initialized?
148
145
  LOG.warn "Connection Fail loading #{source} checkpoint."
149
146
  else
150
147
  LOG.debug "Connection Fail loading #{source} checkpoint."
@@ -251,12 +248,11 @@ module Prefab
251
248
  end
252
249
 
253
250
  def finish_init!(source, project_id)
254
- return unless @initialization_lock.write_locked?
251
+ return if initialized?
255
252
 
256
253
  LOG.debug "Unlocked Config via #{source}"
257
- @initialization_lock.release_write_lock
254
+ @initialization_lock.count_down
258
255
 
259
- Prefab::LoggerClient.instance.config_client = self
260
256
  presenter = Prefab::ConfigClientPresenter.new(
261
257
  size: @config_resolver.local_store.size,
262
258
  source: source,
@@ -281,7 +277,7 @@ module Prefab
281
277
  @streaming_thread = SSE::Client.new(url,
282
278
  headers: headers,
283
279
  read_timeout: SSE_READ_TIMEOUT,
284
- logger: Prefab::SseLogger.new) do |client|
280
+ logger: Prefab::InternalLogger.new(SSE::Client)) do |client|
285
281
  client.on_event do |event|
286
282
  configs = PrefabProto::Configs.decode(Base64.decode64(event.data))
287
283
  load_configs(configs, :sse)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Prefab
4
4
  class ConfigLoader
5
- LOG = Prefab::InternalLogger.new(ConfigLoader)
5
+ LOG = Prefab::InternalLogger.new(self)
6
6
 
7
7
  attr_reader :highwater_mark
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Prefab
4
4
  class ConfigValueUnwrapper
5
- LOG = Prefab::InternalLogger.new(ConfigValueUnwrapper)
5
+ LOG = Prefab::InternalLogger.new(self)
6
6
  CONFIDENTIAL_PREFIX = "*****"
7
7
  attr_reader :weighted_value_index
8
8
 
@@ -5,8 +5,7 @@ require_relative 'periodic_sync'
5
5
  module Prefab
6
6
  class ContextShapeAggregator
7
7
  include Prefab::PeriodicSync
8
-
9
- LOG = Prefab::InternalLogger.new(ContextShapeAggregator)
8
+ LOG = Prefab::InternalLogger.new(self)
10
9
 
11
10
  attr_reader :data
12
11
 
@@ -7,7 +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
+ LOG = Prefab::InternalLogger.new(self)
11
11
  NAMESPACE_KEY = 'NAMESPACE'
12
12
  NO_MATCHING_ROWS = [].freeze
13
13
 
@@ -22,7 +22,7 @@ module Prefab
22
22
  def evaluate(properties)
23
23
  rtn = evaluate_for_env(@project_env_id, properties) ||
24
24
  evaluate_for_env(0, properties)
25
- LOG.debug "Eval Key #{@config.key} Result #{rtn&.reportable_value} with #{properties.to_h}" unless @config.config_type == :LOG_LEVEL
25
+ LOG.trace "Eval Key #{@config.key} Result #{rtn&.reportable_value} with #{properties.to_h}" unless @config.config_type == :LOG_LEVEL
26
26
  rtn
27
27
  end
28
28
 
@@ -105,7 +105,7 @@ module Prefab
105
105
  def in_segment?(criterion, properties)
106
106
  segment = @resolver.get(criterion.value_to_match.string, properties)
107
107
 
108
- @base_client.log.info("Segment #{criterion.value_to_match.string} not found") unless segment
108
+ LOG.info("Segment #{criterion.value_to_match.string} not found") unless segment
109
109
 
110
110
  segment&.report_and_return(@base_client.evaluation_summary_aggregator)
111
111
  end
@@ -8,8 +8,7 @@ module Prefab
8
8
  # server at a regular interval defined by `sync_interval`.
9
9
  class EvaluationSummaryAggregator
10
10
  include Prefab::PeriodicSync
11
-
12
- LOG = Prefab::InternalLogger.new(EvaluationSummaryAggregator)
11
+ LOG = Prefab::InternalLogger.new(self)
13
12
 
14
13
  attr_reader :data
15
14
 
@@ -9,8 +9,7 @@ module Prefab
9
9
  # It shouldn't send the same context more than once per hour.
10
10
  class ExampleContextsAggregator
11
11
  include Prefab::PeriodicSync
12
-
13
- LOG = Prefab::InternalLogger.new(ExampleContextsAggregator)
12
+ LOG = Prefab::InternalLogger.new(self)
14
13
 
15
14
  attr_reader :data, :cache
16
15
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Prefab
4
4
  class FeatureFlagClient
5
+ LOG = Prefab::InternalLogger.new(self)
5
6
  def initialize(base_client)
6
7
  @base_client = base_client
7
8
  end
@@ -35,7 +36,7 @@ module Prefab
35
36
 
36
37
  variant.bool
37
38
  rescue StandardError
38
- @base_client.log.info("is_on? methods only work for boolean feature flags variants. This feature flags variant is '#{variant}'. Returning false")
39
+ LOG.info("is_on? methods only work for boolean feature flags variants. This feature flags variant is '#{variant}'. Returning false")
39
40
  false
40
41
  end
41
42
  end
@@ -1,16 +1,42 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Prefab
4
- class InternalLogger < StaticLogger
5
- INTERNAL_PREFIX = 'cloud.prefab.client'
2
+ class InternalLogger < SemanticLogger::Logger
3
+
4
+ def initialize(klass)
5
+ super(klass, :warn)
6
+ instances << self
7
+ end
6
8
 
7
- def initialize(path)
8
- if path.is_a?(Class)
9
- path_string = path.name.split('::').last.downcase
10
- else
11
- path_string = path
9
+ def log(log, message = nil, progname = nil, &block)
10
+ return if recurse_check[local_log_id]
11
+ recurse_check[local_log_id] = true
12
+ begin
13
+ super(log, message, progname, &block)
14
+ ensure
15
+ recurse_check[local_log_id] = false
12
16
  end
13
- super("#{INTERNAL_PREFIX}.#{path_string}")
17
+ end
18
+
19
+ def local_log_id
20
+ Thread.current.__id__
21
+ end
22
+
23
+ # Our client outputs debug logging,
24
+ # but if you aren't using Prefab logging this could be too chatty.
25
+ # If you aren't using prefab log filter, only log warn level and above
26
+ def self.using_prefab_log_filter!
27
+ @@instances.each do |l|
28
+ l.level = :trace
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def instances
35
+ @@instances ||= []
36
+ end
37
+
38
+ def recurse_check
39
+ @recurse_check ||=Concurrent::Map.new(initial_capacity: 2)
14
40
  end
15
41
  end
16
42
  end
@@ -4,8 +4,7 @@ require_relative 'periodic_sync'
4
4
 
5
5
  module Prefab
6
6
  class LogPathAggregator
7
- LOG = Prefab::InternalLogger.new(LogPathAggregator)
8
-
7
+ LOG = Prefab::InternalLogger.new(self)
9
8
  include Prefab::PeriodicSync
10
9
 
11
10
  INCREMENT = ->(count) { (count || 0) + 1 }