smplkit 3.0.134 → 3.0.136
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/lib/smplkit/client.rb +27 -27
- data/lib/smplkit/config/client.rb +44 -41
- data/lib/smplkit/event_stream.rb +483 -0
- data/lib/smplkit/flags/client.rb +45 -42
- data/lib/smplkit/jobs/client.rb +2 -2
- data/lib/smplkit/jobs/models.rb +2 -2
- data/lib/smplkit/logging/client.rb +69 -59
- data/lib/smplkit/transport.rb +1 -1
- data/lib/smplkit/version.rb +2 -2
- data/lib/smplkit.rb +1 -1
- metadata +2 -22
- data/lib/smplkit/ws.rb +0 -268
data/lib/smplkit/flags/client.rb
CHANGED
|
@@ -18,18 +18,18 @@ require "digest"
|
|
|
18
18
|
# +json_flag+) whose +.get+ evaluates against the cached definitions, plus
|
|
19
19
|
# +refresh+ / +stats+ / +on_change+. The first live call transparently flushes
|
|
20
20
|
# discovery, fetches all flag definitions into the local cache, and opens the
|
|
21
|
-
# live-updates
|
|
21
|
+
# live-updates event stream — no explicit install step.
|
|
22
22
|
#
|
|
23
23
|
# The client supports two construction shapes:
|
|
24
24
|
#
|
|
25
25
|
# * *Wired* into +Smplkit::Client+ — borrows the parent's flags transport for
|
|
26
|
-
# both runtime fetch and CRUD, the parent's shared
|
|
26
|
+
# both runtime fetch and CRUD, the parent's shared event stream for the live
|
|
27
27
|
# channel, and +client.platform.contexts+ for evaluation-context
|
|
28
28
|
# registration. This is the common path.
|
|
29
29
|
# * *Standalone* — +FlagsClient.new(api_key: ..., base_url: ..., ...)+ builds
|
|
30
30
|
# and owns its own flags transport and a contexts client (against its own app
|
|
31
|
-
# transport), and on first live use opens and owns its own
|
|
32
|
-
# tears down only the owned transports and owned
|
|
31
|
+
# transport), and on first live use opens and owns its own event stream.
|
|
32
|
+
# +close+ tears down only the owned transports and owned event stream.
|
|
33
33
|
module Smplkit
|
|
34
34
|
module Flags
|
|
35
35
|
# Describes a flag definition change. Frozen — fields are set at construction.
|
|
@@ -37,7 +37,7 @@ module Smplkit
|
|
|
37
37
|
# @!attribute [r] id
|
|
38
38
|
# @return [String] id of the flag whose definition changed.
|
|
39
39
|
# @!attribute [r] source
|
|
40
|
-
# @return [String] origin of the change (e.g. +"
|
|
40
|
+
# @return [String] origin of the change (e.g. +"push"+ for a live
|
|
41
41
|
# update or +"manual"+ for a refresh).
|
|
42
42
|
# @!attribute [r] deleted
|
|
43
43
|
# @return [Boolean] whether the change was a deletion of the flag.
|
|
@@ -148,7 +148,7 @@ module Smplkit
|
|
|
148
148
|
# defaults). +environment+/+service+ resolve the same way (constructor
|
|
149
149
|
# argument wins). The app transport backs the standalone contexts client
|
|
150
150
|
# (evaluation-context registration); the app base URL is returned so a
|
|
151
|
-
# standalone client can open its own
|
|
151
|
+
# standalone client can open its own event stream against the app service.
|
|
152
152
|
#
|
|
153
153
|
# @api private
|
|
154
154
|
def self.flags_transport(api_key:, base_url:, profile:, base_domain:, scheme:,
|
|
@@ -186,8 +186,8 @@ module Smplkit
|
|
|
186
186
|
# is pure CRUD. The live surface (+boolean_flag+ / +string_flag+ /
|
|
187
187
|
# +number_flag+ / +json_flag+ / +refresh+ / +stats+ / +on_change+) connects
|
|
188
188
|
# lazily on first use — the first call flushes discovery, fetches all flag
|
|
189
|
-
# definitions into the local cache, and opens the live-updates
|
|
190
|
-
# explicit install step is required.
|
|
189
|
+
# definitions into the local cache, and opens the live-updates event
|
|
190
|
+
# stream. No explicit install step is required.
|
|
191
191
|
class FlagsClient
|
|
192
192
|
# @param api_key [String, nil] API key. When omitted, resolved from
|
|
193
193
|
# +SMPLKIT_API_KEY+ or +~/.smplkit+.
|
|
@@ -207,8 +207,8 @@ module Smplkit
|
|
|
207
207
|
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
208
208
|
# @param extra_headers [Hash{String => String}, nil] Extra headers
|
|
209
209
|
# attached to every request.
|
|
210
|
-
# @param streaming [Boolean] Live updates over
|
|
211
|
-
# +true+): the first live call opens a shared
|
|
210
|
+
# @param streaming [Boolean] Live updates over the event stream (default
|
|
211
|
+
# +true+): the first live call opens a shared stream and flag changes
|
|
212
212
|
# stream in. Set +false+ for the stateless read-through surface: the
|
|
213
213
|
# first live call still fetches all flag definitions once (blocking),
|
|
214
214
|
# evaluation stays local, +refresh+ re-fetches on demand, and NO socket
|
|
@@ -263,13 +263,13 @@ module Smplkit
|
|
|
263
263
|
# Live-surface state.
|
|
264
264
|
@flag_store = {}
|
|
265
265
|
@connected = false
|
|
266
|
-
@
|
|
266
|
+
@stream_subscribed = false
|
|
267
267
|
@cache = ResolutionCache.new
|
|
268
268
|
@handles = {}
|
|
269
269
|
@global_listeners = []
|
|
270
270
|
@key_listeners = Hash.new { |h, k| h[k] = [] }
|
|
271
|
-
@
|
|
272
|
-
@
|
|
271
|
+
@event_stream = nil
|
|
272
|
+
@owns_stream = false
|
|
273
273
|
@lock = Mutex.new
|
|
274
274
|
end
|
|
275
275
|
|
|
@@ -566,16 +566,16 @@ module Smplkit
|
|
|
566
566
|
|
|
567
567
|
# Release resources — only those this client owns.
|
|
568
568
|
#
|
|
569
|
-
# Tears down the owned
|
|
570
|
-
# borrows the parent's transport,
|
|
569
|
+
# Tears down the owned event stream (standalone install). A wired client
|
|
570
|
+
# borrows the parent's transport, event stream, and contexts client and
|
|
571
571
|
# closes none of them.
|
|
572
572
|
#
|
|
573
573
|
# @return [void]
|
|
574
574
|
def close
|
|
575
|
-
if @
|
|
576
|
-
@
|
|
577
|
-
@
|
|
578
|
-
@
|
|
575
|
+
if @owns_stream && @event_stream
|
|
576
|
+
@event_stream.stop
|
|
577
|
+
@event_stream = nil
|
|
578
|
+
@owns_stream = false
|
|
579
579
|
end
|
|
580
580
|
# Owned flags/app transports (standalone construction) release their
|
|
581
581
|
# Faraday connections on GC; there is no explicit shutdown to call.
|
|
@@ -680,29 +680,31 @@ module Smplkit
|
|
|
680
680
|
end
|
|
681
681
|
|
|
682
682
|
# ----------------------------------------------------------------
|
|
683
|
-
# Live surface: lazy connect + transport /
|
|
683
|
+
# Live surface: lazy connect + transport / event stream helpers
|
|
684
684
|
# ----------------------------------------------------------------
|
|
685
685
|
|
|
686
|
-
def
|
|
687
|
-
return @parent.
|
|
686
|
+
def ensure_event_stream
|
|
687
|
+
return @parent._ensure_event_stream unless @parent.nil?
|
|
688
688
|
|
|
689
|
-
if @
|
|
690
|
-
@
|
|
689
|
+
if @event_stream.nil?
|
|
690
|
+
@event_stream = EventStream.new(
|
|
691
691
|
app_base_url: @app_base_url, api_key: @standalone_api_key, metrics: @metrics
|
|
692
692
|
)
|
|
693
|
-
@
|
|
694
|
-
@
|
|
693
|
+
@event_stream.start
|
|
694
|
+
@owns_stream = true
|
|
695
695
|
end
|
|
696
|
-
@
|
|
696
|
+
@event_stream
|
|
697
697
|
end
|
|
698
698
|
|
|
699
699
|
# Open the live connection to the running Smpl Flags service.
|
|
700
700
|
#
|
|
701
701
|
# Flushes any buffered discovery declarations, fetches all flag
|
|
702
|
-
# definitions into the local cache, opens the shared
|
|
702
|
+
# definitions into the local cache, opens the shared event stream, and
|
|
703
703
|
# subscribes to +flag_changed+ / +flag_deleted+ / +flags_changed+ events.
|
|
704
|
-
#
|
|
705
|
-
#
|
|
704
|
+
# Also registers the bulk-refresh path as the stream's reconnect refetch,
|
|
705
|
+
# so a stream outage ends with a full re-sync. In stateless mode
|
|
706
|
+
# (+streaming: false+) no stream is ever created; +refresh+ re-fetches on
|
|
707
|
+
# demand.
|
|
706
708
|
#
|
|
707
709
|
# Idempotent and internal — every live method calls it on first use, so
|
|
708
710
|
# the live surface auto-connects with no explicit step.
|
|
@@ -723,13 +725,14 @@ module Smplkit
|
|
|
723
725
|
@connected = true
|
|
724
726
|
return unless @streaming
|
|
725
727
|
|
|
726
|
-
@
|
|
727
|
-
return if @
|
|
728
|
+
@event_stream = ensure_event_stream
|
|
729
|
+
return if @stream_subscribed
|
|
728
730
|
|
|
729
|
-
@
|
|
730
|
-
@
|
|
731
|
-
@
|
|
732
|
-
@
|
|
731
|
+
@event_stream.on("flag_changed") { |data| handle_flag_changed(data) }
|
|
732
|
+
@event_stream.on("flag_deleted") { |data| handle_flag_deleted(data) }
|
|
733
|
+
@event_stream.on("flags_changed") { |data| handle_flags_changed(data) }
|
|
734
|
+
@event_stream.on_reconnect { handle_flags_changed({}) }
|
|
735
|
+
@stream_subscribed = true
|
|
733
736
|
end
|
|
734
737
|
|
|
735
738
|
def do_refresh(_source)
|
|
@@ -759,7 +762,7 @@ module Smplkit
|
|
|
759
762
|
end
|
|
760
763
|
|
|
761
764
|
# ----------------------------------------------------------------
|
|
762
|
-
# Internal: event handlers (called by
|
|
765
|
+
# Internal: event handlers (called by EventStream)
|
|
763
766
|
# ----------------------------------------------------------------
|
|
764
767
|
|
|
765
768
|
def handle_flag_changed(data)
|
|
@@ -770,7 +773,7 @@ module Smplkit
|
|
|
770
773
|
new_data = fetch_flag_single_data(key)
|
|
771
774
|
@flag_store[key] = new_data
|
|
772
775
|
@cache.clear
|
|
773
|
-
fire_change_listeners(key, "
|
|
776
|
+
fire_change_listeners(key, "push") if pre != new_data
|
|
774
777
|
end
|
|
775
778
|
|
|
776
779
|
def handle_flag_deleted(data)
|
|
@@ -780,7 +783,7 @@ module Smplkit
|
|
|
780
783
|
existed = @flag_store.key?(key)
|
|
781
784
|
@flag_store.delete(key)
|
|
782
785
|
@cache.clear
|
|
783
|
-
fire_change_listeners(key, "
|
|
786
|
+
fire_change_listeners(key, "push", deleted: true) if existed
|
|
784
787
|
end
|
|
785
788
|
|
|
786
789
|
def handle_flags_changed(_data)
|
|
@@ -788,7 +791,7 @@ module Smplkit
|
|
|
788
791
|
begin
|
|
789
792
|
fetch_all_flags
|
|
790
793
|
rescue StandardError => e
|
|
791
|
-
Smplkit.debug("
|
|
794
|
+
Smplkit.debug("flags", "flags refresh after flags_changed failed: #{e.message}")
|
|
792
795
|
return
|
|
793
796
|
end
|
|
794
797
|
@cache.clear
|
|
@@ -798,7 +801,7 @@ module Smplkit
|
|
|
798
801
|
return if changed.empty?
|
|
799
802
|
|
|
800
803
|
# Global listener fires once.
|
|
801
|
-
first_event = FlagChangeEvent.new(id: changed.first, source: "
|
|
804
|
+
first_event = FlagChangeEvent.new(id: changed.first, source: "push")
|
|
802
805
|
@global_listeners.each do |cb|
|
|
803
806
|
cb.call(first_event)
|
|
804
807
|
rescue StandardError => e
|
|
@@ -808,7 +811,7 @@ module Smplkit
|
|
|
808
811
|
# Per-key listeners fire for each changed key.
|
|
809
812
|
changed.each do |k|
|
|
810
813
|
deleted = pre_store.key?(k) && !post_store.key?(k)
|
|
811
|
-
event = FlagChangeEvent.new(id: k, source: "
|
|
814
|
+
event = FlagChangeEvent.new(id: k, source: "push", deleted: deleted)
|
|
812
815
|
@key_listeners[k].each do |cb|
|
|
813
816
|
cb.call(event)
|
|
814
817
|
rescue StandardError => e
|
data/lib/smplkit/jobs/client.rb
CHANGED
|
@@ -268,8 +268,8 @@ module Smplkit
|
|
|
268
268
|
# The Smpl Jobs client — accessed via +client.jobs+.
|
|
269
269
|
#
|
|
270
270
|
# Unlike Config/Flags/Logging, Jobs has no live "phone-home" agent — no
|
|
271
|
-
# environment registration, no
|
|
272
|
-
# one client. Defining a job, triggering a run, and reading run history are
|
|
271
|
+
# environment registration, no event stream — so its entire surface lives
|
|
272
|
+
# on one client. Defining a job, triggering a run, and reading run history are
|
|
273
273
|
# all plain request/response calls here:
|
|
274
274
|
#
|
|
275
275
|
# client.jobs.{new_recurring_job,new_manual_job,schedule,get,list,delete,run,usage}
|
data/lib/smplkit/jobs/models.rb
CHANGED
|
@@ -4,8 +4,8 @@ module Smplkit
|
|
|
4
4
|
# Smpl Jobs surface — exposed through +client.jobs.*+.
|
|
5
5
|
#
|
|
6
6
|
# Unlike Config/Flags/Logging, Jobs has no live "phone-home" agent — no
|
|
7
|
-
# environment registration, no
|
|
8
|
-
# single client. A {Job} is an active record: build it with
|
|
7
|
+
# environment registration, no event stream — so its entire surface lives on
|
|
8
|
+
# a single client. A {Job} is an active record: build it with
|
|
9
9
|
# +client.jobs.new_recurring_job(...)+ / +new_manual_job(...)+ /
|
|
10
10
|
# +schedule(...)+, set fields, and call {Job#save} (create when new,
|
|
11
11
|
# full-replace update when it already exists) or {Job#delete}. Runs are
|
|
@@ -20,19 +20,19 @@
|
|
|
20
20
|
# * *Live surface* — directly on the client. +register_adapter+ is a PRE-install
|
|
21
21
|
# configuration call (allowed before +install+). +install+ opens the live
|
|
22
22
|
# connection (monkey-patches the app's logging framework, discovers loggers,
|
|
23
|
-
# fetches + applies levels, opens the shared
|
|
23
|
+
# fetches + applies levels, opens the shared event stream). +on_change+ /
|
|
24
24
|
# +refresh+ require +install+ first; calling them earlier raises
|
|
25
25
|
# +NotInstalledError+.
|
|
26
26
|
#
|
|
27
27
|
# The client supports two construction shapes:
|
|
28
28
|
#
|
|
29
29
|
# * *Wired* into +Smplkit::Client+ — borrows the parent's logging transport for
|
|
30
|
-
# both runtime fetch and CRUD and the parent's shared
|
|
31
|
-
# channel. This is the common path.
|
|
30
|
+
# both runtime fetch and CRUD and the parent's shared event stream for the
|
|
31
|
+
# live channel. This is the common path.
|
|
32
32
|
# * *Standalone* — +LoggingClient.new(api_key: ..., base_url: ..., ...)+ builds
|
|
33
|
-
# and owns its own logging transport and an app transport (the
|
|
34
|
-
#
|
|
35
|
-
#
|
|
33
|
+
# and owns its own logging transport and an app transport (the event stream
|
|
34
|
+
# lives on the app service), and on +install+ opens and owns its own event
|
|
35
|
+
# stream. +close+ tears down only the owned transports and owned event stream.
|
|
36
36
|
module Smplkit
|
|
37
37
|
module Logging
|
|
38
38
|
NOT_INSTALLED_MESSAGE = "Smpl Logging live operations require install() first — this opens a live " \
|
|
@@ -45,10 +45,9 @@ module Smplkit
|
|
|
45
45
|
# client takes after it has already resolved them); otherwise the management
|
|
46
46
|
# config resolver fills in whatever is missing (+~/.smplkit+ / env vars /
|
|
47
47
|
# defaults). +environment+/+service+ resolve the same way (constructor
|
|
48
|
-
# argument wins). The app transport is needed
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
# gateway.
|
|
48
|
+
# argument wins). The app transport is needed because the event stream
|
|
49
|
+
# lives on the app service (like flags); the app base URL is returned so a
|
|
50
|
+
# standalone client can open its own event stream against the app service.
|
|
52
51
|
#
|
|
53
52
|
# @api private
|
|
54
53
|
def self.logging_transport(api_key:, base_url:, profile:, base_domain:, scheme:,
|
|
@@ -97,7 +96,7 @@ module Smplkit
|
|
|
97
96
|
# +"INFO"+, +"DEBUG"+) — the same value the resolution algorithm returns.
|
|
98
97
|
# @!attribute [rw] source
|
|
99
98
|
# @return [String] Short string identifying the trigger — typically
|
|
100
|
-
# +"
|
|
99
|
+
# +"push"+ (a live update) or +"manual"+ (a +refresh+ call).
|
|
101
100
|
LoggerChangeEvent = Struct.new(:id, :level, :source, keyword_init: true) do
|
|
102
101
|
def ==(other)
|
|
103
102
|
other.is_a?(LoggerChangeEvent) &&
|
|
@@ -457,8 +456,8 @@ module Smplkit
|
|
|
457
456
|
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
458
457
|
# @param extra_headers [Hash{String => String}, nil] Extra headers
|
|
459
458
|
# attached to every request.
|
|
460
|
-
# @param streaming [Boolean] Live updates over
|
|
461
|
-
# +true+): +install+ opens a shared
|
|
459
|
+
# @param streaming [Boolean] Live updates over the event stream (default
|
|
460
|
+
# +true+): +install+ opens a shared stream and server-side level
|
|
462
461
|
# changes stream in. Set +false+ for the stateless apply-once surface:
|
|
463
462
|
# +install+ still loads adapters, flushes discovery, and applies the
|
|
464
463
|
# server's levels — all blocking — but NO socket or background thread
|
|
@@ -514,8 +513,8 @@ module Smplkit
|
|
|
514
513
|
@key_listeners = Hash.new { |h, k| h[k] = [] }
|
|
515
514
|
@adapters = []
|
|
516
515
|
@explicit_adapters = false
|
|
517
|
-
@
|
|
518
|
-
@
|
|
516
|
+
@event_stream = nil
|
|
517
|
+
@owns_stream = false
|
|
519
518
|
@lock = Mutex.new
|
|
520
519
|
end
|
|
521
520
|
|
|
@@ -545,12 +544,12 @@ module Smplkit
|
|
|
545
544
|
@adapters.dup
|
|
546
545
|
end
|
|
547
546
|
|
|
548
|
-
# --- Live surface: install (gate) + transport /
|
|
547
|
+
# --- Live surface: install (gate) + transport / event stream helpers ---
|
|
549
548
|
|
|
550
549
|
# Hook smplkit into the application's logging machinery.
|
|
551
550
|
#
|
|
552
551
|
# Loads adapters, scans existing loggers, applies levels from the smplkit
|
|
553
|
-
# server, and wires
|
|
552
|
+
# server, and wires event stream handlers for live updates. This IS the
|
|
554
553
|
# explicit consent gate — +on_change+ / +refresh+ require it first.
|
|
555
554
|
#
|
|
556
555
|
# Idempotent — safe to call multiple times.
|
|
@@ -600,12 +599,15 @@ module Smplkit
|
|
|
600
599
|
"(logging: #{@logging_http&.config&.host}): #{e.class}: #{e.message}")
|
|
601
600
|
end
|
|
602
601
|
|
|
603
|
-
# 7. Register
|
|
604
|
-
#
|
|
605
|
-
#
|
|
602
|
+
# 7. Register event stream handlers for real-time level updates, plus
|
|
603
|
+
# the bulk-refresh path as the stream's reconnect refetch so a stream
|
|
604
|
+
# outage ends with a full re-sync. In stateless mode
|
|
605
|
+
# (+streaming: false+) no stream is ever created — level changes then
|
|
606
|
+
# arrive only via +refresh+.
|
|
606
607
|
if @streaming
|
|
607
|
-
@
|
|
608
|
-
|
|
608
|
+
@event_stream = ensure_event_stream
|
|
609
|
+
stream_handlers.each { |event, handler| @event_stream.on(event, &handler) }
|
|
610
|
+
@event_stream.on_reconnect(refetch_callback)
|
|
609
611
|
end
|
|
610
612
|
|
|
611
613
|
@connected = true
|
|
@@ -643,9 +645,9 @@ module Smplkit
|
|
|
643
645
|
|
|
644
646
|
# Release resources — only those this client owns.
|
|
645
647
|
#
|
|
646
|
-
# Uninstalls the adapter hooks, unsubscribes from the
|
|
647
|
-
# down the owned
|
|
648
|
-
# parent's transport and
|
|
648
|
+
# Uninstalls the adapter hooks, unsubscribes from the event stream, and
|
|
649
|
+
# tears down the owned event stream (standalone install). A wired client
|
|
650
|
+
# borrows the parent's transport and event stream and closes neither.
|
|
649
651
|
def close
|
|
650
652
|
Smplkit.debug("lifecycle", "LoggingClient.close() called")
|
|
651
653
|
@adapters.each do |adapter|
|
|
@@ -653,13 +655,14 @@ module Smplkit
|
|
|
653
655
|
rescue StandardError => e
|
|
654
656
|
Smplkit.debug("logging", "adapter #{adapter.name} uninstall_hook failed: #{e.class}: #{e.message}")
|
|
655
657
|
end
|
|
656
|
-
if @
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
@
|
|
658
|
+
if @event_stream
|
|
659
|
+
stream_handlers.each { |event, handler| @event_stream.off(event, handler) }
|
|
660
|
+
@event_stream.off_reconnect(refetch_callback)
|
|
661
|
+
if @owns_stream
|
|
662
|
+
@event_stream.stop
|
|
663
|
+
@owns_stream = false
|
|
661
664
|
end
|
|
662
|
-
@
|
|
665
|
+
@event_stream = nil
|
|
663
666
|
end
|
|
664
667
|
@connected = false
|
|
665
668
|
end
|
|
@@ -674,7 +677,7 @@ module Smplkit
|
|
|
674
677
|
#
|
|
675
678
|
# Mirrors Ruby's +File.open+ block form: the client is closed
|
|
676
679
|
# automatically when the block returns or raises, so a standalone client's
|
|
677
|
-
# owned transports and
|
|
680
|
+
# owned transports and event stream are always torn down.
|
|
678
681
|
#
|
|
679
682
|
# Smplkit::LoggingClient.open(environment: "production") do |logging|
|
|
680
683
|
# logging.loggers.new("sqlalchemy.engine").save
|
|
@@ -701,10 +704,10 @@ module Smplkit
|
|
|
701
704
|
|
|
702
705
|
# Memoized event → handler map so +install+ registers and +close+
|
|
703
706
|
# unsubscribes the exact same callback objects. They are stored as procs
|
|
704
|
-
# (not bound methods) because +
|
|
707
|
+
# (not bound methods) because +EventStream#off+ removes by object
|
|
705
708
|
# identity, and +on(event, &proc)+ stores the very proc passed here.
|
|
706
|
-
def
|
|
707
|
-
@
|
|
709
|
+
def stream_handlers
|
|
710
|
+
@stream_handlers ||= {
|
|
708
711
|
"logger_changed" => proc { |data| handle_logger_changed(data) },
|
|
709
712
|
"logger_deleted" => proc { |data| handle_logger_deleted(data) },
|
|
710
713
|
"group_changed" => proc { |data| handle_group_changed(data) },
|
|
@@ -713,17 +716,24 @@ module Smplkit
|
|
|
713
716
|
}
|
|
714
717
|
end
|
|
715
718
|
|
|
716
|
-
|
|
717
|
-
|
|
719
|
+
# Memoized reconnect refetch — the same bulk-refresh path the
|
|
720
|
+
# +loggers_changed+ handler runs, registered with the stream on
|
|
721
|
+
# +install+ and deregistered (by object identity) on +close+.
|
|
722
|
+
def refetch_callback
|
|
723
|
+
@refetch_callback ||= proc { handle_loggers_changed({}) }
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def ensure_event_stream
|
|
727
|
+
return @parent._ensure_event_stream unless @parent.nil?
|
|
718
728
|
|
|
719
|
-
if @
|
|
720
|
-
@
|
|
729
|
+
if @event_stream.nil?
|
|
730
|
+
@event_stream = EventStream.new(
|
|
721
731
|
app_base_url: @app_base_url, api_key: @standalone_api_key, metrics: @metrics
|
|
722
732
|
)
|
|
723
|
-
@
|
|
724
|
-
@
|
|
733
|
+
@event_stream.start
|
|
734
|
+
@owns_stream = true
|
|
725
735
|
end
|
|
726
|
-
@
|
|
736
|
+
@event_stream
|
|
727
737
|
end
|
|
728
738
|
|
|
729
739
|
# --- Internal ---
|
|
@@ -777,8 +787,8 @@ module Smplkit
|
|
|
777
787
|
# path).
|
|
778
788
|
#
|
|
779
789
|
# Silent — does not fire change-listener events. Use
|
|
780
|
-
# +fetch_and_apply_deltas+ from the
|
|
781
|
-
# fanout.
|
|
790
|
+
# +fetch_and_apply_deltas+ from the push / refresh paths to get
|
|
791
|
+
# per-logger fanout.
|
|
782
792
|
def fetch_and_apply(trigger: "unknown")
|
|
783
793
|
fetch_cache(trigger)
|
|
784
794
|
apply_levels
|
|
@@ -858,58 +868,58 @@ module Smplkit
|
|
|
858
868
|
end
|
|
859
869
|
end
|
|
860
870
|
|
|
861
|
-
# --- Internal: event handlers (called by
|
|
871
|
+
# --- Internal: event handlers (called by EventStream) ---
|
|
862
872
|
|
|
863
873
|
def handle_logger_changed(data)
|
|
864
874
|
key = data["id"] || ""
|
|
865
|
-
Smplkit.debug("
|
|
875
|
+
Smplkit.debug("events", "logger_changed: fetching logger #{key.inspect}")
|
|
866
876
|
pre = snapshot_effective_levels
|
|
867
877
|
begin
|
|
868
878
|
entry_id, entry = @loggers.get_logger_entry(key)
|
|
869
879
|
@loggers_cache[entry_id || key] = entry
|
|
870
880
|
rescue StandardError => e
|
|
871
|
-
Smplkit.debug("
|
|
881
|
+
Smplkit.debug("events", "failed to fetch logger #{key.inspect} after push event: #{e.class}: #{e.message}")
|
|
872
882
|
return
|
|
873
883
|
end
|
|
874
|
-
apply_deltas_and_fire(pre, "
|
|
884
|
+
apply_deltas_and_fire(pre, "push")
|
|
875
885
|
end
|
|
876
886
|
|
|
877
887
|
def handle_logger_deleted(data)
|
|
878
888
|
key = data["id"] || ""
|
|
879
|
-
Smplkit.debug("
|
|
889
|
+
Smplkit.debug("events", "logger_deleted: removing logger #{key.inspect}")
|
|
880
890
|
pre = snapshot_effective_levels
|
|
881
891
|
@loggers_cache.delete(key)
|
|
882
|
-
apply_deltas_and_fire(pre, "
|
|
892
|
+
apply_deltas_and_fire(pre, "push")
|
|
883
893
|
end
|
|
884
894
|
|
|
885
895
|
def handle_group_changed(data)
|
|
886
896
|
key = data["id"] || ""
|
|
887
|
-
Smplkit.debug("
|
|
897
|
+
Smplkit.debug("events", "group_changed: fetching group #{key.inspect}")
|
|
888
898
|
pre = snapshot_effective_levels
|
|
889
899
|
begin
|
|
890
900
|
entry_id, entry = @log_groups.get_group_entry(key)
|
|
891
901
|
@groups_cache[entry_id || key] = entry
|
|
892
902
|
rescue StandardError => e
|
|
893
|
-
Smplkit.debug("
|
|
894
|
-
"failed to fetch log group #{key.inspect} after
|
|
903
|
+
Smplkit.debug("events",
|
|
904
|
+
"failed to fetch log group #{key.inspect} after push event: #{e.class}: #{e.message}")
|
|
895
905
|
return
|
|
896
906
|
end
|
|
897
|
-
apply_deltas_and_fire(pre, "
|
|
907
|
+
apply_deltas_and_fire(pre, "push")
|
|
898
908
|
end
|
|
899
909
|
|
|
900
910
|
def handle_group_deleted(data)
|
|
901
911
|
key = data["id"] || ""
|
|
902
|
-
Smplkit.debug("
|
|
912
|
+
Smplkit.debug("events", "group_deleted: removing group #{key.inspect}")
|
|
903
913
|
pre = snapshot_effective_levels
|
|
904
914
|
@groups_cache.delete(key)
|
|
905
|
-
apply_deltas_and_fire(pre, "
|
|
915
|
+
apply_deltas_and_fire(pre, "push")
|
|
906
916
|
end
|
|
907
917
|
|
|
908
918
|
def handle_loggers_changed(_data)
|
|
909
|
-
Smplkit.debug("
|
|
910
|
-
fetch_and_apply_deltas(trigger: "loggers_changed
|
|
919
|
+
Smplkit.debug("events", "loggers_changed: full re-fetch")
|
|
920
|
+
fetch_and_apply_deltas(trigger: "loggers_changed event", source: "push")
|
|
911
921
|
rescue StandardError => e
|
|
912
|
-
Smplkit.debug("
|
|
922
|
+
Smplkit.debug("events",
|
|
913
923
|
"failed to re-fetch/apply logging levels after loggers_changed event: #{e.class}: #{e.message}")
|
|
914
924
|
end
|
|
915
925
|
end
|
data/lib/smplkit/transport.rb
CHANGED
|
@@ -65,7 +65,7 @@ module Smplkit
|
|
|
65
65
|
#
|
|
66
66
|
# Construction is side-effect-free: each transport connects lazily on its
|
|
67
67
|
# first call. +app_url+ is carried alongside so the account settings client
|
|
68
|
-
# and the
|
|
68
|
+
# and the event stream can reach the app service. +close+ tears down the
|
|
69
69
|
# underlying Faraday connection pools.
|
|
70
70
|
#
|
|
71
71
|
# @api private
|
data/lib/smplkit/version.rb
CHANGED
|
@@ -26,8 +26,8 @@ module Smplkit
|
|
|
26
26
|
spec ? spec.version.to_s : VERSION
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# The default User-Agent stamped on every outbound request (
|
|
30
|
-
#
|
|
29
|
+
# The default User-Agent stamped on every outbound request (including the
|
|
30
|
+
# live-updates event stream) when the caller has not supplied their own.
|
|
31
31
|
#
|
|
32
32
|
# The platform sits behind a WAF that rejects requests carrying no
|
|
33
33
|
# User-Agent, and an SDK-identifying value keeps support/telemetry able to
|
data/lib/smplkit.rb
CHANGED
|
@@ -43,7 +43,7 @@ require_relative "smplkit/log_level"
|
|
|
43
43
|
require_relative "smplkit/context"
|
|
44
44
|
require_relative "smplkit/config_resolution"
|
|
45
45
|
require_relative "smplkit/metrics"
|
|
46
|
-
require_relative "smplkit/
|
|
46
|
+
require_relative "smplkit/event_stream"
|
|
47
47
|
|
|
48
48
|
# Internal foundation shared by every product client.
|
|
49
49
|
require_relative "smplkit/buffers"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smplkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.136
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Smpl Solutions LLC
|
|
@@ -49,26 +49,6 @@ dependencies:
|
|
|
49
49
|
- - "<"
|
|
50
50
|
- !ruby/object:Gem::Version
|
|
51
51
|
version: '1'
|
|
52
|
-
- !ruby/object:Gem::Dependency
|
|
53
|
-
name: async-websocket
|
|
54
|
-
requirement: !ruby/object:Gem::Requirement
|
|
55
|
-
requirements:
|
|
56
|
-
- - ">="
|
|
57
|
-
- !ruby/object:Gem::Version
|
|
58
|
-
version: '0.26'
|
|
59
|
-
- - "<"
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '1'
|
|
62
|
-
type: :runtime
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0.26'
|
|
69
|
-
- - "<"
|
|
70
|
-
- !ruby/object:Gem::Version
|
|
71
|
-
version: '1'
|
|
72
52
|
- !ruby/object:Gem::Dependency
|
|
73
53
|
name: concurrent-ruby
|
|
74
54
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -933,6 +913,7 @@ files:
|
|
|
933
913
|
- lib/smplkit/context.rb
|
|
934
914
|
- lib/smplkit/debug.rb
|
|
935
915
|
- lib/smplkit/errors.rb
|
|
916
|
+
- lib/smplkit/event_stream.rb
|
|
936
917
|
- lib/smplkit/flags/client.rb
|
|
937
918
|
- lib/smplkit/flags/helpers.rb
|
|
938
919
|
- lib/smplkit/flags/models.rb
|
|
@@ -960,7 +941,6 @@ files:
|
|
|
960
941
|
- lib/smplkit/railtie.rb
|
|
961
942
|
- lib/smplkit/transport.rb
|
|
962
943
|
- lib/smplkit/version.rb
|
|
963
|
-
- lib/smplkit/ws.rb
|
|
964
944
|
- sig/smplkit.rbs
|
|
965
945
|
- sig/smplkit/config.rbs
|
|
966
946
|
- sig/smplkit/flags.rbs
|