launchdarkly-server-sdk 6.3.0 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -4
- data/lib/ldclient-rb/config.rb +112 -62
- data/lib/ldclient-rb/context.rb +444 -0
- data/lib/ldclient-rb/evaluation_detail.rb +26 -22
- data/lib/ldclient-rb/events.rb +256 -146
- data/lib/ldclient-rb/flags_state.rb +26 -15
- data/lib/ldclient-rb/impl/big_segments.rb +18 -18
- data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
- data/lib/ldclient-rb/impl/context.rb +96 -0
- data/lib/ldclient-rb/impl/context_filter.rb +145 -0
- data/lib/ldclient-rb/impl/data_source.rb +188 -0
- data/lib/ldclient-rb/impl/data_store.rb +59 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +9 -10
- data/lib/ldclient-rb/impl/evaluator.rb +386 -142
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +40 -41
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +26 -55
- data/lib/ldclient-rb/impl/event_sender.rb +7 -6
- data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
- data/lib/ldclient-rb/impl/event_types.rb +136 -0
- data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +19 -7
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +38 -30
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +24 -11
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +109 -12
- data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
- data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
- data/lib/ldclient-rb/impl/model/clause.rb +45 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +255 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
- data/lib/ldclient-rb/impl/model/segment.rb +132 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +54 -44
- data/lib/ldclient-rb/impl/repeating_task.rb +3 -4
- data/lib/ldclient-rb/impl/sampler.rb +25 -0
- data/lib/ldclient-rb/impl/store_client_wrapper.rb +102 -8
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +2 -2
- data/lib/ldclient-rb/impl/unbounded_pool.rb +1 -1
- data/lib/ldclient-rb/impl/util.rb +59 -1
- data/lib/ldclient-rb/in_memory_store.rb +9 -2
- data/lib/ldclient-rb/integrations/consul.rb +2 -2
- data/lib/ldclient-rb/integrations/dynamodb.rb +2 -2
- data/lib/ldclient-rb/integrations/file_data.rb +4 -4
- data/lib/ldclient-rb/integrations/redis.rb +5 -5
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +287 -62
- data/lib/ldclient-rb/integrations/test_data.rb +18 -14
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +20 -9
- data/lib/ldclient-rb/interfaces.rb +600 -14
- data/lib/ldclient-rb/ldclient.rb +314 -134
- data/lib/ldclient-rb/memoized_value.rb +1 -1
- data/lib/ldclient-rb/migrations.rb +230 -0
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +1 -1
- data/lib/ldclient-rb/polling.rb +52 -6
- data/lib/ldclient-rb/reference.rb +274 -0
- data/lib/ldclient-rb/requestor.rb +9 -11
- data/lib/ldclient-rb/stream.rb +96 -34
- data/lib/ldclient-rb/util.rb +97 -14
- data/lib/ldclient-rb/version.rb +1 -1
- data/lib/ldclient-rb.rb +3 -4
- metadata +65 -23
- data/lib/ldclient-rb/event_summarizer.rb +0 -55
- data/lib/ldclient-rb/file_data_source.rb +0 -23
- data/lib/ldclient-rb/impl/event_factory.rb +0 -126
- data/lib/ldclient-rb/newrelic.rb +0 -17
- data/lib/ldclient-rb/redis_store.rb +0 -88
- data/lib/ldclient-rb/user_filter.rb +0 -52
data/lib/ldclient-rb/events.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
require "ldclient-rb/impl/context_filter"
|
1
2
|
require "ldclient-rb/impl/diagnostic_events"
|
2
3
|
require "ldclient-rb/impl/event_sender"
|
4
|
+
require "ldclient-rb/impl/event_summarizer"
|
5
|
+
require "ldclient-rb/impl/event_types"
|
3
6
|
require "ldclient-rb/impl/util"
|
4
7
|
|
5
8
|
require "concurrent"
|
@@ -18,7 +21,7 @@ require "time"
|
|
18
21
|
# On a separate worker thread, EventDispatcher consumes events from the inbox. These are considered
|
19
22
|
# "input events" because they may or may not actually be sent to LaunchDarkly; most flag evaluation
|
20
23
|
# events are not sent, but are counted and the counters become part of a single summary event.
|
21
|
-
# EventDispatcher updates those counters, creates "index" events for any
|
24
|
+
# EventDispatcher updates those counters, creates "index" events for any contexts that have not been seen
|
22
25
|
# recently, and places any events that will be sent to LaunchDarkly into the "outbox" queue.
|
23
26
|
#
|
24
27
|
# When it is time to flush events to LaunchDarkly, the contents of the outbox are handed off to
|
@@ -26,16 +29,35 @@ require "time"
|
|
26
29
|
#
|
27
30
|
|
28
31
|
module LaunchDarkly
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
module EventProcessorMethods
|
33
|
+
def record_eval_event(
|
34
|
+
context,
|
35
|
+
key,
|
36
|
+
version = nil,
|
37
|
+
variation = nil,
|
38
|
+
value = nil,
|
39
|
+
reason = nil,
|
40
|
+
default = nil,
|
41
|
+
track_events = false,
|
42
|
+
debug_until = nil,
|
43
|
+
prereq_of = nil,
|
44
|
+
sampling_ratio = nil,
|
45
|
+
exclude_from_summaries = false
|
46
|
+
)
|
47
|
+
end
|
32
48
|
|
33
|
-
|
34
|
-
|
49
|
+
def record_identify_event(context)
|
50
|
+
end
|
35
51
|
|
36
|
-
|
37
|
-
|
38
|
-
|
52
|
+
def record_custom_event(
|
53
|
+
context,
|
54
|
+
key,
|
55
|
+
data = nil,
|
56
|
+
metric_value = nil
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def record_migration_op_event(event)
|
39
61
|
end
|
40
62
|
|
41
63
|
def flush
|
@@ -45,12 +67,12 @@ module LaunchDarkly
|
|
45
67
|
end
|
46
68
|
end
|
47
69
|
|
70
|
+
MAX_FLUSH_WORKERS = 5
|
71
|
+
private_constant :MAX_FLUSH_WORKERS
|
72
|
+
|
48
73
|
# @private
|
49
|
-
class
|
50
|
-
|
51
|
-
@event = event
|
52
|
-
end
|
53
|
-
attr_reader :event
|
74
|
+
class NullEventProcessor
|
75
|
+
include EventProcessorMethods
|
54
76
|
end
|
55
77
|
|
56
78
|
# @private
|
@@ -58,7 +80,7 @@ module LaunchDarkly
|
|
58
80
|
end
|
59
81
|
|
60
82
|
# @private
|
61
|
-
class
|
83
|
+
class FlushContextsMessage
|
62
84
|
end
|
63
85
|
|
64
86
|
# @private
|
@@ -70,7 +92,7 @@ module LaunchDarkly
|
|
70
92
|
def initialize
|
71
93
|
@reply = Concurrent::Semaphore.new(0)
|
72
94
|
end
|
73
|
-
|
95
|
+
|
74
96
|
def completed
|
75
97
|
@reply.release
|
76
98
|
end
|
@@ -90,6 +112,8 @@ module LaunchDarkly
|
|
90
112
|
|
91
113
|
# @private
|
92
114
|
class EventProcessor
|
115
|
+
include EventProcessorMethods
|
116
|
+
|
93
117
|
def initialize(sdk_key, config, client = nil, diagnostic_accumulator = nil, test_properties = nil)
|
94
118
|
raise ArgumentError, "sdk_key must not be nil" if sdk_key.nil? # see LDClient constructor comment on sdk_key
|
95
119
|
@logger = config.logger
|
@@ -98,10 +122,10 @@ module LaunchDarkly
|
|
98
122
|
post_to_inbox(FlushMessage.new)
|
99
123
|
end
|
100
124
|
@flush_task.execute
|
101
|
-
@
|
102
|
-
post_to_inbox(
|
125
|
+
@contexts_flush_task = Concurrent::TimerTask.new(execution_interval: config.context_keys_flush_interval) do
|
126
|
+
post_to_inbox(FlushContextsMessage.new)
|
103
127
|
end
|
104
|
-
@
|
128
|
+
@contexts_flush_task.execute
|
105
129
|
if !diagnostic_accumulator.nil?
|
106
130
|
interval = test_properties && test_properties.has_key?(:diagnostic_recording_interval) ?
|
107
131
|
test_properties[:diagnostic_recording_interval] :
|
@@ -116,16 +140,42 @@ module LaunchDarkly
|
|
116
140
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
117
141
|
@inbox_full = Concurrent::AtomicBoolean.new(false)
|
118
142
|
|
119
|
-
event_sender = test_properties
|
120
|
-
|
121
|
-
|
143
|
+
event_sender = (test_properties || {})[:event_sender] ||
|
144
|
+
Impl::EventSender.new(sdk_key, config, client || Util.new_http_client(config.events_uri, config))
|
145
|
+
|
146
|
+
@timestamp_fn = (test_properties || {})[:timestamp_fn] || proc { Impl::Util.current_time_millis }
|
122
147
|
|
123
148
|
EventDispatcher.new(@inbox, sdk_key, config, diagnostic_accumulator, event_sender)
|
124
149
|
end
|
125
150
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
151
|
+
def record_eval_event(
|
152
|
+
context,
|
153
|
+
key,
|
154
|
+
version = nil,
|
155
|
+
variation = nil,
|
156
|
+
value = nil,
|
157
|
+
reason = nil,
|
158
|
+
default = nil,
|
159
|
+
track_events = false,
|
160
|
+
debug_until = nil,
|
161
|
+
prereq_of = nil,
|
162
|
+
sampling_ratio = nil,
|
163
|
+
exclude_from_summaries = false
|
164
|
+
)
|
165
|
+
post_to_inbox(LaunchDarkly::Impl::EvalEvent.new(timestamp, context, key, version, variation, value, reason,
|
166
|
+
default, track_events, debug_until, prereq_of, sampling_ratio, exclude_from_summaries))
|
167
|
+
end
|
168
|
+
|
169
|
+
def record_identify_event(context)
|
170
|
+
post_to_inbox(LaunchDarkly::Impl::IdentifyEvent.new(timestamp, context))
|
171
|
+
end
|
172
|
+
|
173
|
+
def record_custom_event(context, key, data = nil, metric_value = nil)
|
174
|
+
post_to_inbox(LaunchDarkly::Impl::CustomEvent.new(timestamp, context, key, data, metric_value))
|
175
|
+
end
|
176
|
+
|
177
|
+
def record_migration_op_event(event)
|
178
|
+
post_to_inbox(event)
|
129
179
|
end
|
130
180
|
|
131
181
|
def flush
|
@@ -137,8 +187,8 @@ module LaunchDarkly
|
|
137
187
|
# final shutdown, which includes a final flush, is done synchronously
|
138
188
|
if @stopped.make_true
|
139
189
|
@flush_task.shutdown
|
140
|
-
@
|
141
|
-
@diagnostic_event_task.shutdown
|
190
|
+
@contexts_flush_task.shutdown
|
191
|
+
@diagnostic_event_task.shutdown unless @diagnostic_event_task.nil?
|
142
192
|
# Note that here we are not calling post_to_inbox, because we *do* want to wait if the inbox
|
143
193
|
# is full; an orderly shutdown can't happen unless these messages are received.
|
144
194
|
@inbox << FlushMessage.new
|
@@ -155,9 +205,11 @@ module LaunchDarkly
|
|
155
205
|
sync_msg.wait_for_completion
|
156
206
|
end
|
157
207
|
|
158
|
-
private
|
208
|
+
private def timestamp
|
209
|
+
@timestamp_fn.call()
|
210
|
+
end
|
159
211
|
|
160
|
-
def post_to_inbox(message)
|
212
|
+
private def post_to_inbox(message)
|
161
213
|
begin
|
162
214
|
@inbox.push(message, non_block=true)
|
163
215
|
rescue ThreadError
|
@@ -179,14 +231,15 @@ module LaunchDarkly
|
|
179
231
|
@config = config
|
180
232
|
@diagnostic_accumulator = config.diagnostic_opt_out? ? nil : diagnostic_accumulator
|
181
233
|
@event_sender = event_sender
|
234
|
+
@sampler = LaunchDarkly::Impl::Sampler.new(Random.new)
|
182
235
|
|
183
|
-
@
|
236
|
+
@context_keys = SimpleLRUCacheSet.new(config.context_keys_capacity)
|
184
237
|
@formatter = EventOutputFormatter.new(config)
|
185
238
|
@disabled = Concurrent::AtomicBoolean.new(false)
|
186
239
|
@last_known_past_time = Concurrent::AtomicReference.new(0)
|
187
|
-
@
|
240
|
+
@deduplicated_contexts = 0
|
188
241
|
@events_in_last_batch = 0
|
189
|
-
|
242
|
+
|
190
243
|
outbox = EventBuffer.new(config.capacity, config.logger)
|
191
244
|
flush_workers = NonBlockingThreadPool.new(MAX_FLUSH_WORKERS)
|
192
245
|
|
@@ -209,12 +262,10 @@ module LaunchDarkly
|
|
209
262
|
begin
|
210
263
|
message = inbox.pop
|
211
264
|
case message
|
212
|
-
when EventMessage
|
213
|
-
dispatch_event(message.event, outbox)
|
214
265
|
when FlushMessage
|
215
266
|
trigger_flush(outbox, flush_workers)
|
216
|
-
when
|
217
|
-
@
|
267
|
+
when FlushContextsMessage
|
268
|
+
@context_keys.clear
|
218
269
|
when DiagnosticEventMessage
|
219
270
|
send_and_reset_diagnostics(outbox, diagnostic_event_workers)
|
220
271
|
when TestSyncMessage
|
@@ -224,6 +275,8 @@ module LaunchDarkly
|
|
224
275
|
do_shutdown(flush_workers, diagnostic_event_workers)
|
225
276
|
running = false
|
226
277
|
message.completed
|
278
|
+
else
|
279
|
+
dispatch_event(message, outbox)
|
227
280
|
end
|
228
281
|
rescue => e
|
229
282
|
Util.log_exception(@config.logger, "Unexpected error in event processor", e)
|
@@ -234,7 +287,7 @@ module LaunchDarkly
|
|
234
287
|
def do_shutdown(flush_workers, diagnostic_event_workers)
|
235
288
|
flush_workers.shutdown
|
236
289
|
flush_workers.wait_for_termination
|
237
|
-
|
290
|
+
unless diagnostic_event_workers.nil?
|
238
291
|
diagnostic_event_workers.shutdown
|
239
292
|
diagnostic_event_workers.wait_for_termination
|
240
293
|
end
|
@@ -244,58 +297,52 @@ module LaunchDarkly
|
|
244
297
|
def synchronize_for_testing(flush_workers, diagnostic_event_workers)
|
245
298
|
# Used only by unit tests. Wait until all active flush workers have finished.
|
246
299
|
flush_workers.wait_all
|
247
|
-
diagnostic_event_workers.wait_all
|
300
|
+
diagnostic_event_workers.wait_all unless diagnostic_event_workers.nil?
|
248
301
|
end
|
249
302
|
|
250
303
|
def dispatch_event(event, outbox)
|
251
304
|
return if @disabled.value
|
252
305
|
|
253
306
|
# Always record the event in the summary.
|
254
|
-
outbox.add_to_summary(event)
|
307
|
+
outbox.add_to_summary(event) unless event.exclude_from_summaries
|
255
308
|
|
256
309
|
# Decide whether to add the event to the payload. Feature events may be added twice, once for
|
257
310
|
# the event (if tracked) and once for debugging.
|
258
311
|
will_add_full_event = false
|
259
312
|
debug_event = nil
|
260
|
-
if event
|
261
|
-
will_add_full_event = event
|
313
|
+
if event.is_a?(LaunchDarkly::Impl::EvalEvent)
|
314
|
+
will_add_full_event = event.track_events
|
262
315
|
if should_debug_event(event)
|
263
|
-
debug_event = event
|
264
|
-
debug_event[:debug] = true
|
316
|
+
debug_event = LaunchDarkly::Impl::DebugEvent.new(event)
|
265
317
|
end
|
266
318
|
else
|
267
319
|
will_add_full_event = true
|
268
320
|
end
|
269
321
|
|
270
|
-
# For each
|
271
|
-
# an identify event for that
|
272
|
-
if !(
|
273
|
-
|
274
|
-
outbox.add_event({
|
275
|
-
kind: "index",
|
276
|
-
creationDate: event[:creationDate],
|
277
|
-
user: event[:user]
|
278
|
-
})
|
279
|
-
end
|
322
|
+
# For each context we haven't seen before, we add an index event - unless this is already
|
323
|
+
# an identify event for that context.
|
324
|
+
if !event.context.nil? && !notice_context(event.context) && !event.is_a?(LaunchDarkly::Impl::IdentifyEvent) && !event.is_a?(LaunchDarkly::Impl::MigrationOpEvent)
|
325
|
+
outbox.add_event(LaunchDarkly::Impl::IndexEvent.new(event.timestamp, event.context))
|
280
326
|
end
|
281
327
|
|
282
|
-
outbox.add_event(event) if will_add_full_event
|
283
|
-
outbox.add_event(debug_event) if !debug_event.nil?
|
328
|
+
outbox.add_event(event) if will_add_full_event && @sampler.sample(event.sampling_ratio.nil? ? 1 : event.sampling_ratio)
|
329
|
+
outbox.add_event(debug_event) if !debug_event.nil? && @sampler.sample(event.sampling_ratio.nil? ? 1 : event.sampling_ratio)
|
284
330
|
end
|
285
331
|
|
286
|
-
#
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
332
|
+
#
|
333
|
+
# Add to the set of contexts we've noticed, and return true if the context
|
334
|
+
# was already known to us.
|
335
|
+
# @param context [LaunchDarkly::LDContext]
|
336
|
+
# @return [Boolean]
|
337
|
+
#
|
338
|
+
def notice_context(context)
|
339
|
+
known = @context_keys.add(context.fully_qualified_key)
|
340
|
+
@deduplicated_contexts += 1 if known
|
341
|
+
known
|
295
342
|
end
|
296
343
|
|
297
344
|
def should_debug_event(event)
|
298
|
-
debug_until = event
|
345
|
+
debug_until = event.debug_until
|
299
346
|
if !debug_until.nil?
|
300
347
|
last_past = @last_known_past_time.value
|
301
348
|
debug_until > last_past && debug_until > Impl::Util.current_time_millis
|
@@ -309,7 +356,7 @@ module LaunchDarkly
|
|
309
356
|
return
|
310
357
|
end
|
311
358
|
|
312
|
-
payload = outbox.get_payload
|
359
|
+
payload = outbox.get_payload
|
313
360
|
if !payload.events.empty? || !payload.summary.counters.empty?
|
314
361
|
count = payload.events.length + (payload.summary.counters.empty? ? 0 : 1)
|
315
362
|
@events_in_last_batch = count
|
@@ -319,7 +366,7 @@ module LaunchDarkly
|
|
319
366
|
events_out = @formatter.make_output_events(payload.events, payload.summary)
|
320
367
|
result = @event_sender.send_event_data(events_out.to_json, "#{events_out.length} events", false)
|
321
368
|
@disabled.value = true if result.must_shutdown
|
322
|
-
|
369
|
+
unless result.time_from_server.nil?
|
323
370
|
@last_known_past_time.value = (result.time_from_server.to_f * 1000).to_i
|
324
371
|
end
|
325
372
|
rescue => e
|
@@ -335,8 +382,8 @@ module LaunchDarkly
|
|
335
382
|
def send_and_reset_diagnostics(outbox, diagnostic_event_workers)
|
336
383
|
return if @diagnostic_accumulator.nil?
|
337
384
|
dropped_count = outbox.get_and_clear_dropped_count
|
338
|
-
event = @diagnostic_accumulator.create_periodic_event_and_reset(dropped_count, @
|
339
|
-
@
|
385
|
+
event = @diagnostic_accumulator.create_periodic_event_and_reset(dropped_count, @deduplicated_contexts, @events_in_last_batch)
|
386
|
+
@deduplicated_contexts = 0
|
340
387
|
@events_in_last_batch = 0
|
341
388
|
send_diagnostic_event(event, diagnostic_event_workers)
|
342
389
|
end
|
@@ -365,17 +412,16 @@ module LaunchDarkly
|
|
365
412
|
@capacity_exceeded = false
|
366
413
|
@dropped_events = 0
|
367
414
|
@events = []
|
368
|
-
@summarizer = EventSummarizer.new
|
415
|
+
@summarizer = LaunchDarkly::Impl::EventSummarizer.new
|
369
416
|
end
|
370
417
|
|
371
418
|
def add_event(event)
|
372
419
|
if @events.length < @capacity
|
373
|
-
@logger.debug { "[LDClient] Enqueueing event: #{event.to_json}" }
|
374
420
|
@events.push(event)
|
375
421
|
@capacity_exceeded = false
|
376
422
|
else
|
377
423
|
@dropped_events += 1
|
378
|
-
|
424
|
+
unless @capacity_exceeded
|
379
425
|
@capacity_exceeded = true
|
380
426
|
@logger.warn { "[LDClient] Exceeded event queue capacity. Increase capacity to avoid dropping events." }
|
381
427
|
end
|
@@ -387,7 +433,7 @@ module LaunchDarkly
|
|
387
433
|
end
|
388
434
|
|
389
435
|
def get_payload
|
390
|
-
|
436
|
+
FlushPayload.new(@events, @summarizer.snapshot)
|
391
437
|
end
|
392
438
|
|
393
439
|
def get_and_clear_dropped_count
|
@@ -404,113 +450,177 @@ module LaunchDarkly
|
|
404
450
|
|
405
451
|
# @private
|
406
452
|
class EventOutputFormatter
|
453
|
+
FEATURE_KIND = 'feature'
|
454
|
+
IDENTIFY_KIND = 'identify'
|
455
|
+
CUSTOM_KIND = 'custom'
|
456
|
+
INDEX_KIND = 'index'
|
457
|
+
DEBUG_KIND = 'debug'
|
458
|
+
MIGRATION_OP_KIND = 'migration_op'
|
459
|
+
SUMMARY_KIND = 'summary'
|
460
|
+
|
407
461
|
def initialize(config)
|
408
|
-
@
|
409
|
-
@user_filter = UserFilter.new(config)
|
462
|
+
@context_filter = LaunchDarkly::Impl::ContextFilter.new(config.all_attributes_private, config.private_attributes)
|
410
463
|
end
|
411
464
|
|
412
465
|
# Transforms events into the format used for event sending.
|
413
466
|
def make_output_events(events, summary)
|
414
467
|
events_out = events.map { |e| make_output_event(e) }
|
415
|
-
|
468
|
+
unless summary.counters.empty?
|
416
469
|
events_out.push(make_summary_event(summary))
|
417
470
|
end
|
418
471
|
events_out
|
419
472
|
end
|
420
473
|
|
421
|
-
private
|
474
|
+
private def make_output_event(event)
|
475
|
+
case event
|
422
476
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
477
|
+
when LaunchDarkly::Impl::EvalEvent
|
478
|
+
out = {
|
479
|
+
kind: FEATURE_KIND,
|
480
|
+
creationDate: event.timestamp,
|
481
|
+
key: event.key,
|
482
|
+
value: event.value,
|
483
|
+
}
|
484
|
+
out[:default] = event.default unless event.default.nil?
|
485
|
+
out[:variation] = event.variation unless event.variation.nil?
|
486
|
+
out[:version] = event.version unless event.version.nil?
|
487
|
+
out[:prereqOf] = event.prereq_of unless event.prereq_of.nil?
|
488
|
+
out[:contextKeys] = event.context.keys
|
489
|
+
out[:reason] = event.reason unless event.reason.nil?
|
490
|
+
out
|
427
491
|
|
428
|
-
|
429
|
-
case event[:kind]
|
430
|
-
when "feature"
|
431
|
-
is_debug = event[:debug]
|
492
|
+
when LaunchDarkly::Impl::MigrationOpEvent
|
432
493
|
out = {
|
433
|
-
kind:
|
434
|
-
creationDate: event
|
435
|
-
|
436
|
-
|
494
|
+
kind: MIGRATION_OP_KIND,
|
495
|
+
creationDate: event.timestamp,
|
496
|
+
contextKeys: event.context.keys,
|
497
|
+
operation: event.operation.to_s,
|
498
|
+
evaluation: {
|
499
|
+
key: event.key,
|
500
|
+
value: event.evaluation.value,
|
501
|
+
},
|
437
502
|
}
|
438
|
-
|
439
|
-
out[:
|
440
|
-
out[:
|
441
|
-
out[:
|
442
|
-
out[:
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
503
|
+
|
504
|
+
out[:evaluation][:version] = event.version unless event.version.nil?
|
505
|
+
out[:evaluation][:default] = event.default unless event.default.nil?
|
506
|
+
out[:evaluation][:variation] = event.evaluation.variation_index unless event.evaluation.variation_index.nil?
|
507
|
+
out[:evaluation][:reason] = event.evaluation.reason unless event.evaluation.reason.nil?
|
508
|
+
out[:samplingRatio] = event.sampling_ratio unless event.sampling_ratio.nil? || event.sampling_ratio == 1
|
509
|
+
|
510
|
+
measurements = []
|
511
|
+
|
512
|
+
unless event.invoked.empty?
|
513
|
+
measurements << {
|
514
|
+
"key": "invoked",
|
515
|
+
"values": event.invoked.map { |origin| [origin, true] }.to_h,
|
516
|
+
}
|
517
|
+
end
|
518
|
+
|
519
|
+
unless event.consistency_check.nil?
|
520
|
+
measurement = {
|
521
|
+
"key": "consistent",
|
522
|
+
"value": event.consistency_check,
|
523
|
+
}
|
524
|
+
|
525
|
+
unless event.consistency_check_ratio.nil? || event.consistency_check_ratio == 1
|
526
|
+
measurement[:samplingRatio] = event.consistency_check_ratio
|
527
|
+
end
|
528
|
+
|
529
|
+
measurements << measurement
|
530
|
+
end
|
531
|
+
|
532
|
+
|
533
|
+
unless event.latencies.empty?
|
534
|
+
measurements << {
|
535
|
+
"key": "latency_ms",
|
536
|
+
"values": event.latencies,
|
537
|
+
}
|
447
538
|
end
|
448
|
-
|
539
|
+
|
540
|
+
unless event.errors.empty?
|
541
|
+
measurements << {
|
542
|
+
"key": "error",
|
543
|
+
"values": event.errors.map { |origin| [origin, true] }.to_h,
|
544
|
+
}
|
545
|
+
end
|
546
|
+
out[:measurements] = measurements unless measurements.empty?
|
547
|
+
|
449
548
|
out
|
450
|
-
|
549
|
+
|
550
|
+
when LaunchDarkly::Impl::IdentifyEvent
|
451
551
|
{
|
452
|
-
kind:
|
453
|
-
creationDate: event
|
454
|
-
key: event
|
455
|
-
|
552
|
+
kind: IDENTIFY_KIND,
|
553
|
+
creationDate: event.timestamp,
|
554
|
+
key: event.context.fully_qualified_key,
|
555
|
+
context: @context_filter.filter(event.context),
|
456
556
|
}
|
457
|
-
|
557
|
+
|
558
|
+
when LaunchDarkly::Impl::CustomEvent
|
458
559
|
out = {
|
459
|
-
kind:
|
460
|
-
creationDate: event
|
461
|
-
key: event
|
560
|
+
kind: CUSTOM_KIND,
|
561
|
+
creationDate: event.timestamp,
|
562
|
+
key: event.key,
|
462
563
|
}
|
463
|
-
out[:data] = event
|
464
|
-
|
465
|
-
|
466
|
-
else
|
467
|
-
out[:userKey] = event[:user][:key]
|
468
|
-
end
|
469
|
-
out[:metricValue] = event[:metricValue] if event.has_key?(:metricValue)
|
470
|
-
out[:contextKind] = event[:contextKind] if event.has_key?(:contextKind)
|
564
|
+
out[:data] = event.data unless event.data.nil?
|
565
|
+
out[:contextKeys] = event.context.keys
|
566
|
+
out[:metricValue] = event.metric_value unless event.metric_value.nil?
|
471
567
|
out
|
472
|
-
|
568
|
+
|
569
|
+
when LaunchDarkly::Impl::IndexEvent
|
473
570
|
{
|
474
|
-
kind:
|
475
|
-
creationDate: event
|
476
|
-
|
571
|
+
kind: INDEX_KIND,
|
572
|
+
creationDate: event.timestamp,
|
573
|
+
context: @context_filter.filter(event.context),
|
477
574
|
}
|
575
|
+
|
576
|
+
when LaunchDarkly::Impl::DebugEvent
|
577
|
+
original = event.eval_event
|
578
|
+
out = {
|
579
|
+
kind: DEBUG_KIND,
|
580
|
+
creationDate: original.timestamp,
|
581
|
+
key: original.key,
|
582
|
+
context: @context_filter.filter(original.context),
|
583
|
+
value: original.value,
|
584
|
+
}
|
585
|
+
out[:default] = original.default unless original.default.nil?
|
586
|
+
out[:variation] = original.variation unless original.variation.nil?
|
587
|
+
out[:version] = original.version unless original.version.nil?
|
588
|
+
out[:prereqOf] = original.prereq_of unless original.prereq_of.nil?
|
589
|
+
out[:reason] = original.reason unless original.reason.nil?
|
590
|
+
out
|
591
|
+
|
478
592
|
else
|
479
|
-
|
593
|
+
nil
|
480
594
|
end
|
481
595
|
end
|
482
596
|
|
483
597
|
# Transforms the summary data into the format used for event sending.
|
484
|
-
def make_summary_event(summary)
|
598
|
+
private def make_summary_event(summary)
|
485
599
|
flags = {}
|
486
|
-
summary
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
if ckey[:version].nil?
|
503
|
-
c[:unknown] = true
|
504
|
-
else
|
505
|
-
c[:version] = ckey[:version]
|
600
|
+
summary.counters.each do |flagKey, flagInfo|
|
601
|
+
counters = []
|
602
|
+
flagInfo.versions.each do |version, variations|
|
603
|
+
variations.each do |variation, counter|
|
604
|
+
c = {
|
605
|
+
value: counter.value,
|
606
|
+
count: counter.count,
|
607
|
+
}
|
608
|
+
c[:variation] = variation unless variation.nil?
|
609
|
+
if version.nil?
|
610
|
+
c[:unknown] = true
|
611
|
+
else
|
612
|
+
c[:version] = version
|
613
|
+
end
|
614
|
+
counters.push(c)
|
615
|
+
end
|
506
616
|
end
|
507
|
-
|
508
|
-
|
617
|
+
flags[flagKey] = { default: flagInfo.default, counters: counters, contextKinds: flagInfo.context_kinds.to_a }
|
618
|
+
end
|
509
619
|
{
|
510
|
-
kind:
|
620
|
+
kind: SUMMARY_KIND,
|
511
621
|
startDate: summary[:start_date],
|
512
622
|
endDate: summary[:end_date],
|
513
|
-
features: flags
|
623
|
+
features: flags,
|
514
624
|
}
|
515
625
|
end
|
516
626
|
end
|