launchdarkly-server-sdk 6.3.0 → 8.0.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/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
|