prefab-cloud-ruby 0.21.0 → 0.23.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a34b06515d401b7e73fd8b6adf77294187d94ea3a1d1909d159ebfd63b4046ad
4
- data.tar.gz: 6ba449742b1a75517b35a47151452dbdf94c0a8aef0e58a639ae5b2481262d43
3
+ metadata.gz: 2cd914702e6efb141278bbf12bf120342c0de3d788e8e6399a3baefd8f11a102
4
+ data.tar.gz: d7ba7ad4210bbf2f89005479e970f33b6486a67fbef125502c86455830381797
5
5
  SHA512:
6
- metadata.gz: 0a32e0ac965c54d634c579de66927d2982cca26d7098daaf3cdfd2dda88bf04bd33265c1e9f85e303d75fc9360f6df202cee256e59a91012b94f0adcf29252aa
7
- data.tar.gz: 2c37b2cfeef43f44d2dcbe8835ff8ea130393ad98dd9dd61328371972bf1818ab12d932f6bec319a52ec2423f9204762c6305ab3643023a3aa5ea4bfd18974bf
6
+ metadata.gz: a797285f8e5ecffc90b5861f87af50c5aae93f6dc3ba1803b79232823ee4ff2fa4fdf4017c04712aa89fa5f607496eb99376dbc6595bcfad69e2df2ae0aa0324
7
+ data.tar.gz: 965bc69143e55822b5a53d691eb50ee5a883b50e85dfe3ecf868ab58b947efc717b752d287d636636846418539c2ce602451b12b4a29f596a41a41be2839ef7e
@@ -22,7 +22,7 @@ jobs:
22
22
  runs-on: ubuntu-latest
23
23
  strategy:
24
24
  matrix:
25
- ruby-version: ['2.6', '2.7', '3.0']
25
+ ruby-version: ['2.6', '2.7', '3.0', '3.1']
26
26
 
27
27
  steps:
28
28
  - uses: actions/checkout@v3
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gem 'googleapis-common-protos-types', platforms: :ruby
6
6
  gem 'google-protobuf', platforms: :ruby
7
7
  gem 'grpc', platforms: :ruby
8
8
  gem 'ld-eventsource'
9
+ gem 'uuid'
9
10
 
10
11
  group :development do
11
12
  gem 'benchmark-ips'
@@ -20,4 +21,5 @@ group :test do
20
21
  gem 'minitest'
21
22
  gem 'minitest-focus'
22
23
  gem 'minitest-reporters'
24
+ gem 'timecop'
23
25
  end
data/Gemfile.lock CHANGED
@@ -68,6 +68,8 @@ GEM
68
68
  llhttp-ffi (0.3.1)
69
69
  ffi-compiler (~> 1.0)
70
70
  rake (~> 13.0)
71
+ macaddr (1.7.2)
72
+ systemu (~> 2.6.5)
71
73
  mini_portile2 (2.8.0)
72
74
  minitest (5.16.2)
73
75
  minitest-focus (1.3.1)
@@ -92,7 +94,7 @@ GEM
92
94
  psych (3.3.1)
93
95
  public_suffix (4.0.6)
94
96
  racc (1.6.1)
95
- rack (3.0.4.1)
97
+ rack (3.0.6.1)
96
98
  rake (13.0.6)
97
99
  rchardet (1.8.0)
98
100
  rdoc (6.3.3)
@@ -103,10 +105,14 @@ GEM
103
105
  docile (~> 1.1)
104
106
  simplecov-html (~> 0.11)
105
107
  simplecov-html (0.12.3)
108
+ systemu (2.6.5)
106
109
  thread_safe (0.3.6)
110
+ timecop (0.9.4)
107
111
  unf (0.1.4)
108
112
  unf_ext
109
113
  unf_ext (0.0.8)
114
+ uuid (2.3.9)
115
+ macaddr (~> 1.0)
110
116
 
111
117
  PLATFORMS
112
118
  ruby
@@ -127,6 +133,8 @@ DEPENDENCIES
127
133
  minitest-reporters
128
134
  rdoc
129
135
  simplecov
136
+ timecop
137
+ uuid
130
138
 
131
139
  BUNDLED WITH
132
140
  2.3.5
data/README.md CHANGED
@@ -3,18 +3,20 @@ Ruby Client for Prefab FeatureFlags, Config as a Service: https://www.prefab.clo
3
3
 
4
4
  ```ruby
5
5
  client = Prefab::Client.new
6
- @feature_flags = client.feature_flag_client
7
6
 
8
- # Create a flag that is on for 10% of traffic, the entire beta group and user:1
9
- @feature_flags.upsert(Prefab::FeatureFlag.new(feature: "MyFeature", pct: 0.1, whitelisted: ["betas", "user:1"]))
7
+ lookup_key = "user-123"
8
+ identity_attributes = {
9
+ team_id: 432,
10
+ user_id: 123,
11
+ subscription_level: 'pro',
12
+ email: "alice@example.com"
13
+ }
10
14
 
11
- # Use Flags By Themselves
12
- puts @feature_flags.feature_is_on? "MyFeature" # returns yes 10 pct of the time
15
+ result = client.enabled? "my-first-feature-flag", lookup_key, identity_attributes
13
16
 
14
- # A single user should get the same result each time
15
- puts @feature_flags.feature_is_on? "MyFeature", "user:1123"
17
+ puts "my-first-feature-flag is: #{result} for #{lookup_key}"
16
18
  ```
17
- See full documentation https://www.prefab.cloud/documentation/installation
19
+ See full documentation https://docs.prefab.cloud/docs/ruby-sdk/ruby
18
20
 
19
21
  ## Supports
20
22
 
@@ -74,5 +76,5 @@ REMOTE_BRANCH=main LOCAL_BRANCH=main bundle exec rake release
74
76
 
75
77
  ## Copyright
76
78
 
77
- Copyright (c) 2022 Jeff Dwyer. See LICENSE.txt for
79
+ Copyright (c) 2023 Jeff Dwyer. See LICENSE.txt for
78
80
  further details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.21.0
1
+ 0.23.0
@@ -13,16 +13,16 @@ module Prefab
13
13
  i = 0
14
14
  while i < WAIT_SEC
15
15
  if @call.instance_variable_get('@wrapped').cancelled?
16
- @base_client.log_internal Logger::DEBUG, 'Cancelled streaming.'
16
+ @base_client.log_internal ::Logger::DEBUG, 'Cancelled streaming.'
17
17
  return
18
18
  else
19
- @base_client.log_internal Logger::DEBUG, 'Unable to cancel streaming. Trying again'
19
+ @base_client.log_internal ::Logger::DEBUG, 'Unable to cancel streaming. Trying again'
20
20
  @call.instance_variable_get('@wrapped').instance_variable_get('@call').cancel
21
21
  i += 1
22
22
  sleep(1)
23
23
  end
24
24
  end
25
- @base_client.log_internal Logger::INFO, 'Unable to cancel streaming.'
25
+ @base_client.log_internal ::Logger::INFO, 'Unable to cancel streaming.'
26
26
  end
27
27
 
28
28
  def request_response(request:, call:, method:, metadata:, &block)
data/lib/prefab/client.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'uuid'
4
+
3
5
  module Prefab
4
6
  class Client
5
7
  MAX_SLEEP_SEC = 10
@@ -13,16 +15,18 @@ module Prefab
13
15
  attr_reader :api_key
14
16
  attr_reader :prefab_api_url
15
17
  attr_reader :options
18
+ attr_reader :instance_hash
16
19
 
17
20
  def initialize(options = Prefab::Options.new)
18
- @options = options
21
+ @options = options.is_a?(Prefab::Options) ? options : Prefab::Options.new(options)
19
22
  @shared_cache = @options.shared_cache
20
23
  @stats = @options.stats
21
24
  @namespace = @options.namespace
22
25
  @stubs = {}
26
+ @instance_hash = UUID.new.generate
23
27
 
24
28
  if @options.local_only?
25
- log_internal Logger::INFO, 'Prefab Running in Local Mode'
29
+ log_internal ::Logger::INFO, 'Prefab Running in Local Mode'
26
30
  else
27
31
  @api_key = @options.api_key
28
32
  raise Prefab::Errors::InvalidApiKeyError, @api_key if @api_key.nil? || @api_key.empty? || api_key.count('-') < 1
@@ -30,12 +34,24 @@ module Prefab
30
34
  @interceptor = Prefab::AuthInterceptor.new(@api_key)
31
35
  @prefab_api_url = @options.prefab_api_url
32
36
  @prefab_grpc_url = @options.prefab_grpc_url
33
- log_internal Logger::INFO,
37
+ log_internal ::Logger::INFO,
34
38
  "Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
35
39
  at_exit do
36
40
  channel.destroy
37
41
  end
38
42
  end
43
+ # start config client
44
+ config_client
45
+ end
46
+
47
+ def with_log_context(lookup_key, properties)
48
+ Thread.current[:prefab_log_lookup_key] = lookup_key
49
+ Thread.current[:prefab_log_properties] = properties
50
+
51
+ yield
52
+ ensure
53
+ Thread.current[:prefab_log_lookup_key] = nil
54
+ Thread.current[:prefab_log_properties] = {}
39
55
  end
40
56
 
41
57
  def channel
@@ -55,9 +71,26 @@ module Prefab
55
71
  @feature_flag_client ||= Prefab::FeatureFlagClient.new(self)
56
72
  end
57
73
 
74
+ def log_path_collector
75
+ return nil if @options.collect_max_paths <= 0
76
+
77
+ @log_path_collector ||= LogPathCollector.new(client: self, max_paths: @options.collect_max_paths,
78
+ sync_interval: @options.collect_sync_interval)
79
+ end
80
+
58
81
  def log
59
82
  @logger_client ||= Prefab::LoggerClient.new(@options.logdev, formatter: @options.log_formatter,
60
- prefix: @options.log_prefix)
83
+ prefix: @options.log_prefix,
84
+ log_path_collector: log_path_collector)
85
+ end
86
+
87
+ def set_rails_loggers
88
+ Rails.logger = log
89
+ ActionView::Base.logger = log
90
+ ActionController::Base.logger = log
91
+ ActiveJob::Base.logger = log
92
+ ActiveRecord::Base.logger = log
93
+ ActiveStorage.logger = log
61
94
  end
62
95
 
63
96
  def log_internal(level, msg, path = nil)
@@ -65,6 +98,9 @@ module Prefab
65
98
  end
66
99
 
67
100
  def request(service, method, req_options: {}, params: {})
101
+ # Future-proofing since we previously bumped into a conflict with a service with a `send` method
102
+ raise ArgumentError, 'Cannot call public_send on an grpc service in Ruby' if method.to_s == 'public_send'
103
+
68
104
  opts = { timeout: 10 }.merge(req_options)
69
105
 
70
106
  attempts = 0
@@ -72,16 +108,17 @@ module Prefab
72
108
 
73
109
  begin
74
110
  attempts += 1
75
- stub_for(service, opts[:timeout]).send(method, *params)
111
+
112
+ stub_for(service, opts[:timeout]).public_send(method, *params)
76
113
  rescue StandardError => e
77
- log_internal Logger::WARN, e
114
+ log_internal ::Logger::WARN, e
78
115
 
79
116
  raise e if Time.now - start_time > opts[:timeout]
80
117
 
81
118
  sleep_seconds = [BASE_SLEEP_SEC * (2**(attempts - 1)), MAX_SLEEP_SEC].min
82
119
  sleep_seconds *= (0.5 * (1 + rand))
83
120
  sleep_seconds = [BASE_SLEEP_SEC, sleep_seconds].max
84
- log_internal Logger::INFO, "Sleep #{sleep_seconds} and Reset #{service} #{method}"
121
+ log_internal ::Logger::INFO, "Sleep #{sleep_seconds} and Reset #{service} #{method}"
85
122
  sleep sleep_seconds
86
123
  reset!
87
124
  retry
@@ -10,7 +10,7 @@ module Prefab
10
10
  def initialize(base_client, timeout)
11
11
  @base_client = base_client
12
12
  @options = base_client.options
13
- @base_client.log_internal Logger::DEBUG, 'Initialize ConfigClient'
13
+ @base_client.log_internal ::Logger::DEBUG, 'Initialize ConfigClient'
14
14
  @timeout = timeout
15
15
 
16
16
  @stream_lock = Concurrent::ReadWriteLock.new
@@ -21,9 +21,9 @@ module Prefab
21
21
  @config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
22
22
 
23
23
  @initialization_lock = Concurrent::ReadWriteLock.new
24
- @base_client.log_internal Logger::DEBUG, 'Initialize ConfigClient: AcquireWriteLock'
24
+ @base_client.log_internal ::Logger::DEBUG, 'Initialize ConfigClient: AcquireWriteLock'
25
25
  @initialization_lock.acquire_write_lock
26
- @base_client.log_internal Logger::DEBUG, 'Initialize ConfigClient: AcquiredWriteLock'
26
+ @base_client.log_internal ::Logger::DEBUG, 'Initialize ConfigClient: AcquiredWriteLock'
27
27
  @initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
28
28
 
29
29
  @cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
@@ -74,6 +74,7 @@ module Prefab
74
74
 
75
75
  def get(key, default = Prefab::Client::NO_DEFAULT_PROVIDED, properties = {}, lookup_key = nil)
76
76
  value = _get(key, lookup_key, properties)
77
+
77
78
  value ? Prefab::ConfigValueUnwrapper.unwrap(value, key, properties) : handle_default(key, default)
78
79
  end
79
80
 
@@ -99,7 +100,7 @@ module Prefab
99
100
  raise Prefab::Errors::InitializationTimeoutError.new(@options.initialization_timeout_sec, key)
100
101
  end
101
102
 
102
- @base_client.log_internal Logger::WARN,
103
+ @base_client.log_internal ::Logger::WARN,
103
104
  "Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have"
104
105
  @initialization_lock.release_write_lock
105
106
 
@@ -119,13 +120,13 @@ module Prefab
119
120
 
120
121
  return if success
121
122
 
122
- @base_client.log_internal Logger::INFO, 'LoadCheckpoint: Fallback to GRPC API'
123
+ @base_client.log_internal ::Logger::INFO, 'LoadCheckpoint: Fallback to GRPC API'
123
124
 
124
125
  success = load_checkpoint_from_grpc_api
125
126
 
126
127
  return if success
127
128
 
128
- @base_client.log_internal Logger::WARN, 'No success loading checkpoints'
129
+ @base_client.log_internal ::Logger::WARN, 'No success loading checkpoints'
129
130
  end
130
131
 
131
132
  def load_checkpoint_from_grpc_api
@@ -135,9 +136,9 @@ module Prefab
135
136
  load_configs(resp, :remote_api_grpc)
136
137
  true
137
138
  rescue GRPC::Unauthenticated
138
- @base_client.log_internal Logger::WARN, 'Unauthenticated'
139
+ @base_client.log_internal ::Logger::WARN, 'Unauthenticated'
139
140
  rescue StandardError => e
140
- @base_client.log_internal Logger::WARN, "Unexpected grpc_api problem loading checkpoint #{e}"
141
+ @base_client.log_internal ::Logger::WARN, "Unexpected grpc_api problem loading checkpoint #{e}"
141
142
  false
142
143
  end
143
144
 
@@ -162,11 +163,11 @@ module Prefab
162
163
  load_configs(configs, source)
163
164
  true
164
165
  else
165
- @base_client.log_internal Logger::INFO, "Checkpoint #{source} failed to load. Response #{resp.status}"
166
+ @base_client.log_internal ::Logger::INFO, "Checkpoint #{source} failed to load. Response #{resp.status}"
166
167
  false
167
168
  end
168
169
  rescue StandardError => e
169
- @base_client.log_internal Logger::WARN, "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
170
+ @base_client.log_internal ::Logger::WARN, "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
170
171
  false
171
172
  end
172
173
 
@@ -180,10 +181,10 @@ module Prefab
180
181
  @config_loader.set(config, source)
181
182
  end
182
183
  if @config_loader.highwater_mark > starting_highwater_mark
183
- @base_client.log_internal Logger::INFO,
184
+ @base_client.log_internal ::Logger::INFO,
184
185
  "Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
185
186
  else
186
- @base_client.log_internal Logger::DEBUG,
187
+ @base_client.log_internal ::Logger::DEBUG,
187
188
  "Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.", 'load_configs'
188
189
  end
189
190
  @base_client.stats.increment('prefab.config.checkpoint.load')
@@ -201,7 +202,7 @@ module Prefab
201
202
  delta = @checkpoint_freq_secs - (Time.now - started_at)
202
203
  sleep(delta) if delta > 0
203
204
  rescue StandardError => e
204
- @base_client.log_internal Logger::INFO, "Issue Checkpointing #{e.message}"
205
+ @base_client.log_internal ::Logger::INFO, "Issue Checkpointing #{e.message}"
205
206
  end
206
207
  end
207
208
  end
@@ -209,10 +210,10 @@ module Prefab
209
210
  def finish_init!(source)
210
211
  return unless @initialization_lock.write_locked?
211
212
 
212
- @base_client.log_internal Logger::INFO, "Unlocked Config via #{source}"
213
+ @base_client.log_internal ::Logger::INFO, "Unlocked Config via #{source}"
213
214
  @initialization_lock.release_write_lock
214
215
  @base_client.log.set_config_client(self)
215
- @base_client.log_internal Logger::INFO, to_s
216
+ @base_client.log_internal ::Logger::INFO, to_s
216
217
  end
217
218
 
218
219
  def start_sse_streaming_connection_thread(start_at_id)
@@ -223,7 +224,7 @@ module Prefab
223
224
  "Authorization": "Basic #{auth_string}"
224
225
  }
225
226
  url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
226
- @base_client.log_internal Logger::INFO, "SSE Streaming Connect to #{url} start_at #{start_at_id}"
227
+ @base_client.log_internal ::Logger::INFO, "SSE Streaming Connect to #{url} start_at #{start_at_id}"
227
228
  @streaming_thread = SSE::Client.new(url,
228
229
  headers: headers,
229
230
  read_timeout: SSE_READ_TIMEOUT,
@@ -29,7 +29,7 @@ module Prefab
29
29
  @api_config.delete(config.key)
30
30
  else
31
31
  if @api_config[config.key]
32
- @base_client.log_internal Logger::DEBUG,
32
+ @base_client.log_internal ::Logger::DEBUG,
33
33
  "Replace #{config.key} with value from #{source} #{@api_config[config.key][:config].id} -> #{config.id}"
34
34
  end
35
35
  @api_config[config.key] = { source: source, config: config }
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prefab
4
- class InternalLogger < Logger
4
+ class InternalLogger < ::Logger
5
5
  def initialize(path, logger)
6
6
  @path = path
7
7
  @logger = logger
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prefab
4
+ class LogPathCollector
5
+ INCREMENT = ->(count) { (count || 0) + 1 }
6
+
7
+ SEVERITY_KEY = {
8
+ ::Logger::DEBUG => 'debugs',
9
+ ::Logger::INFO => 'infos',
10
+ ::Logger::WARN => 'warns',
11
+ ::Logger::ERROR => 'errors',
12
+ ::Logger::FATAL => 'fatals'
13
+ }.freeze
14
+
15
+ def initialize(client:, max_paths:, sync_interval:)
16
+ @max_paths = max_paths
17
+ @sync_interval = sync_interval
18
+ @client = client
19
+ @start_at = now
20
+
21
+ @pool = Concurrent::ThreadPoolExecutor.new(
22
+ fallback_policy: :discard,
23
+ max_queue: 5,
24
+ max_threads: 4,
25
+ min_threads: 1,
26
+ name: 'prefab-log-paths'
27
+ )
28
+
29
+ @paths = Concurrent::Map.new
30
+
31
+ start_periodic_sync
32
+ end
33
+
34
+ def push(path, severity)
35
+ return unless @paths.size < @max_paths
36
+
37
+ @paths.compute([path, severity], &INCREMENT)
38
+ end
39
+
40
+ private
41
+
42
+ def sync
43
+ return if @paths.size.zero?
44
+
45
+ log_internal "Syncing #{@paths.size} paths"
46
+
47
+ flush
48
+ end
49
+
50
+ def flush
51
+ to_ship = @paths.dup
52
+ @paths.clear
53
+
54
+ start_at_was = @start_at
55
+ @start_at = now
56
+
57
+ @pool.post do
58
+ log_internal "Uploading stats for #{to_ship.size} paths"
59
+
60
+ aggregate = Hash.new { |h, k| h[k] = Prefab::Logger.new }
61
+
62
+ to_ship.each do |(path, severity), count|
63
+ aggregate[path][SEVERITY_KEY[severity]] = count
64
+ aggregate[path]['logger_name'] = path
65
+ end
66
+
67
+ loggers = Prefab::Loggers.new(
68
+ loggers: aggregate.values,
69
+ start_at: start_at_was,
70
+ end_at: now,
71
+ instance_hash: @client.instance_hash,
72
+ namespace: @client.namespace
73
+ )
74
+
75
+ @client.request Prefab::LoggerReportingService, :send, req_options: {}, params: loggers
76
+ end
77
+ end
78
+
79
+ def start_periodic_sync
80
+ Thread.new do
81
+ log_internal "Initialized log path collector instance_hash=#{@client.instance_hash} max_paths=#{@max_paths} sync_interval=#{@sync_interval}"
82
+
83
+ loop do
84
+ sleep @sync_interval
85
+ sync
86
+ end
87
+ end
88
+ end
89
+
90
+ def log_internal(message)
91
+ @client.log.log_internal message, 'log_path_collector', nil, ::Logger::INFO
92
+ end
93
+
94
+ def now
95
+ (Time.now.utc.to_f * 1000).to_i
96
+ end
97
+ end
98
+ end
@@ -1,33 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'prefab/log_path_collector'
4
+
3
5
  module Prefab
4
- class LoggerClient < Logger
6
+ class LoggerClient < ::Logger
5
7
  SEP = '.'
6
8
  BASE_KEY = 'log-level'
7
9
  UNKNOWN_PATH = 'unknown.'
8
10
  INTERNAL_PREFIX = 'cloud.prefab.client'
9
11
 
10
12
  LOG_LEVEL_LOOKUPS = {
11
- Prefab::LogLevel::NOT_SET_LOG_LEVEL => Logger::DEBUG,
12
- Prefab::LogLevel::TRACE => Logger::DEBUG,
13
- Prefab::LogLevel::DEBUG => Logger::DEBUG,
14
- Prefab::LogLevel::INFO => Logger::INFO,
15
- Prefab::LogLevel::WARN => Logger::WARN,
16
- Prefab::LogLevel::ERROR => Logger::ERROR,
17
- Prefab::LogLevel::FATAL => Logger::FATAL
13
+ Prefab::LogLevel::NOT_SET_LOG_LEVEL => ::Logger::DEBUG,
14
+ Prefab::LogLevel::TRACE => ::Logger::DEBUG,
15
+ Prefab::LogLevel::DEBUG => ::Logger::DEBUG,
16
+ Prefab::LogLevel::INFO => ::Logger::INFO,
17
+ Prefab::LogLevel::WARN => ::Logger::WARN,
18
+ Prefab::LogLevel::ERROR => ::Logger::ERROR,
19
+ Prefab::LogLevel::FATAL => ::Logger::FATAL
18
20
  }
19
21
 
20
- def initialize(logdev, formatter: nil, prefix: nil)
22
+ def initialize(logdev, log_path_collector: nil, formatter: nil, prefix: nil)
21
23
  super(logdev)
22
24
  self.formatter = formatter
23
25
  @config_client = BootstrappingConfigClient.new
24
26
  @silences = Concurrent::Map.new(initial_capacity: 2)
25
- @prefix = prefix
27
+ @prefix = "#{prefix}#{prefix && '.'}"
28
+
29
+ @log_path_collector = log_path_collector
26
30
  end
27
31
 
28
32
  def add(severity, message = nil, progname = nil, loc, &block)
29
- path = get_loc_path(loc)
30
- path = "#{@prefix}#{@prefix && '.'}#{path}"
33
+ path_loc = get_loc_path(loc)
34
+ path = @prefix + path_loc
35
+
36
+ @log_path_collector&.push(path_loc, severity)
31
37
 
32
38
  log(message, path, progname, severity, &block)
33
39
  end
@@ -43,7 +49,8 @@ module Prefab
43
49
  end
44
50
 
45
51
  def log(message, path, progname, severity)
46
- severity ||= Logger::UNKNOWN
52
+ severity ||= ::Logger::UNKNOWN
53
+
47
54
  return true if @logdev.nil? || severity < level_of(path) || @silences[local_log_id]
48
55
 
49
56
  progname = "#{path}: #{progname || @progname}"
@@ -124,14 +131,26 @@ module Prefab
124
131
 
125
132
  private
126
133
 
134
+ NO_DEFAULT = nil
135
+
127
136
  # Find the closest match to 'log_level.path' in config
128
137
  def level_of(path)
129
- closest_log_level_match = @config_client.get(BASE_KEY, :WARN)
138
+ properties = Thread.current[:prefab_log_properties] || {}
139
+ lookup_key = Thread.current[:prefab_log_lookup_key] || nil
140
+
141
+ closest_log_level_match = nil
142
+
130
143
  path.split(SEP).each_with_object([BASE_KEY]) do |n, memo|
131
144
  memo << n
132
- val = @config_client.get(memo.join(SEP), nil)
145
+ val = @config_client.get(memo.join(SEP), NO_DEFAULT, properties, lookup_key)
133
146
  closest_log_level_match = val unless val.nil?
134
147
  end
148
+
149
+ if closest_log_level_match.nil?
150
+ # get the top-level setting or default to WARN
151
+ closest_log_level_match = @config_client.get(BASE_KEY, :WARN, properties, lookup_key)
152
+ end
153
+
135
154
  closest_log_level_match_int = Prefab::LogLevel.resolve(closest_log_level_match)
136
155
  LOG_LEVEL_LOOKUPS[closest_log_level_match_int]
137
156
  end
@@ -158,7 +177,7 @@ module Prefab
158
177
  # StubConfigClient to be used while config client initializes
159
178
  # since it may log
160
179
  class BootstrappingConfigClient
161
- def get(_key, default = nil)
180
+ def get(_key, default = nil, _properties = {}, _lookup_key = nil)
162
181
  ENV['PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'] ? ENV['PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'].upcase.to_sym : default
163
182
  end
164
183
  end
@@ -17,6 +17,7 @@ module Prefab
17
17
  attr_reader :prefab_config_override_dir
18
18
  attr_reader :prefab_config_classpath_dir
19
19
  attr_reader :prefab_envs
20
+ attr_reader :collect_sync_interval
20
21
 
21
22
  DEFAULT_LOG_FORMATTER = proc { |severity, datetime, progname, msg|
22
23
  "#{severity.ljust(5)} #{datetime}:#{' ' if progname}#{progname} #{msg}\n"
@@ -37,7 +38,10 @@ module Prefab
37
38
  LOCAL_ONLY = 2
38
39
  end
39
40
 
40
- def initialize(
41
+ DEFAULT_MAX_PATHS = 1_000
42
+ DEFAULT_SYNC_INTERVAL = 60
43
+
44
+ private def init(
41
45
  api_key: ENV['PREFAB_API_KEY'],
42
46
  logdev: $stdout,
43
47
  stats: NoopStats.new, # receives increment("prefab.limitcheck", {:tags=>["policy_group:page_view", "pass:true"]})
@@ -55,9 +59,11 @@ module Prefab
55
59
  prefab_datasources: ENV['PREFAB_DATASOURCES'] == 'LOCAL_ONLY' ? DATASOURCES::LOCAL_ONLY : DATASOURCES::ALL,
56
60
  prefab_config_override_dir: Dir.home,
57
61
  prefab_config_classpath_dir: '.',
58
- prefab_envs: ENV['PREFAB_ENVS'].nil? ? [] : ENV['PREFAB_ENVS'].split(',')
62
+ prefab_envs: ENV['PREFAB_ENVS'].nil? ? [] : ENV['PREFAB_ENVS'].split(','),
63
+ collect_logs: true,
64
+ collect_max_paths: DEFAULT_MAX_PATHS,
65
+ collect_sync_interval: DEFAULT_SYNC_INTERVAL
59
66
  )
60
- # debugger
61
67
  @api_key = api_key
62
68
  @logdev = logdev
63
69
  @stats = stats
@@ -74,12 +80,25 @@ module Prefab
74
80
  @prefab_config_classpath_dir = prefab_config_classpath_dir
75
81
  @prefab_config_override_dir = prefab_config_override_dir
76
82
  @prefab_envs = Array(prefab_envs)
83
+ @collect_logs = collect_logs
84
+ @collect_max_paths = collect_max_paths
85
+ @collect_sync_interval = collect_sync_interval
86
+ end
87
+
88
+ def initialize(options = {})
89
+ init(**options)
77
90
  end
78
91
 
79
92
  def local_only?
80
93
  @prefab_datasources == DATASOURCES::LOCAL_ONLY
81
94
  end
82
95
 
96
+ def collect_max_paths
97
+ return 0 if !@collect_logs || local_only?
98
+
99
+ @collect_max_paths
100
+ end
101
+
83
102
  # https://api.prefab.cloud -> https://api-prefab-cloud.global.ssl.fastly.net
84
103
  def url_for_api_cdn
85
104
  ENV['PREFAB_CDN_URL'] || "#{@prefab_api_url.gsub(/\./, '-')}.global.ssl.fastly.net"
@@ -21,10 +21,10 @@ module Prefab
21
21
 
22
22
  def load
23
23
  if File.exist?(@file)
24
- @client.log_internal Logger::INFO, "Load #{@file}"
24
+ @client.log_internal ::Logger::INFO, "Load #{@file}"
25
25
  YAML.load_file(@file)
26
26
  else
27
- @client.log_internal Logger::INFO, "No file #{@file}"
27
+ @client.log_internal ::Logger::INFO, "No file #{@file}"
28
28
  {}
29
29
  end
30
30
  end