prefab-cloud-ruby 0.20.0 → 0.22.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/.envrc.sample +3 -0
- data/.github/workflows/ruby.yml +5 -1
- data/.gitmodules +3 -0
- data/Gemfile +14 -12
- data/Gemfile.lock +24 -14
- data/README.md +12 -10
- data/Rakefile +13 -14
- data/VERSION +1 -1
- data/lib/prefab/auth_interceptor.rb +2 -1
- data/lib/prefab/cancellable_interceptor.rb +8 -7
- data/lib/prefab/client.rb +52 -27
- data/lib/prefab/config_client.rb +59 -70
- data/lib/prefab/config_loader.rb +7 -114
- data/lib/prefab/config_resolver.rb +27 -57
- data/lib/prefab/config_value_unwrapper.rb +23 -0
- data/lib/prefab/criteria_evaluator.rb +96 -0
- data/lib/prefab/errors/invalid_api_key_error.rb +1 -1
- data/lib/prefab/feature_flag_client.rb +13 -145
- data/lib/prefab/internal_logger.rb +7 -6
- data/lib/prefab/local_config_parser.rb +110 -0
- data/lib/prefab/log_path_collector.rb +98 -0
- data/lib/prefab/logger_client.rb +46 -44
- data/lib/prefab/murmer3.rb +3 -4
- data/lib/prefab/noop_cache.rb +5 -7
- data/lib/prefab/noop_stats.rb +2 -3
- data/lib/prefab/options.rb +32 -11
- data/lib/prefab/ratelimit_client.rb +11 -13
- data/lib/prefab/sse_logger.rb +3 -2
- data/lib/prefab/weighted_value_resolver.rb +42 -0
- data/lib/prefab/yaml_config_parser.rb +32 -0
- data/lib/prefab-cloud-ruby.rb +7 -2
- data/lib/prefab_pb.rb +70 -43
- data/lib/prefab_services_pb.rb +14 -1
- data/prefab-cloud-ruby.gemspec +33 -19
- data/test/.prefab.unit_tests.config.yaml +3 -2
- data/test/integration_test.rb +98 -0
- data/test/integration_test_helpers.rb +37 -0
- data/test/test_client.rb +56 -31
- data/test/test_config_client.rb +21 -20
- data/test/test_config_loader.rb +48 -37
- data/test/test_config_resolver.rb +312 -135
- data/test/test_config_value_unwrapper.rb +83 -0
- data/test/test_criteria_evaluator.rb +533 -0
- data/test/test_feature_flag_client.rb +35 -347
- data/test/test_helper.rb +18 -14
- data/test/test_integration.rb +33 -0
- data/test/test_local_config_parser.rb +78 -0
- data/test/test_log_path_collector.rb +56 -0
- data/test/test_logger.rb +52 -51
- data/test/test_options.rb +32 -0
- data/test/test_weighted_value_resolver.rb +65 -0
- metadata +30 -16
- data/lib/prefab/config_helper.rb +0 -31
- data/run_test_harness_server.sh +0 -8
- data/test/harness_server.rb +0 -64
@@ -1,176 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Prefab
|
3
4
|
class FeatureFlagClient
|
4
|
-
include Prefab::ConfigHelper
|
5
|
-
MAX_32_FLOAT = 4294967294.0
|
6
|
-
|
7
5
|
def initialize(base_client)
|
8
6
|
@base_client = base_client
|
9
7
|
end
|
10
8
|
|
11
|
-
def upsert(feature_name, feature_obj)
|
12
|
-
@base_client.config_client.upsert(feature_name, Prefab::ConfigValue.new(feature_flag: feature_obj))
|
13
|
-
end
|
14
|
-
|
15
9
|
def feature_is_on?(feature_name)
|
16
10
|
feature_is_on_for?(feature_name, nil)
|
17
11
|
end
|
18
12
|
|
19
13
|
def feature_is_on_for?(feature_name, lookup_key, attributes: {})
|
20
|
-
@base_client.stats.increment(
|
14
|
+
@base_client.stats.increment('prefab.featureflag.on', tags: ["feature:#{feature_name}"])
|
21
15
|
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
def get(feature_name, lookup_key=nil, attributes={}, default: false)
|
26
|
-
variant = _get(feature_name, lookup_key, attributes, default: default)
|
16
|
+
variant = @base_client.config_client.get(feature_name, false, attributes, lookup_key)
|
27
17
|
|
28
|
-
|
18
|
+
is_on?(variant)
|
29
19
|
end
|
30
20
|
|
31
|
-
|
21
|
+
def get(feature_name, lookup_key = nil, attributes = {}, default: false)
|
22
|
+
value = _get(feature_name, lookup_key, attributes)
|
32
23
|
|
33
|
-
|
34
|
-
if variant_maybe.nil?
|
35
|
-
default != Prefab::Client::NO_DEFAULT_PROVIDED ? default : nil
|
36
|
-
else
|
37
|
-
value_of_variant(variant_maybe)
|
38
|
-
end
|
24
|
+
value.nil? ? default : value
|
39
25
|
end
|
40
26
|
|
41
|
-
|
42
|
-
feature_obj = @base_client.config_client.get(feature_name, default)
|
43
|
-
config_obj = @base_client.config_client.get_config_obj(feature_name)
|
44
|
-
|
45
|
-
return nil if feature_obj.nil? || config_obj.nil?
|
46
|
-
|
47
|
-
if feature_obj == !!feature_obj
|
48
|
-
return feature_obj
|
49
|
-
end
|
27
|
+
private
|
50
28
|
|
51
|
-
|
52
|
-
|
29
|
+
def _get(feature_name, lookup_key = nil, attributes = {})
|
30
|
+
@base_client.config_client.get(feature_name, nil, attributes, lookup_key)
|
53
31
|
end
|
54
32
|
|
55
33
|
def is_on?(variant)
|
56
|
-
if variant.nil?
|
57
|
-
return false
|
58
|
-
end
|
34
|
+
return false if variant.nil?
|
59
35
|
|
60
|
-
if variant == !!variant
|
61
|
-
return variant
|
62
|
-
end
|
36
|
+
return variant if variant == !!variant
|
63
37
|
|
64
38
|
variant.bool
|
65
|
-
rescue
|
39
|
+
rescue StandardError
|
66
40
|
@base_client.log.info("is_on? methods only work for boolean feature flags variants. This feature flags variant is '#{variant}'. Returning false")
|
67
41
|
false
|
68
42
|
end
|
69
|
-
|
70
|
-
def get_variant(feature_name, lookup_key, attributes, feature_obj, variants)
|
71
|
-
if !feature_obj.active
|
72
|
-
return get_variant_obj(variants, feature_obj.inactive_variant_idx)
|
73
|
-
end
|
74
|
-
|
75
|
-
#default to inactive
|
76
|
-
variant_weights = [Prefab::VariantWeight.new(variant_idx: feature_obj.inactive_variant_idx, weight: 1)]
|
77
|
-
|
78
|
-
# if rules.match
|
79
|
-
feature_obj.rules.each do |rule|
|
80
|
-
if criteria_match?(rule.criteria, lookup_key, attributes)
|
81
|
-
variant_weights = rule.variant_weights
|
82
|
-
break
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
percent_through_distribution = rand()
|
87
|
-
if lookup_key
|
88
|
-
percent_through_distribution = get_user_pct(feature_name, lookup_key)
|
89
|
-
end
|
90
|
-
|
91
|
-
variant_idx = get_variant_idx_from_weights(variant_weights, percent_through_distribution, feature_name)
|
92
|
-
|
93
|
-
return get_variant_obj(variants, variant_idx)
|
94
|
-
end
|
95
|
-
|
96
|
-
def get_variant_obj(variants, idx)
|
97
|
-
# our array is 0 based, but the idx are 1 based so the protos are clearly set
|
98
|
-
return variants[idx - 1] if variants.length >= idx
|
99
|
-
nil
|
100
|
-
end
|
101
|
-
|
102
|
-
def get_variant_idx_from_weights(variant_weights, percent_through_distribution, feature_name)
|
103
|
-
distrubution_space = variant_weights.inject(0) { |sum, v| sum + v.weight }
|
104
|
-
bucket = distrubution_space * percent_through_distribution
|
105
|
-
sum = 0
|
106
|
-
variant_weights.each do |variant_weight|
|
107
|
-
if bucket < sum + variant_weight.weight
|
108
|
-
return variant_weight.variant_idx
|
109
|
-
else
|
110
|
-
sum += variant_weight.weight
|
111
|
-
end
|
112
|
-
end
|
113
|
-
# variants didn't add up to 100%
|
114
|
-
@base_client.log.info("Variants of #{feature_name} did not add to 100%")
|
115
|
-
return variant_weights.last.variant_idx
|
116
|
-
end
|
117
|
-
|
118
|
-
def get_user_pct(feature, lookup_key)
|
119
|
-
to_hash = "#{feature}#{lookup_key}"
|
120
|
-
int_value = Murmur3.murmur3_32(to_hash)
|
121
|
-
int_value / MAX_32_FLOAT
|
122
|
-
end
|
123
|
-
|
124
|
-
def criteria_match?(criteria, lookup_key, attributes)
|
125
|
-
case criteria.operator
|
126
|
-
when :ALWAYS_TRUE
|
127
|
-
true
|
128
|
-
when :LOOKUP_KEY_IN
|
129
|
-
criteria.values.include?(lookup_key)
|
130
|
-
when :LOOKUP_KEY_NOT_IN
|
131
|
-
!criteria.values.include?(lookup_key)
|
132
|
-
when :IN_SEG
|
133
|
-
segment_matches?(criteria.values, lookup_key, attributes)
|
134
|
-
when :NOT_IN_SEG
|
135
|
-
!segment_matches?(criteria.values, lookup_key, attributes)
|
136
|
-
when :PROP_IS_ONE_OF
|
137
|
-
criteria.values.include?(attribute_value(attributes, criteria.property))
|
138
|
-
when :PROP_IS_NOT_ONE_OF
|
139
|
-
!criteria.values.include?(attribute_value(attributes, criteria.property))
|
140
|
-
when :PROP_ENDS_WITH_ONE_OF
|
141
|
-
criteria.values.any? { |value| attribute_value(attributes, criteria.property)&.end_with?(value) }
|
142
|
-
when :PROP_DOES_NOT_END_WITH_ONE_OF
|
143
|
-
criteria.values.none? { |value| attribute_value(attributes, criteria.property)&.end_with?(value) }
|
144
|
-
else
|
145
|
-
@base_client.log.info("Unknown Operator: #{criteria.operator}")
|
146
|
-
false
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def attribute_value(attributes, property)
|
151
|
-
attributes[property] || attributes[property.to_sym]
|
152
|
-
end
|
153
|
-
|
154
|
-
# evaluate each segment key and return whether any match
|
155
|
-
# there should be an associated segment available as a standard config obj
|
156
|
-
def segment_matches?(segment_keys, lookup_key, attributes)
|
157
|
-
segment_keys.any? do |segment_key|
|
158
|
-
segment = @base_client.config_client.get(segment_key)
|
159
|
-
if segment.nil?
|
160
|
-
@base_client.log.info("Missing Segment")
|
161
|
-
false
|
162
|
-
else
|
163
|
-
segment_match?(segment, lookup_key, attributes)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# does a given segment match?
|
169
|
-
def segment_match?(segment, lookup_key, attributes)
|
170
|
-
segment.criterion.any? do |criteria|
|
171
|
-
criteria_match?(criteria, lookup_key, attributes)
|
172
|
-
end
|
173
|
-
end
|
174
43
|
end
|
175
44
|
end
|
176
|
-
|
@@ -1,28 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Prefab
|
3
|
-
class InternalLogger < Logger
|
4
|
+
class InternalLogger < ::Logger
|
4
5
|
def initialize(path, logger)
|
5
6
|
@path = path
|
6
7
|
@logger = logger
|
7
8
|
end
|
8
9
|
|
9
|
-
def debug(progname = nil
|
10
|
+
def debug(progname = nil)
|
10
11
|
@logger.log_internal yield, @path, progname, DEBUG
|
11
12
|
end
|
12
13
|
|
13
|
-
def info(progname = nil
|
14
|
+
def info(progname = nil)
|
14
15
|
@logger.log_internal yield, @path, progname, INFO
|
15
16
|
end
|
16
17
|
|
17
|
-
def warn(progname = nil
|
18
|
+
def warn(progname = nil)
|
18
19
|
@logger.log_internal yield, @path, progname, WARN
|
19
20
|
end
|
20
21
|
|
21
|
-
def error(progname = nil
|
22
|
+
def error(progname = nil)
|
22
23
|
@logger.log_internal yield, @path, progname, ERROR
|
23
24
|
end
|
24
25
|
|
25
|
-
def fatal(progname = nil
|
26
|
+
def fatal(progname = nil)
|
26
27
|
@logger.log_internal yield, @path, progname, FATAL
|
27
28
|
end
|
28
29
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Prefab
|
4
|
+
class LocalConfigParser
|
5
|
+
class << self
|
6
|
+
def parse(key, value, config, file)
|
7
|
+
if value.instance_of?(Hash)
|
8
|
+
if value['feature_flag']
|
9
|
+
config[key] = feature_flag_config(file, key, value)
|
10
|
+
else
|
11
|
+
value.each do |nest_key, nest_value|
|
12
|
+
nested_key = "#{key}.#{nest_key}"
|
13
|
+
nested_key = key if nest_key == '_'
|
14
|
+
parse(nested_key, nest_value, config, file)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
config[key] = {
|
19
|
+
source: file,
|
20
|
+
match: 'default',
|
21
|
+
config: Prefab::Config.new(
|
22
|
+
config_type: :CONFIG,
|
23
|
+
key: key,
|
24
|
+
rows: [
|
25
|
+
Prefab::ConfigRow.new(values: [
|
26
|
+
Prefab::ConditionalValue.new(value: value_from(key, value))
|
27
|
+
])
|
28
|
+
]
|
29
|
+
)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
config
|
34
|
+
end
|
35
|
+
|
36
|
+
def value_from(key, raw)
|
37
|
+
case raw
|
38
|
+
when String
|
39
|
+
if key.to_s.start_with? Prefab::LoggerClient::BASE_KEY
|
40
|
+
prefab_log_level_resolve = Prefab::LogLevel.resolve(raw.upcase.to_sym) || Prefab::LogLevel::NOT_SET_LOG_LEVEL
|
41
|
+
{ log_level: prefab_log_level_resolve }
|
42
|
+
else
|
43
|
+
{ string: raw }
|
44
|
+
end
|
45
|
+
when Integer
|
46
|
+
{ int: raw }
|
47
|
+
when TrueClass, FalseClass
|
48
|
+
{ bool: raw }
|
49
|
+
when Float
|
50
|
+
{ double: raw }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def feature_flag_config(file, key, value)
|
55
|
+
criterion = (parse_criterion(value['criterion']) if value['criterion'])
|
56
|
+
|
57
|
+
variant = Prefab::ConfigValue.new(value_from(key, value['value']))
|
58
|
+
|
59
|
+
row = Prefab::ConfigRow.new(
|
60
|
+
values: [
|
61
|
+
Prefab::ConditionalValue.new(
|
62
|
+
criteria: [criterion].compact,
|
63
|
+
value: Prefab::ConfigValue.new(
|
64
|
+
weighted_values: Prefab::WeightedValues.new(weighted_values: [
|
65
|
+
Prefab::WeightedValue.new(
|
66
|
+
weight: 1000,
|
67
|
+
value: variant
|
68
|
+
)
|
69
|
+
])
|
70
|
+
)
|
71
|
+
)
|
72
|
+
]
|
73
|
+
)
|
74
|
+
|
75
|
+
raise Prefab::Error, "Feature flag config `#{key}` #{file} must have a `value`" unless value.key?('value')
|
76
|
+
|
77
|
+
{
|
78
|
+
source: file,
|
79
|
+
match: key,
|
80
|
+
config: Prefab::Config.new(
|
81
|
+
config_type: :FEATURE_FLAG,
|
82
|
+
key: key,
|
83
|
+
allowable_values: [variant],
|
84
|
+
rows: [row]
|
85
|
+
)
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_criterion(criterion)
|
90
|
+
Prefab::Criterion.new(operator: criterion['operator'],
|
91
|
+
property_name: parse_property(criterion),
|
92
|
+
value_to_match: parse_value_to_match(criterion['values']))
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_property(criterion)
|
96
|
+
if criterion['operator'] == 'LOOKUP_KEY_IN'
|
97
|
+
Prefab::CriteriaEvaluator::LOOKUP_KEY
|
98
|
+
else
|
99
|
+
criterion['property']
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse_value_to_match(values)
|
104
|
+
raise "Can't handle #{values}" unless values.instance_of?(Array)
|
105
|
+
|
106
|
+
Prefab::ConfigValue.new(string_list: Prefab::StringList.new(values: values))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -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
|
data/lib/prefab/logger_client.rb
CHANGED
@@ -1,52 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module Prefab
|
3
|
-
class LoggerClient < Logger
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
require 'prefab/log_path_collector'
|
4
|
+
|
5
|
+
module Prefab
|
6
|
+
class LoggerClient < ::Logger
|
7
|
+
SEP = '.'
|
8
|
+
BASE_KEY = 'log-level'
|
9
|
+
UNKNOWN_PATH = 'unknown.'
|
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
|
-
|
30
|
-
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
|
34
40
|
|
35
41
|
def log_internal(message, path = nil, progname, severity, &block)
|
36
|
-
if path
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
path = if path
|
43
|
+
"#{INTERNAL_PREFIX}.#{path}"
|
44
|
+
else
|
45
|
+
INTERNAL_PREFIX
|
46
|
+
end
|
41
47
|
|
42
48
|
log(message, path, progname, severity, &block)
|
43
49
|
end
|
44
50
|
|
45
|
-
def log(message, path, progname, severity
|
46
|
-
severity ||= Logger::UNKNOWN
|
47
|
-
|
48
|
-
|
49
|
-
end
|
51
|
+
def log(message, path, progname, severity)
|
52
|
+
severity ||= ::Logger::UNKNOWN
|
53
|
+
|
54
|
+
return true if @logdev.nil? || severity < level_of(path) || @silences[local_log_id]
|
50
55
|
|
51
56
|
progname = "#{path}: #{progname || @progname}"
|
52
57
|
|
@@ -60,7 +65,8 @@ module Prefab
|
|
60
65
|
end
|
61
66
|
|
62
67
|
@logdev.write(
|
63
|
-
format_message(format_severity(severity), Time.now, progname, message)
|
68
|
+
format_message(format_severity(severity), Time.now, progname, message)
|
69
|
+
)
|
64
70
|
true
|
65
71
|
end
|
66
72
|
|
@@ -85,23 +91,23 @@ module Prefab
|
|
85
91
|
end
|
86
92
|
|
87
93
|
def debug?
|
88
|
-
true
|
94
|
+
true
|
89
95
|
end
|
90
96
|
|
91
97
|
def info?
|
92
|
-
true
|
98
|
+
true
|
93
99
|
end
|
94
100
|
|
95
101
|
def warn?
|
96
|
-
true
|
102
|
+
true
|
97
103
|
end
|
98
104
|
|
99
105
|
def error?
|
100
|
-
true
|
106
|
+
true
|
101
107
|
end
|
102
108
|
|
103
109
|
def fatal?
|
104
|
-
true
|
110
|
+
true
|
105
111
|
end
|
106
112
|
|
107
113
|
def level
|
@@ -128,13 +134,10 @@ module Prefab
|
|
128
134
|
# Find the closest match to 'log_level.path' in config
|
129
135
|
def level_of(path)
|
130
136
|
closest_log_level_match = @config_client.get(BASE_KEY, :WARN)
|
131
|
-
path.split(SEP).
|
137
|
+
path.split(SEP).each_with_object([BASE_KEY]) do |n, memo|
|
132
138
|
memo << n
|
133
139
|
val = @config_client.get(memo.join(SEP), nil)
|
134
|
-
unless val.nil?
|
135
|
-
closest_log_level_match = val
|
136
|
-
end
|
137
|
-
memo
|
140
|
+
closest_log_level_match = val unless val.nil?
|
138
141
|
end
|
139
142
|
closest_log_level_match_int = Prefab::LogLevel.resolve(closest_log_level_match)
|
140
143
|
LOG_LEVEL_LOOKUPS[closest_log_level_match_int]
|
@@ -150,10 +153,10 @@ module Prefab
|
|
150
153
|
def get_path(absolute_path, base_label)
|
151
154
|
path = (absolute_path || UNKNOWN_PATH).dup
|
152
155
|
path.slice! Dir.pwd
|
153
|
-
path.gsub!(
|
156
|
+
path.gsub!(%r{(.*)?(?=/lib)}im, '') # replace everything before first lib
|
154
157
|
|
155
|
-
path = path.gsub(
|
156
|
-
path.slice!
|
158
|
+
path = path.gsub('/', SEP).gsub(/.rb.*/, '') + SEP + base_label
|
159
|
+
path.slice! '.lib'
|
157
160
|
path.slice! SEP
|
158
161
|
path
|
159
162
|
end
|
@@ -162,9 +165,8 @@ module Prefab
|
|
162
165
|
# StubConfigClient to be used while config client initializes
|
163
166
|
# since it may log
|
164
167
|
class BootstrappingConfigClient
|
165
|
-
def get(
|
166
|
-
ENV[
|
168
|
+
def get(_key, default = nil)
|
169
|
+
ENV['PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'] ? ENV['PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL'].upcase.to_sym : default
|
167
170
|
end
|
168
171
|
end
|
169
172
|
end
|
170
|
-
|
data/lib/prefab/murmer3.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Murmur3
|
3
4
|
## MurmurHash3 was written by Austin Appleby, and is placed in the public
|
4
5
|
## domain. The author hereby disclaims copyright to this source code.
|
@@ -9,7 +10,6 @@ class Murmur3
|
|
9
10
|
((x << r) | (x >> (32 - r))) & MASK32
|
10
11
|
end
|
11
12
|
|
12
|
-
|
13
13
|
def self.murmur3_32_fmix(h)
|
14
14
|
h &= MASK32
|
15
15
|
h ^= h >> 16
|
@@ -25,7 +25,7 @@ class Murmur3
|
|
25
25
|
(k1 * 0x1b873593) & MASK32
|
26
26
|
end
|
27
27
|
|
28
|
-
def self.murmur3_32(str, seed=0)
|
28
|
+
def self.murmur3_32(str, seed = 0)
|
29
29
|
h1 = seed
|
30
30
|
numbers = str.unpack('V*C*')
|
31
31
|
tailn = str.length % 4
|
@@ -33,7 +33,7 @@ class Murmur3
|
|
33
33
|
for k1 in numbers
|
34
34
|
h1 ^= murmur3_32__mmix(k1)
|
35
35
|
h1 = murmur3_32_rotl(h1, 13)
|
36
|
-
h1 = (h1*5 + 0xe6546b64) & MASK32
|
36
|
+
h1 = (h1 * 5 + 0xe6546b64) & MASK32
|
37
37
|
end
|
38
38
|
|
39
39
|
unless tail.empty?
|
@@ -47,5 +47,4 @@ class Murmur3
|
|
47
47
|
h1 ^= str.length
|
48
48
|
murmur3_32_fmix(h1)
|
49
49
|
end
|
50
|
-
|
51
50
|
end
|
data/lib/prefab/noop_cache.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Prefab
|
3
4
|
class NoopCache
|
4
|
-
def fetch(
|
5
|
+
def fetch(_name, _opts)
|
5
6
|
yield
|
6
7
|
end
|
7
8
|
|
8
|
-
def write(name, value, opts=nil)
|
9
|
-
end
|
9
|
+
def write(name, value, opts = nil); end
|
10
10
|
|
11
|
-
def read(name)
|
12
|
-
end
|
11
|
+
def read(name); end
|
13
12
|
|
14
|
-
def delete(name)
|
15
|
-
end
|
13
|
+
def delete(name); end
|
16
14
|
end
|
17
15
|
end
|
data/lib/prefab/noop_stats.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module Prefab
|
3
2
|
|
3
|
+
module Prefab
|
4
4
|
class NoopStats
|
5
5
|
# receives increment("prefab.ratelimit.limitcheck", {:tags=>["policy_group:page_view", "pass:true"]})
|
6
|
-
def increment(name, opts={})
|
7
|
-
end
|
6
|
+
def increment(name, opts = {}); end
|
8
7
|
end
|
9
8
|
end
|