prefab-cloud-ruby 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 +7 -0
- data/.envrc.sample +3 -0
- data/.github/workflows/ruby.yml +46 -0
- data/.gitmodules +3 -0
- data/.rubocop.yml +13 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +169 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +188 -0
- data/LICENSE.txt +20 -0
- data/README.md +94 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/bin/console +21 -0
- data/compile_protos.sh +18 -0
- data/lib/prefab/client.rb +153 -0
- data/lib/prefab/config_client.rb +292 -0
- data/lib/prefab/config_client_presenter.rb +18 -0
- data/lib/prefab/config_loader.rb +84 -0
- data/lib/prefab/config_resolver.rb +77 -0
- data/lib/prefab/config_value_unwrapper.rb +115 -0
- data/lib/prefab/config_value_wrapper.rb +18 -0
- data/lib/prefab/context.rb +179 -0
- data/lib/prefab/context_shape.rb +20 -0
- data/lib/prefab/context_shape_aggregator.rb +65 -0
- data/lib/prefab/criteria_evaluator.rb +136 -0
- data/lib/prefab/encryption.rb +65 -0
- data/lib/prefab/error.rb +6 -0
- data/lib/prefab/errors/env_var_parse_error.rb +11 -0
- data/lib/prefab/errors/initialization_timeout_error.rb +13 -0
- data/lib/prefab/errors/invalid_api_key_error.rb +19 -0
- data/lib/prefab/errors/missing_default_error.rb +13 -0
- data/lib/prefab/errors/missing_env_var_error.rb +11 -0
- data/lib/prefab/errors/uninitialized_error.rb +13 -0
- data/lib/prefab/evaluation.rb +52 -0
- data/lib/prefab/evaluation_summary_aggregator.rb +87 -0
- data/lib/prefab/example_contexts_aggregator.rb +78 -0
- data/lib/prefab/exponential_backoff.rb +21 -0
- data/lib/prefab/feature_flag_client.rb +42 -0
- data/lib/prefab/http_connection.rb +41 -0
- data/lib/prefab/internal_logger.rb +16 -0
- data/lib/prefab/local_config_parser.rb +151 -0
- data/lib/prefab/log_path_aggregator.rb +69 -0
- data/lib/prefab/logger_client.rb +264 -0
- data/lib/prefab/murmer3.rb +50 -0
- data/lib/prefab/options.rb +208 -0
- data/lib/prefab/periodic_sync.rb +69 -0
- data/lib/prefab/prefab.rb +56 -0
- data/lib/prefab/rate_limit_cache.rb +41 -0
- data/lib/prefab/resolved_config_presenter.rb +86 -0
- data/lib/prefab/time_helpers.rb +7 -0
- data/lib/prefab/weighted_value_resolver.rb +42 -0
- data/lib/prefab/yaml_config_parser.rb +34 -0
- data/lib/prefab-cloud-ruby.rb +57 -0
- data/lib/prefab_pb.rb +93 -0
- data/prefab-cloud-ruby.gemspec +155 -0
- data/test/.prefab.default.config.yaml +2 -0
- data/test/.prefab.unit_tests.config.yaml +28 -0
- data/test/integration_test.rb +150 -0
- data/test/integration_test_helpers.rb +151 -0
- data/test/support/common_helpers.rb +180 -0
- data/test/support/mock_base_client.rb +42 -0
- data/test/support/mock_config_client.rb +19 -0
- data/test/support/mock_config_loader.rb +1 -0
- data/test/test_client.rb +444 -0
- data/test/test_config_client.rb +109 -0
- data/test/test_config_loader.rb +117 -0
- data/test/test_config_resolver.rb +430 -0
- data/test/test_config_value_unwrapper.rb +224 -0
- data/test/test_config_value_wrapper.rb +42 -0
- data/test/test_context.rb +203 -0
- data/test/test_context_shape.rb +50 -0
- data/test/test_context_shape_aggregator.rb +147 -0
- data/test/test_criteria_evaluator.rb +726 -0
- data/test/test_encryption.rb +16 -0
- data/test/test_evaluation_summary_aggregator.rb +162 -0
- data/test/test_example_contexts_aggregator.rb +238 -0
- data/test/test_exponential_backoff.rb +18 -0
- data/test/test_feature_flag_client.rb +48 -0
- data/test/test_helper.rb +17 -0
- data/test/test_integration.rb +58 -0
- data/test/test_local_config_parser.rb +147 -0
- data/test/test_log_path_aggregator.rb +62 -0
- data/test/test_logger.rb +621 -0
- data/test/test_logger_initialization.rb +12 -0
- data/test/test_options.rb +75 -0
- data/test/test_prefab.rb +12 -0
- data/test/test_rate_limit_cache.rb +44 -0
- data/test/test_weighted_value_resolver.rb +71 -0
- metadata +337 -0
data/test/test_logger.rb
ADDED
@@ -0,0 +1,621 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class TestLogger < Minitest::Test
|
6
|
+
TEST_ENV_ID = 2
|
7
|
+
DEFAULT_VALUE = 'FATAL'
|
8
|
+
DEFAULT_ENV_VALUE = 'INFO'
|
9
|
+
DESIRED_VALUE = 'DEBUG'
|
10
|
+
WRONG_ENV_VALUE = 'ERROR'
|
11
|
+
PROJECT_ENV_ID = 1
|
12
|
+
|
13
|
+
DEFAULT_ROW = PrefabProto::ConfigRow.new(
|
14
|
+
values: [
|
15
|
+
PrefabProto::ConditionalValue.new(
|
16
|
+
value: PrefabProto::ConfigValue.new(log_level: DEFAULT_VALUE)
|
17
|
+
)
|
18
|
+
]
|
19
|
+
)
|
20
|
+
|
21
|
+
def setup
|
22
|
+
super
|
23
|
+
Prefab::LoggerClient.send(:public, :get_path)
|
24
|
+
Prefab::LoggerClient.send(:public, :get_loc_path)
|
25
|
+
Prefab::LoggerClient.send(:public, :level_of)
|
26
|
+
@logger = Prefab::LoggerClient.new($stdout)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_get_path
|
30
|
+
assert_equal 'test_l.foo_warn',
|
31
|
+
@logger.get_path('/Users/jdwyah/Documents/workspace/RateLimitInc/prefab-cloud-ruby/lib/test_l.rb',
|
32
|
+
'foo_warn')
|
33
|
+
|
34
|
+
assert_equal 'active_support.log_subscriber.info',
|
35
|
+
@logger.get_path('/Users/jdwyah/.rvm/gems/ruby-2.3.3@forcerank/gems/activesupport-4.1.16/lib/active_support/log_subscriber.rb',
|
36
|
+
'info')
|
37
|
+
assert_equal 'active_support.log_subscriber.info',
|
38
|
+
@logger.get_path("/Users/jeffdwyer/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/activesupport-7.0.2.4/lib/active_support/log_subscriber.rb:130:in `info'",
|
39
|
+
'info')
|
40
|
+
assert_equal 'unknown.info',
|
41
|
+
@logger.get_path(nil,
|
42
|
+
'info')
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_loc_resolution
|
46
|
+
backtrace_location = Struct.new(:absolute_path, :base_label, :string) do
|
47
|
+
def to_s
|
48
|
+
string
|
49
|
+
end
|
50
|
+
end # https://ruby-doc.org/core-3.0.0/Thread/Backtrace/Location.html
|
51
|
+
|
52
|
+
# verify that even if the Thread::Backtrace::Location does not have an absolute_location, we do our best
|
53
|
+
assert_equal 'active_support.log_subscriber.info',
|
54
|
+
@logger.get_loc_path(backtrace_location.new(nil,
|
55
|
+
'info',
|
56
|
+
"/Users/jeffdwyer/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/activesupport-7.0.2.4/lib/active_support/log_subscriber.rb:130:in `info'"))
|
57
|
+
assert_equal 'test_l.info',
|
58
|
+
@logger.get_loc_path(backtrace_location.new('/Users/jdwyah/Documents/workspace/RateLimitInc/prefab-cloud-ruby/lib/test_l.rb',
|
59
|
+
'info',
|
60
|
+
"/Users/jeffdwyer/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/activesupport-7.0.2.4/lib/active_support/log_subscriber.rb:130:in `info'"))
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_level_of
|
64
|
+
with_env('PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL', 'info') do
|
65
|
+
# env var overrides the default level
|
66
|
+
assert_equal ::Logger::INFO,
|
67
|
+
@logger.level_of('app.models.user'), 'PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL is info'
|
68
|
+
|
69
|
+
@logger.config_client = MockConfigClient.new({})
|
70
|
+
assert_equal ::Logger::WARN,
|
71
|
+
@logger.level_of('app.models.user'), 'default is warn'
|
72
|
+
|
73
|
+
@logger.config_client = MockConfigClient.new('log-level.app' => :INFO)
|
74
|
+
assert_equal ::Logger::INFO,
|
75
|
+
@logger.level_of('app.models.user')
|
76
|
+
|
77
|
+
@logger.config_client = MockConfigClient.new('log-level.app' => :DEBUG)
|
78
|
+
assert_equal ::Logger::DEBUG,
|
79
|
+
@logger.level_of('app.models.user')
|
80
|
+
|
81
|
+
@logger.config_client = MockConfigClient.new('log-level.app' => :DEBUG,
|
82
|
+
'log-level.app.models' => :ERROR)
|
83
|
+
assert_equal ::Logger::ERROR,
|
84
|
+
@logger.level_of('app.models.user'), 'test leveling'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_log_internal
|
89
|
+
prefab, io = captured_logger
|
90
|
+
prefab.log.log_internal(::Logger::WARN, 'test message', 'cloud.prefab.client.test.path')
|
91
|
+
assert_logged io, 'WARN', "cloud.prefab.client.test.path", "test message"
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_log_internal_unknown
|
95
|
+
prefab, io = captured_logger
|
96
|
+
prefab.log.log_internal(::Logger::UNKNOWN, 'test message', 'cloud.prefab.client.test.path')
|
97
|
+
assert_logged io, 'ANY', "cloud.prefab.client.test.path", "test message"
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_log_internal_silencing
|
101
|
+
prefab, io = captured_logger
|
102
|
+
prefab.log.silence do
|
103
|
+
prefab.log.log_internal(::Logger::WARN, 'should not log', 'cloud.prefab.client.test.path')
|
104
|
+
end
|
105
|
+
prefab.log.log_internal(::Logger::WARN, 'should log', 'cloud.prefab.client.test.path')
|
106
|
+
assert_logged io, 'WARN', "cloud.prefab.client.test.path", "should log"
|
107
|
+
refute_logged io, 'should not log'
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_log
|
111
|
+
prefab, io = captured_logger
|
112
|
+
prefab.log.log('test message', 'test.path', '', ::Logger::WARN)
|
113
|
+
assert_logged io, 'WARN', "test.path", "test message"
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_log_unknown
|
117
|
+
prefab, io = captured_logger
|
118
|
+
prefab.log.log('test message', 'test.path', '', ::Logger::UNKNOWN)
|
119
|
+
assert_logged io, 'ANY', "test.path", "test message"
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_log_silencing
|
123
|
+
prefab, io = captured_logger
|
124
|
+
prefab.log.silence do
|
125
|
+
prefab.log.log('should not log', 'test.path', '', ::Logger::WARN)
|
126
|
+
end
|
127
|
+
prefab.log.log('should log', 'test.path', '', ::Logger::WARN)
|
128
|
+
assert_logged io, 'WARN', "test.path", "should log"
|
129
|
+
refute_logged io, 'should not log'
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_logging_with_prefix
|
133
|
+
prefix = 'my.own.prefix'
|
134
|
+
message = 'this is a test'
|
135
|
+
|
136
|
+
prefab, io = captured_logger(log_prefix: prefix)
|
137
|
+
|
138
|
+
prefixed_logger = prefab.log
|
139
|
+
prefixed_logger.error message
|
140
|
+
|
141
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_prefix", message
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_logging_without_a_progname
|
145
|
+
prefab, io = captured_logger
|
146
|
+
message = 'MY MESSAGE'
|
147
|
+
|
148
|
+
prefab.log.error message
|
149
|
+
|
150
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_logging_without_a_progname', message
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_logging_without_a_progname_or_message
|
154
|
+
prefab, io = captured_logger
|
155
|
+
|
156
|
+
prefab.log.error
|
157
|
+
|
158
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_logging_without_a_progname_or_message', ''
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_logging_with_a_progname
|
162
|
+
prefab, io = captured_logger
|
163
|
+
message = 'MY MESSAGE'
|
164
|
+
|
165
|
+
prefab.log.progname = 'MY_PROGNAME'
|
166
|
+
prefab.log.error message
|
167
|
+
|
168
|
+
assert_logged io, 'ERROR', 'MY_PROGNAME: test.test_logger.test_logging_with_a_progname', message
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_logging_with_a_progname_and_no_message
|
172
|
+
prefab, io = captured_logger
|
173
|
+
|
174
|
+
prefab.log.progname = 'MY_PROGNAME'
|
175
|
+
prefab.log.error
|
176
|
+
|
177
|
+
assert_logged io, 'ERROR', 'MY_PROGNAME: test.test_logger.test_logging_with_a_progname_and_no_message', 'MY_PROGNAME'
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_logging_with_criteria_on_top_level_key
|
181
|
+
prefix = 'my.own.prefix'
|
182
|
+
|
183
|
+
config = PrefabProto::Config.new(
|
184
|
+
key: 'log-level',
|
185
|
+
rows: [
|
186
|
+
DEFAULT_ROW,
|
187
|
+
|
188
|
+
# wrong env
|
189
|
+
PrefabProto::ConfigRow.new(
|
190
|
+
project_env_id: TEST_ENV_ID,
|
191
|
+
values: [
|
192
|
+
PrefabProto::ConditionalValue.new(
|
193
|
+
criteria: [
|
194
|
+
PrefabProto::Criterion.new(
|
195
|
+
operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
196
|
+
value_to_match: string_list(['hotmail.com', 'gmail.com']),
|
197
|
+
property_name: 'user.email_suffix'
|
198
|
+
)
|
199
|
+
],
|
200
|
+
value: PrefabProto::ConfigValue.new(log_level: WRONG_ENV_VALUE)
|
201
|
+
)
|
202
|
+
]
|
203
|
+
),
|
204
|
+
|
205
|
+
# correct env
|
206
|
+
PrefabProto::ConfigRow.new(
|
207
|
+
project_env_id: PROJECT_ENV_ID,
|
208
|
+
values: [
|
209
|
+
PrefabProto::ConditionalValue.new(
|
210
|
+
criteria: [
|
211
|
+
PrefabProto::Criterion.new(
|
212
|
+
operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
213
|
+
value_to_match: string_list(['hotmail.com', 'gmail.com']),
|
214
|
+
property_name: 'user.email_suffix'
|
215
|
+
)
|
216
|
+
],
|
217
|
+
value: PrefabProto::ConfigValue.new(log_level: DESIRED_VALUE)
|
218
|
+
),
|
219
|
+
PrefabProto::ConditionalValue.new(
|
220
|
+
value: PrefabProto::ConfigValue.new(log_level: DEFAULT_ENV_VALUE)
|
221
|
+
)
|
222
|
+
]
|
223
|
+
)
|
224
|
+
]
|
225
|
+
)
|
226
|
+
|
227
|
+
prefab, io = captured_logger(log_prefix: prefix)
|
228
|
+
|
229
|
+
inject_config(prefab, config)
|
230
|
+
inject_project_env_id(prefab, PROJECT_ENV_ID)
|
231
|
+
|
232
|
+
# without any context, the level should be the default for the env (info)
|
233
|
+
prefab.with_context({}) do
|
234
|
+
prefab.log.debug 'Test debug'
|
235
|
+
refute_logged io, 'Test debug'
|
236
|
+
|
237
|
+
prefab.log.info 'Test info'
|
238
|
+
assert_logged io, 'INFO', "#{prefix}.test.test_logger.test_logging_with_criteria_on_top_level_key", 'Test info'
|
239
|
+
|
240
|
+
prefab.log.error 'Test error'
|
241
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_criteria_on_top_level_key", 'Test error'
|
242
|
+
end
|
243
|
+
|
244
|
+
reset_io(io)
|
245
|
+
|
246
|
+
# with the wrong context, the level should be the default for the env (info)
|
247
|
+
prefab.with_context(user: { email_suffix: 'yahoo.com' }) do
|
248
|
+
prefab.log.debug 'Test debug'
|
249
|
+
refute_logged io, 'Test debug'
|
250
|
+
|
251
|
+
prefab.log.info 'Test info'
|
252
|
+
assert_logged io, 'INFO', "#{prefix}.test.test_logger.test_logging_with_criteria_on_top_level_key", 'Test info'
|
253
|
+
|
254
|
+
prefab.log.error 'Test error'
|
255
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_criteria_on_top_level_key", 'Test error'
|
256
|
+
end
|
257
|
+
|
258
|
+
reset_io(io)
|
259
|
+
|
260
|
+
# with the correct context, the level should be the desired value (debug)
|
261
|
+
prefab.with_context(user: { email_suffix: 'hotmail.com' }) do
|
262
|
+
prefab.log.debug 'Test debug'
|
263
|
+
assert_logged io, 'DEBUG', "#{prefix}.test.test_logger.test_logging_with_criteria_on_top_level_key", 'Test debug'
|
264
|
+
|
265
|
+
prefab.log.info 'Test info'
|
266
|
+
assert_logged io, 'INFO', "#{prefix}.test.test_logger.test_logging_with_criteria_on_top_level_key", 'Test info'
|
267
|
+
|
268
|
+
prefab.log.error 'Test error'
|
269
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_criteria_on_top_level_key", 'Test error'
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_logging_with_criteria_on_key_path
|
274
|
+
prefix = 'my.own.prefix'
|
275
|
+
|
276
|
+
config = PrefabProto::Config.new(
|
277
|
+
key: 'log-level.my.own.prefix.test.test_logger',
|
278
|
+
rows: [
|
279
|
+
DEFAULT_ROW,
|
280
|
+
|
281
|
+
# wrong env
|
282
|
+
PrefabProto::ConfigRow.new(
|
283
|
+
project_env_id: TEST_ENV_ID,
|
284
|
+
values: [
|
285
|
+
PrefabProto::ConditionalValue.new(
|
286
|
+
criteria: [
|
287
|
+
PrefabProto::Criterion.new(
|
288
|
+
operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
289
|
+
value_to_match: string_list(['hotmail.com', 'gmail.com']),
|
290
|
+
property_name: 'email_suffix'
|
291
|
+
)
|
292
|
+
],
|
293
|
+
value: PrefabProto::ConfigValue.new(log_level: WRONG_ENV_VALUE)
|
294
|
+
)
|
295
|
+
]
|
296
|
+
),
|
297
|
+
|
298
|
+
# correct env
|
299
|
+
PrefabProto::ConfigRow.new(
|
300
|
+
project_env_id: PROJECT_ENV_ID,
|
301
|
+
values: [
|
302
|
+
PrefabProto::ConditionalValue.new(
|
303
|
+
criteria: [
|
304
|
+
PrefabProto::Criterion.new(
|
305
|
+
operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
306
|
+
value_to_match: string_list(['hotmail.com', 'gmail.com']),
|
307
|
+
property_name: 'user.email_suffix'
|
308
|
+
)
|
309
|
+
],
|
310
|
+
value: PrefabProto::ConfigValue.new(log_level: DESIRED_VALUE)
|
311
|
+
),
|
312
|
+
|
313
|
+
PrefabProto::ConditionalValue.new(
|
314
|
+
criteria: [
|
315
|
+
PrefabProto::Criterion.new(
|
316
|
+
operator: PrefabProto::Criterion::CriterionOperator::PROP_IS_ONE_OF,
|
317
|
+
value_to_match: string_list(%w[user:4567]),
|
318
|
+
property_name: 'user.tracking_id'
|
319
|
+
)
|
320
|
+
],
|
321
|
+
value: PrefabProto::ConfigValue.new(log_level: DESIRED_VALUE)
|
322
|
+
),
|
323
|
+
|
324
|
+
PrefabProto::ConditionalValue.new(
|
325
|
+
value: PrefabProto::ConfigValue.new(log_level: DEFAULT_ENV_VALUE)
|
326
|
+
)
|
327
|
+
]
|
328
|
+
)
|
329
|
+
]
|
330
|
+
)
|
331
|
+
|
332
|
+
prefab, io = captured_logger(log_prefix: prefix)
|
333
|
+
|
334
|
+
inject_config(prefab, config)
|
335
|
+
inject_project_env_id(prefab, PROJECT_ENV_ID)
|
336
|
+
|
337
|
+
# without any context, the level should be the default for the env (info)
|
338
|
+
prefab.with_context({}) do
|
339
|
+
prefab.log.debug 'Test debug'
|
340
|
+
refute_logged io, 'Test debug'
|
341
|
+
|
342
|
+
prefab.log.info 'Test info'
|
343
|
+
assert_logged io, 'INFO', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test info'
|
344
|
+
|
345
|
+
prefab.log.error 'Test error'
|
346
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test error'
|
347
|
+
end
|
348
|
+
|
349
|
+
reset_io(io)
|
350
|
+
|
351
|
+
# with the wrong context, the level should be the default for the env (info)
|
352
|
+
prefab.with_context(user: { email_suffix: 'yahoo.com' }) do
|
353
|
+
prefab.log.debug 'Test debug'
|
354
|
+
refute_logged io, 'Test debug'
|
355
|
+
|
356
|
+
prefab.log.info 'Test info'
|
357
|
+
assert_logged io, 'INFO', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test info'
|
358
|
+
|
359
|
+
prefab.log.error 'Test error'
|
360
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test error'
|
361
|
+
end
|
362
|
+
|
363
|
+
reset_io(io)
|
364
|
+
|
365
|
+
# with the correct context, the level should be the desired value (debug)
|
366
|
+
prefab.with_context(user: { email_suffix: 'hotmail.com' }) do
|
367
|
+
prefab.log.debug 'Test debug'
|
368
|
+
assert_logged io, 'DEBUG', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test debug'
|
369
|
+
|
370
|
+
prefab.log.info 'Test info'
|
371
|
+
assert_logged io, 'INFO', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test info'
|
372
|
+
|
373
|
+
prefab.log.error 'Test error'
|
374
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test error'
|
375
|
+
end
|
376
|
+
|
377
|
+
reset_io(io)
|
378
|
+
|
379
|
+
# with the correct lookup key
|
380
|
+
prefab.with_context(user: { tracking_id: 'user:4567' }) do
|
381
|
+
prefab.log.debug 'Test debug'
|
382
|
+
assert_logged io, 'DEBUG', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test debug'
|
383
|
+
|
384
|
+
prefab.log.info 'Test info'
|
385
|
+
assert_logged io, 'INFO', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test info'
|
386
|
+
|
387
|
+
prefab.log.error 'Test error'
|
388
|
+
assert_logged io, 'ERROR', "#{prefix}.test.test_logger.test_logging_with_criteria_on_key_path", 'Test error'
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
def test_logging_with_a_block
|
393
|
+
prefab, io = captured_logger
|
394
|
+
message = 'MY MESSAGE'
|
395
|
+
|
396
|
+
prefab.log.error do
|
397
|
+
message
|
398
|
+
end
|
399
|
+
|
400
|
+
prefab.log.info do
|
401
|
+
raise 'THIS WILL NEVER BE EVALUATED'
|
402
|
+
end
|
403
|
+
|
404
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_logging_with_a_block', message
|
405
|
+
end
|
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
|
+
|
436
|
+
def test_structured_logging
|
437
|
+
prefab, io = captured_logger
|
438
|
+
message = 'HELLO'
|
439
|
+
|
440
|
+
prefab.log.error message, user: "michael", id: 123
|
441
|
+
|
442
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_logging', "#{message} id=123 user=michael"
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_structured_json_logging
|
446
|
+
prefab, io = captured_logger(log_formatter: Prefab::Options::JSON_LOG_FORMATTER)
|
447
|
+
message = 'HELLO'
|
448
|
+
|
449
|
+
prefab.log.error message, user: "michael", id: 123
|
450
|
+
|
451
|
+
log_data = JSON.parse(io.string)
|
452
|
+
assert log_data["message"] == message
|
453
|
+
assert log_data["user"] == "michael"
|
454
|
+
assert log_data["id"] == 123
|
455
|
+
end
|
456
|
+
|
457
|
+
def test_structured_internal_logging
|
458
|
+
prefab, io = captured_logger
|
459
|
+
|
460
|
+
prefab.log.log_internal(::Logger::WARN, 'test', 'cloud.prefab.client.test.path', user: "michael")
|
461
|
+
|
462
|
+
assert_logged io, 'WARN', 'cloud.prefab.client.test.path', "test user=michael"
|
463
|
+
end
|
464
|
+
|
465
|
+
def test_structured_block_logger
|
466
|
+
prefab, io = captured_logger
|
467
|
+
message = 'MY MESSAGE'
|
468
|
+
|
469
|
+
prefab.log.error user: "michael" do
|
470
|
+
message
|
471
|
+
end
|
472
|
+
|
473
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_structured_block_logger', "#{message} user=michael"
|
474
|
+
end
|
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
|
+
|
545
|
+
def test_context_key_threads
|
546
|
+
prefab, io = captured_logger
|
547
|
+
|
548
|
+
threads = []
|
549
|
+
1000.times.map do |i|
|
550
|
+
threads << Thread.new do
|
551
|
+
prefab.with_context({test: {"thread_#{i}": "thread_#{i}"}}) do
|
552
|
+
prefab.log.add_context_keys "test.thread_#{i}"
|
553
|
+
prefab.log.error "UH OH"
|
554
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_context_key_threads',
|
555
|
+
"UH OH test.thread_#{i}=thread_#{i}"
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
threads.each { |thr| thr.join }
|
560
|
+
end
|
561
|
+
|
562
|
+
def test_tagged
|
563
|
+
prefab, io = captured_logger
|
564
|
+
|
565
|
+
prefab.log.tagged([[]]) do # rails sends some of these
|
566
|
+
prefab.log.tagged("outside") do
|
567
|
+
prefab.log.tagged("nested", "tag2") do
|
568
|
+
prefab.log.error "msg"
|
569
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_tagged',
|
570
|
+
'msg log.tags=\["outside", "nested", "tag2"\]'
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
def test_req_tagged
|
577
|
+
prefab, io = captured_logger
|
578
|
+
prefab.log.tagged("tag-1").error "first"
|
579
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_req_tagged',
|
580
|
+
'first req.tags=\["tag-1"\]'
|
581
|
+
reset_io(io)
|
582
|
+
|
583
|
+
prefab.log.tagged("tag-2").error "2nd"
|
584
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_req_tagged',
|
585
|
+
'2nd req.tags=\["tag-1", "tag-2"\]'
|
586
|
+
prefab.log.flush
|
587
|
+
|
588
|
+
prefab.log.tagged("tag-3").error "3rd"
|
589
|
+
assert_logged io, 'ERROR', 'test.test_logger.test_req_tagged',
|
590
|
+
'3rd req.tags=\["tag-3"\]'
|
591
|
+
prefab.log.flush
|
592
|
+
end
|
593
|
+
|
594
|
+
private
|
595
|
+
|
596
|
+
def assert_logged(logged_io, level, path, message)
|
597
|
+
assert_match(/#{level}\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+]?\d+:\s+#{path} #{message}\n/, logged_io.string)
|
598
|
+
end
|
599
|
+
|
600
|
+
def refute_logged(logged_io, message)
|
601
|
+
refute_match(/#{message}/, logged_io.string)
|
602
|
+
end
|
603
|
+
|
604
|
+
def captured_logger(options = {})
|
605
|
+
io = StringIO.new
|
606
|
+
options = Prefab::Options.new(**options.merge(
|
607
|
+
logdev: io,
|
608
|
+
prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
|
609
|
+
))
|
610
|
+
prefab = Prefab::Client.new(options)
|
611
|
+
|
612
|
+
[prefab, io]
|
613
|
+
end
|
614
|
+
|
615
|
+
def reset_io(io)
|
616
|
+
io.close
|
617
|
+
io.reopen
|
618
|
+
|
619
|
+
assert_equal '', io.string
|
620
|
+
end
|
621
|
+
end
|