smplkit 3.0.14 → 3.0.16
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/logging/client.rb +175 -13
- data/lib/smplkit/logging/resolution.rb +118 -0
- data/lib/smplkit/management/client.rb +138 -17
- data/lib/smplkit.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 215caf14863d35bebf83849e104befe12423a9ef468a547cb44714e6a9ea1932
|
|
4
|
+
data.tar.gz: 928ab9e89ea2dca1548eb82a06aa8d9b525f874026437bb6b862b4cdc254a4c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f77af115d5412a34711fb54af8de0c402e47f33e53512fb808794a103a046d514494d947a6ed9d7d42998d9aca3f196db433914de52f96b785e3b35e5c63972
|
|
7
|
+
data.tar.gz: d028dd314e06e87fc6185f80695ba0c4bdf21b5d77d548c62e32bc2c4138590f7566c327cc83946191c26e233be9b0d923d1ba0d3d6fb59f835f91ca9f6f3bfa
|
|
@@ -9,6 +9,11 @@ module Smplkit
|
|
|
9
9
|
# Obtained via +Smplkit::Client#logging+. Manages the discovery and level
|
|
10
10
|
# application for a customer's logging frameworks via pluggable adapters.
|
|
11
11
|
# CRUD has moved to +mgmt.loggers.*+ / +mgmt.log_groups.*+.
|
|
12
|
+
#
|
|
13
|
+
# Level resolution is client-side: the server stores raw configuration
|
|
14
|
+
# and the SDK walks the chain (env override → base → group chain →
|
|
15
|
+
# dot-notation ancestry) to compute each managed logger's effective
|
|
16
|
+
# level. See {Smplkit::Logging::Resolution}.
|
|
12
17
|
class LoggingClient
|
|
13
18
|
def initialize(parent, manage:, metrics:, logging_base_url:, app_base_url:)
|
|
14
19
|
@parent = parent
|
|
@@ -21,6 +26,23 @@ module Smplkit
|
|
|
21
26
|
@global_listeners = []
|
|
22
27
|
@key_listeners = Hash.new { |h, k| h[k] = [] }
|
|
23
28
|
@lock = Mutex.new
|
|
29
|
+
# original_name → normalized_id for every adapter-discovered logger.
|
|
30
|
+
# We keep originals so adapter.apply_level receives whatever the
|
|
31
|
+
# framework's registry indexes by.
|
|
32
|
+
@name_map = {}
|
|
33
|
+
# normalized_id → resolution-cache entry. Populated by
|
|
34
|
+
# +_fetch_and_apply+ and mutated by the +logger_changed+ /
|
|
35
|
+
# +logger_deleted+ WS handlers.
|
|
36
|
+
@loggers_cache = {}
|
|
37
|
+
# group id → resolution-cache entry. Without this, any managed
|
|
38
|
+
# logger with +level=null+ that inherits from a group silently
|
|
39
|
+
# keeps whatever level its adapter had at startup.
|
|
40
|
+
@groups_cache = {}
|
|
41
|
+
# normalized_id → resolved level (string). Used to decide whether
|
|
42
|
+
# to fire change listeners on a re-resolution — a group-driven
|
|
43
|
+
# change isn't visible in the raw +loggers_cache+ but moves the
|
|
44
|
+
# resolved value.
|
|
45
|
+
@resolved_levels = {}
|
|
24
46
|
end
|
|
25
47
|
|
|
26
48
|
# Install the logging integration.
|
|
@@ -39,8 +61,15 @@ module Smplkit
|
|
|
39
61
|
adapter.install_hook { |name, _explicit, effective| observe_logger(adapter, name, effective) }
|
|
40
62
|
end
|
|
41
63
|
|
|
64
|
+
flush_initial_registration
|
|
65
|
+
fetch_and_apply(trigger: "install")
|
|
66
|
+
|
|
42
67
|
@ws_manager = @parent._ensure_ws
|
|
43
68
|
@ws_manager.on("logger_changed") { |data| handle_logger_changed(data) }
|
|
69
|
+
@ws_manager.on("logger_deleted") { |data| handle_logger_deleted(data) }
|
|
70
|
+
@ws_manager.on("group_changed") { |data| handle_group_changed(data) }
|
|
71
|
+
@ws_manager.on("group_deleted") { |data| handle_group_deleted(data) }
|
|
72
|
+
@ws_manager.on("loggers_changed") { |data| handle_loggers_changed(data) }
|
|
44
73
|
@installed = true
|
|
45
74
|
self
|
|
46
75
|
end
|
|
@@ -63,14 +92,20 @@ module Smplkit
|
|
|
63
92
|
@manage.loggers.get(name)
|
|
64
93
|
end
|
|
65
94
|
|
|
66
|
-
def list
|
|
67
|
-
@manage.loggers.list
|
|
95
|
+
def list(page_number: nil, page_size: nil)
|
|
96
|
+
@manage.loggers.list(page_number: page_number, page_size: page_size)
|
|
68
97
|
end
|
|
69
98
|
|
|
70
99
|
def delete(name)
|
|
71
100
|
@manage.loggers.delete(name)
|
|
72
101
|
end
|
|
73
102
|
|
|
103
|
+
# Re-fetch all loggers and groups and re-apply resolved levels. Fires
|
|
104
|
+
# change listeners for any logger whose resolved level moved.
|
|
105
|
+
def refresh
|
|
106
|
+
fetch_and_apply(trigger: "refresh")
|
|
107
|
+
end
|
|
108
|
+
|
|
74
109
|
def on_change(name = nil, &block)
|
|
75
110
|
raise ArgumentError, "on_change requires a block" unless block
|
|
76
111
|
|
|
@@ -102,15 +137,17 @@ module Smplkit
|
|
|
102
137
|
|
|
103
138
|
return unless @adapters.empty?
|
|
104
139
|
|
|
105
|
-
#
|
|
106
|
-
#
|
|
140
|
+
# Defensive log — unreachable in practice because stdlib +Logger+
|
|
141
|
+
# is always present, so +StdlibLoggerAdapter+ is always
|
|
107
142
|
# constructible.
|
|
143
|
+
# :nocov:
|
|
108
144
|
Smplkit.debug("registration", "no logging adapters loaded; runtime features disabled")
|
|
109
145
|
# :nocov:
|
|
110
146
|
end
|
|
111
147
|
|
|
112
148
|
def observe_logger(_adapter, raw_name, level)
|
|
113
149
|
normalized = Normalize.normalize_logger_name(raw_name)
|
|
150
|
+
@name_map[raw_name] = normalized
|
|
114
151
|
@manage.loggers.register(LoggerSource.new(
|
|
115
152
|
name: normalized,
|
|
116
153
|
resolved_level: level,
|
|
@@ -120,16 +157,135 @@ module Smplkit
|
|
|
120
157
|
))
|
|
121
158
|
end
|
|
122
159
|
|
|
160
|
+
def flush_initial_registration
|
|
161
|
+
@manage.loggers.flush
|
|
162
|
+
rescue StandardError => e
|
|
163
|
+
Smplkit.debug("registration", "initial logger flush failed: #{e.class}: #{e.message}")
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Full re-fetch of loggers + groups, then apply resolved levels.
|
|
167
|
+
# Fires change listeners for any logger whose resolved level moved.
|
|
168
|
+
def fetch_and_apply(trigger:)
|
|
169
|
+
Smplkit.debug("resolution", "full resolution pass starting (trigger: #{trigger})")
|
|
170
|
+
loggers = @manage.loggers.list_logger_entries
|
|
171
|
+
groups = @manage.log_groups.list_group_entries
|
|
172
|
+
@loggers_cache = loggers
|
|
173
|
+
@groups_cache = groups
|
|
174
|
+
apply_levels(source: "websocket")
|
|
175
|
+
rescue StandardError => e
|
|
176
|
+
Smplkit.debug("resolution", "fetch_and_apply failed (trigger: #{trigger}): #{e.class}: #{e.message}")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Resolve the effective level for every locally-known managed logger
|
|
180
|
+
# and push it to every adapter. Returns the list of normalized ids
|
|
181
|
+
# whose resolved level changed.
|
|
182
|
+
#
|
|
183
|
+
# +source+ is the +LoggerChangeEvent#source+ for any change-listener
|
|
184
|
+
# event we fire. The default reflects callers that arrived through a
|
|
185
|
+
# server event (WebSocket).
|
|
186
|
+
def apply_levels(source: "websocket")
|
|
187
|
+
changed = []
|
|
188
|
+
@name_map.each do |raw_name, normalized_id|
|
|
189
|
+
entry = @loggers_cache[normalized_id]
|
|
190
|
+
next if entry.nil?
|
|
191
|
+
next unless entry["managed"]
|
|
192
|
+
|
|
193
|
+
resolved_string = Resolution.resolve_level(
|
|
194
|
+
normalized_id, @parent._environment, @loggers_cache, @groups_cache
|
|
195
|
+
)
|
|
196
|
+
coerced = LogLevel.coerce(resolved_string)
|
|
197
|
+
push_to_adapters(raw_name, coerced)
|
|
198
|
+
previous = @resolved_levels[normalized_id]
|
|
199
|
+
if previous != resolved_string
|
|
200
|
+
@resolved_levels[normalized_id] = resolved_string
|
|
201
|
+
changed << [normalized_id, coerced]
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
fire_resolved_change_events(changed, source: source)
|
|
205
|
+
changed
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def push_to_adapters(raw_name, coerced_level)
|
|
209
|
+
@adapters.each do |a|
|
|
210
|
+
a.apply_level(raw_name, coerced_level)
|
|
211
|
+
rescue StandardError => e
|
|
212
|
+
Smplkit.debug("logging", "adapter apply_level raised: #{e.class}: #{e.message}")
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def fire_resolved_change_events(changed, source:)
|
|
217
|
+
changed.each do |(normalized_id, coerced_level)|
|
|
218
|
+
event = LoggerChangeEvent.new(name: normalized_id, level: coerced_level, source: source)
|
|
219
|
+
(@global_listeners + @key_listeners[normalized_id]).each do |cb|
|
|
220
|
+
cb.call(event)
|
|
221
|
+
rescue StandardError => e
|
|
222
|
+
Smplkit.debug("logging", "listener raised: #{e.class}: #{e.message}")
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
123
227
|
def handle_logger_changed(data)
|
|
124
|
-
|
|
125
|
-
|
|
228
|
+
key = data["id"] || data["name"] || ""
|
|
229
|
+
normalized = Normalize.normalize_logger_name(key)
|
|
230
|
+
return if normalized.empty?
|
|
126
231
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
232
|
+
begin
|
|
233
|
+
entry_id, entry = @manage.loggers.get_logger_entry(normalized)
|
|
234
|
+
@loggers_cache[entry_id || normalized] = entry
|
|
235
|
+
rescue StandardError => e
|
|
236
|
+
Smplkit.debug("websocket", "logger_changed fetch failed for #{normalized.inspect}: #{e.class}: #{e.message}")
|
|
237
|
+
return
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
apply_levels(source: "websocket")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def handle_logger_deleted(data)
|
|
244
|
+
key = data["id"] || data["name"] || ""
|
|
245
|
+
normalized = Normalize.normalize_logger_name(key)
|
|
246
|
+
return if normalized.empty?
|
|
247
|
+
|
|
248
|
+
existed = @loggers_cache.delete(normalized)
|
|
249
|
+
@resolved_levels.delete(normalized)
|
|
250
|
+
return unless existed
|
|
251
|
+
|
|
252
|
+
apply_levels(source: "websocket")
|
|
253
|
+
fire_deletion_event(normalized)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def handle_group_changed(data)
|
|
257
|
+
key = data["id"] || data["key"] || ""
|
|
258
|
+
return if key.to_s.empty?
|
|
259
|
+
|
|
260
|
+
begin
|
|
261
|
+
entry_id, entry = @manage.log_groups.get_group_entry(key)
|
|
262
|
+
@groups_cache[entry_id || key] = entry
|
|
263
|
+
rescue StandardError => e
|
|
264
|
+
Smplkit.debug("websocket", "group_changed fetch failed for #{key.inspect}: #{e.class}: #{e.message}")
|
|
265
|
+
return
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
apply_levels(source: "websocket")
|
|
269
|
+
end
|
|
130
270
|
|
|
131
|
-
|
|
132
|
-
|
|
271
|
+
def handle_group_deleted(data)
|
|
272
|
+
key = data["id"] || data["key"] || ""
|
|
273
|
+
return if key.to_s.empty?
|
|
274
|
+
|
|
275
|
+
existed = @groups_cache.delete(key)
|
|
276
|
+
return unless existed
|
|
277
|
+
|
|
278
|
+
apply_levels(source: "websocket")
|
|
279
|
+
fire_deletion_event(key)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def handle_loggers_changed(_data)
|
|
283
|
+
fetch_and_apply(trigger: "loggers_changed WS event")
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def fire_deletion_event(key)
|
|
287
|
+
event = LoggerChangeEvent.new(name: key, level: nil, source: "websocket", deleted: true)
|
|
288
|
+
(@global_listeners + @key_listeners[key]).each do |cb|
|
|
133
289
|
cb.call(event)
|
|
134
290
|
rescue StandardError => e
|
|
135
291
|
Smplkit.debug("logging", "listener raised: #{e.class}: #{e.message}")
|
|
@@ -137,9 +293,15 @@ module Smplkit
|
|
|
137
293
|
end
|
|
138
294
|
end
|
|
139
295
|
|
|
140
|
-
LoggerChangeEvent = Struct.new(:name, :level, :source, keyword_init: true) do
|
|
296
|
+
LoggerChangeEvent = Struct.new(:name, :level, :source, :deleted, keyword_init: true) do
|
|
297
|
+
def initialize(name:, level:, source:, deleted: false)
|
|
298
|
+
super
|
|
299
|
+
end
|
|
300
|
+
|
|
141
301
|
def ==(other)
|
|
142
|
-
other.is_a?(LoggerChangeEvent) &&
|
|
302
|
+
other.is_a?(LoggerChangeEvent) &&
|
|
303
|
+
name == other.name && level == other.level &&
|
|
304
|
+
source == other.source && deleted == other.deleted
|
|
143
305
|
end
|
|
144
306
|
end
|
|
145
307
|
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
5
|
+
module Smplkit
|
|
6
|
+
module Logging
|
|
7
|
+
# Client-side level resolution per ADR-034 §3.1.
|
|
8
|
+
#
|
|
9
|
+
# The server stores raw configuration and returns it as-is; the SDK is
|
|
10
|
+
# responsible for walking the inheritance chain. Mirrors the Python
|
|
11
|
+
# SDK's +smplkit.logging._resolution+ verbatim — both implementations
|
|
12
|
+
# MUST resolve identically for any given (loggers, groups, env) input.
|
|
13
|
+
module Resolution
|
|
14
|
+
FALLBACK_LEVEL = "INFO"
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# Resolve the effective level for +logger_id+ in +environment+.
|
|
19
|
+
#
|
|
20
|
+
# Resolution chain (first non-nil wins):
|
|
21
|
+
#
|
|
22
|
+
# 1. Logger's own +environments[env].level+
|
|
23
|
+
# 2. Logger's own +level+
|
|
24
|
+
# 3. Group chain (recursive: group env level → group level → parent group …)
|
|
25
|
+
# 4. Dot-notation ancestry (+com.acme.payments+ → +com.acme+ → +com+,
|
|
26
|
+
# applying steps 1–3 at each)
|
|
27
|
+
# 5. System fallback: +"INFO"+
|
|
28
|
+
#
|
|
29
|
+
# +loggers+ and +groups+ are id-keyed Hashes whose values are Hashes
|
|
30
|
+
# with the same shape as the Python SDK: +"level"+, +"group"+ (parent
|
|
31
|
+
# group id for loggers; parent_id for groups), +"environments"+
|
|
32
|
+
# (Hash keyed by env name with +{"level" => "..."}+ values).
|
|
33
|
+
def resolve_level(logger_id, environment, loggers, groups)
|
|
34
|
+
result = resolve_for_entry(logger_id, environment, loggers, groups)
|
|
35
|
+
if result
|
|
36
|
+
if Smplkit::Debug.enabled
|
|
37
|
+
source = find_resolution_source(logger_id, environment, loggers, groups)
|
|
38
|
+
Smplkit.debug("resolution", "#{logger_id} -> #{result} (source: #{source})")
|
|
39
|
+
end
|
|
40
|
+
return result
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
parts = logger_id.split(".")
|
|
44
|
+
(parts.length - 1).downto(1) do |i|
|
|
45
|
+
ancestor_id = parts[0, i].join(".")
|
|
46
|
+
result = resolve_for_entry(ancestor_id, environment, loggers, groups)
|
|
47
|
+
if result
|
|
48
|
+
Smplkit.debug("resolution", "#{logger_id} -> #{result} (source: ancestor \"#{ancestor_id}\")")
|
|
49
|
+
return result
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Smplkit.debug("resolution", "#{logger_id} -> #{FALLBACK_LEVEL} (source: system default)")
|
|
54
|
+
FALLBACK_LEVEL
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Try to resolve a level for a single entry (logger or ancestor).
|
|
58
|
+
# Returns +nil+ if no level is found at any step of 1–3.
|
|
59
|
+
def resolve_for_entry(logger_id, environment, loggers, groups)
|
|
60
|
+
entry = loggers[logger_id]
|
|
61
|
+
return nil if entry.nil?
|
|
62
|
+
|
|
63
|
+
env_level = env_level_of(entry, environment)
|
|
64
|
+
return env_level if env_level
|
|
65
|
+
|
|
66
|
+
base = entry["level"]
|
|
67
|
+
return base if base
|
|
68
|
+
|
|
69
|
+
resolve_group_chain(entry["group"], environment, groups)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Walk the group chain looking for a level. Cycle-safe via +visited+.
|
|
73
|
+
def resolve_group_chain(group_id, environment, groups)
|
|
74
|
+
visited = Set.new
|
|
75
|
+
current_id = group_id
|
|
76
|
+
while !current_id.nil? && !visited.include?(current_id)
|
|
77
|
+
visited.add(current_id)
|
|
78
|
+
group = groups[current_id]
|
|
79
|
+
break if group.nil?
|
|
80
|
+
|
|
81
|
+
env_level = env_level_of(group, environment)
|
|
82
|
+
return env_level if env_level
|
|
83
|
+
|
|
84
|
+
base = group["level"]
|
|
85
|
+
return base if base
|
|
86
|
+
|
|
87
|
+
current_id = group["group"]
|
|
88
|
+
end
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Human-readable label for which resolution step won. Only consulted
|
|
93
|
+
# when debug logging is enabled; mirrors Python's +_find_resolution_source+.
|
|
94
|
+
def find_resolution_source(logger_id, environment, loggers, groups)
|
|
95
|
+
entry = loggers[logger_id]
|
|
96
|
+
return "not found" if entry.nil?
|
|
97
|
+
|
|
98
|
+
return %(env override "#{environment}") if env_level_of(entry, environment)
|
|
99
|
+
return "base level" if entry["level"]
|
|
100
|
+
|
|
101
|
+
group_id = entry["group"]
|
|
102
|
+
return %(group "#{group_id}") if resolve_group_chain(group_id, environment, groups)
|
|
103
|
+
|
|
104
|
+
"unknown"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def env_level_of(entry, environment)
|
|
108
|
+
envs = entry["environments"]
|
|
109
|
+
return nil unless envs.is_a?(Hash)
|
|
110
|
+
|
|
111
|
+
env_data = envs[environment]
|
|
112
|
+
return nil unless env_data.is_a?(Hash)
|
|
113
|
+
|
|
114
|
+
env_data["level"]
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -27,6 +27,12 @@ module Smplkit
|
|
|
27
27
|
# etc.) and converts at the boundary via the existing
|
|
28
28
|
# +<resource>_from_resource+ helpers.
|
|
29
29
|
class ManagementClient
|
|
30
|
+
# Default page[size] the runtime asks for when walking a list
|
|
31
|
+
# endpoint to completion. The platform caps page[size] at 1000;
|
|
32
|
+
# using the same value here makes the minimum number of round-trips
|
|
33
|
+
# per exhaustive fetch.
|
|
34
|
+
RUNTIME_PAGE_SIZE = 1000
|
|
35
|
+
|
|
30
36
|
attr_reader :contexts, :context_types, :environments, :account_settings,
|
|
31
37
|
:config, :flags, :loggers, :log_groups, :audit
|
|
32
38
|
|
|
@@ -126,6 +132,33 @@ module Smplkit
|
|
|
126
132
|
end
|
|
127
133
|
end
|
|
128
134
|
|
|
135
|
+
# Walk a generated paginated list endpoint to completion.
|
|
136
|
+
#
|
|
137
|
+
# The block receives a per-page +opts+ hash with +page_number+ and
|
|
138
|
+
# +page_size+ filled in, calls the generated list method through
|
|
139
|
+
# {ErrorMapping.call}, and returns the response object. Pages stop
|
|
140
|
+
# when the server returns fewer rows than requested — the platform's
|
|
141
|
+
# standard last-page signal across every offset-paginated list
|
|
142
|
+
# endpoint. Returns the concatenated +response.data+ rows.
|
|
143
|
+
module PaginatedFetch
|
|
144
|
+
module_function
|
|
145
|
+
|
|
146
|
+
def collect(page_size: RUNTIME_PAGE_SIZE)
|
|
147
|
+
rows = []
|
|
148
|
+
page_number = 1
|
|
149
|
+
loop do
|
|
150
|
+
opts = { page_number: page_number, page_size: page_size }
|
|
151
|
+
response = ErrorMapping.call { yield(opts) }
|
|
152
|
+
page = response.data || []
|
|
153
|
+
rows.concat(page)
|
|
154
|
+
break if page.length < page_size
|
|
155
|
+
|
|
156
|
+
page_number += 1
|
|
157
|
+
end
|
|
158
|
+
rows
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
129
162
|
# Deep-stringify Hash keys so resources returned by generated +to_hash+
|
|
130
163
|
# (symbol-keyed) match what the wrapper helpers expect (string-keyed).
|
|
131
164
|
module ResourceShim
|
|
@@ -175,8 +208,11 @@ module Smplkit
|
|
|
175
208
|
Smplkit.debug("registration", "context flush failed: #{e.class}: #{e.message}")
|
|
176
209
|
end
|
|
177
210
|
|
|
178
|
-
def list
|
|
179
|
-
|
|
211
|
+
def list(page_number: nil, page_size: nil)
|
|
212
|
+
opts = {}
|
|
213
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
214
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
215
|
+
response = ErrorMapping.call { @api.list_contexts(opts) }
|
|
180
216
|
(response.data || []).map { |r| context_from_resource(ResourceShim.from_model(r)) }
|
|
181
217
|
end
|
|
182
218
|
|
|
@@ -237,8 +273,11 @@ module Smplkit
|
|
|
237
273
|
@api = SmplkitGeneratedClient::App::ContextTypesApi.new(api_client)
|
|
238
274
|
end
|
|
239
275
|
|
|
240
|
-
def list
|
|
241
|
-
|
|
276
|
+
def list(page_number: nil, page_size: nil)
|
|
277
|
+
opts = {}
|
|
278
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
279
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
280
|
+
response = ErrorMapping.call { @api.list_context_types(opts) }
|
|
242
281
|
(response.data || []).map { |r| from_resource(ResourceShim.from_model(r)) }
|
|
243
282
|
end
|
|
244
283
|
|
|
@@ -296,8 +335,11 @@ module Smplkit
|
|
|
296
335
|
@api = SmplkitGeneratedClient::App::EnvironmentsApi.new(api_client)
|
|
297
336
|
end
|
|
298
337
|
|
|
299
|
-
def list
|
|
300
|
-
|
|
338
|
+
def list(page_number: nil, page_size: nil)
|
|
339
|
+
opts = {}
|
|
340
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
341
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
342
|
+
response = ErrorMapping.call { @api.list_environments(opts) }
|
|
301
343
|
(response.data || []).map { |r| from_resource(ResourceShim.from_model(r)) }
|
|
302
344
|
end
|
|
303
345
|
|
|
@@ -413,8 +455,11 @@ module Smplkit
|
|
|
413
455
|
@api = SmplkitGeneratedClient::Config::ConfigsApi.new(api_client)
|
|
414
456
|
end
|
|
415
457
|
|
|
416
|
-
def list
|
|
417
|
-
|
|
458
|
+
def list(page_number: nil, page_size: nil)
|
|
459
|
+
opts = {}
|
|
460
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
461
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
462
|
+
response = ErrorMapping.call { @api.list_configs(opts) }
|
|
418
463
|
(response.data || []).map { |r| Smplkit::Config::Helpers.config_from_json(self, ResourceShim.from_model(r)) }
|
|
419
464
|
end
|
|
420
465
|
|
|
@@ -451,8 +496,11 @@ module Smplkit
|
|
|
451
496
|
# Build the parent-chain for a given config, walking +parent_id+
|
|
452
497
|
# pointers across the full config list. Mirrors the Python SDK's
|
|
453
498
|
# client-side resolution — there is no server +/chain+ endpoint.
|
|
499
|
+
#
|
|
500
|
+
# Walks every page of +list_configs+ so an account with more than
|
|
501
|
+
# +RUNTIME_PAGE_SIZE+ configs still resolves chains correctly.
|
|
454
502
|
def fetch_chain(target_key)
|
|
455
|
-
all_configs =
|
|
503
|
+
all_configs = fetch_all_configs
|
|
456
504
|
by_key = all_configs.to_h { |c| [c.key, c] }
|
|
457
505
|
by_id = all_configs.to_h { |c| [c.id, c] }
|
|
458
506
|
|
|
@@ -475,6 +523,11 @@ module Smplkit
|
|
|
475
523
|
|
|
476
524
|
private
|
|
477
525
|
|
|
526
|
+
def fetch_all_configs
|
|
527
|
+
rows = PaginatedFetch.collect { |opts| @api.list_configs(opts) }
|
|
528
|
+
rows.map { |r| Smplkit::Config::Helpers.config_from_json(self, ResourceShim.from_model(r)) }
|
|
529
|
+
end
|
|
530
|
+
|
|
478
531
|
def config_body(config)
|
|
479
532
|
SmplkitGeneratedClient::Config::ConfigResponse.new(
|
|
480
533
|
data: SmplkitGeneratedClient::Config::ConfigResource.new(
|
|
@@ -576,8 +629,11 @@ module Smplkit
|
|
|
576
629
|
@buffer.pending_count
|
|
577
630
|
end
|
|
578
631
|
|
|
579
|
-
def list
|
|
580
|
-
|
|
632
|
+
def list(page_number: nil, page_size: nil)
|
|
633
|
+
opts = {}
|
|
634
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
635
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
636
|
+
response = ErrorMapping.call { @api.list_flags(opts) }
|
|
581
637
|
(response.data || []).map { |r| flag_from_resource(ResourceShim.from_model(r)) }
|
|
582
638
|
end
|
|
583
639
|
|
|
@@ -634,9 +690,11 @@ module Smplkit
|
|
|
634
690
|
Smplkit::Flags::Helpers.flag_dict_from_json(ResourceShim.from_model(response.data))
|
|
635
691
|
end
|
|
636
692
|
|
|
693
|
+
# Runtime entry — walks every page so an account holding more than
|
|
694
|
+
# +RUNTIME_PAGE_SIZE+ flags still gets a complete in-memory store.
|
|
637
695
|
def list_flags
|
|
638
|
-
|
|
639
|
-
|
|
696
|
+
rows = PaginatedFetch.collect { |opts| @api.list_flags(opts) }
|
|
697
|
+
rows.map { |r| Smplkit::Flags::Helpers.flag_dict_from_json(ResourceShim.from_model(r)) }
|
|
640
698
|
end
|
|
641
699
|
|
|
642
700
|
private
|
|
@@ -731,8 +789,11 @@ module Smplkit
|
|
|
731
789
|
Smplkit.debug("registration", "logger flush failed: #{e.class}: #{e.message}")
|
|
732
790
|
end
|
|
733
791
|
|
|
734
|
-
def list
|
|
735
|
-
|
|
792
|
+
def list(page_number: nil, page_size: nil)
|
|
793
|
+
opts = {}
|
|
794
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
795
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
796
|
+
response = ErrorMapping.call { @api.list_loggers(opts) }
|
|
736
797
|
(response.data || []).map do |r|
|
|
737
798
|
Smplkit::Logging::Helpers.logger_resource_to_model(self, ResourceShim.from_model(r))
|
|
738
799
|
end
|
|
@@ -755,8 +816,38 @@ module Smplkit
|
|
|
755
816
|
Smplkit::Logging::Helpers.logger_resource_to_model(self, ResourceShim.from_model(response.data))
|
|
756
817
|
end
|
|
757
818
|
|
|
819
|
+
# Runtime entry — walks every page and returns an id-keyed Hash of
|
|
820
|
+
# resolution-cache entries (+level+, +group+, +managed+,
|
|
821
|
+
# +environments+). Mirrors the Python SDK's
|
|
822
|
+
# +LoggingClient._fetch_and_apply+ loggers branch.
|
|
823
|
+
def list_logger_entries
|
|
824
|
+
rows = PaginatedFetch.collect { |opts| @api.list_loggers(opts) }
|
|
825
|
+
rows.to_h { |r| logger_entry_from_resource(ResourceShim.from_model(r)) }
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
# Fetch one logger as a resolution-cache entry. Used by the
|
|
829
|
+
# +logger_changed+ WS handler.
|
|
830
|
+
def get_logger_entry(id)
|
|
831
|
+
normalized = Smplkit::Logging::Normalize.normalize_logger_name(id)
|
|
832
|
+
response = ErrorMapping.call { @api.get_logger(normalized) }
|
|
833
|
+
logger_entry_from_resource(ResourceShim.from_model(response.data))
|
|
834
|
+
end
|
|
835
|
+
|
|
758
836
|
private
|
|
759
837
|
|
|
838
|
+
def logger_entry_from_resource(resource)
|
|
839
|
+
attrs = resource["attributes"] || {}
|
|
840
|
+
[
|
|
841
|
+
resource["id"],
|
|
842
|
+
{
|
|
843
|
+
"level" => attrs["level"],
|
|
844
|
+
"group" => attrs["group"],
|
|
845
|
+
"managed" => attrs.key?("managed") ? attrs["managed"] : true,
|
|
846
|
+
"environments" => attrs["environments"] || {}
|
|
847
|
+
}
|
|
848
|
+
]
|
|
849
|
+
end
|
|
850
|
+
|
|
760
851
|
def logger_body(logger)
|
|
761
852
|
# Logger server schema: name, level, group, managed.
|
|
762
853
|
# +resolved_level+ is read-only, +service+/+environment+ are
|
|
@@ -781,8 +872,11 @@ module Smplkit
|
|
|
781
872
|
@api = SmplkitGeneratedClient::Logging::LogGroupsApi.new(api_client)
|
|
782
873
|
end
|
|
783
874
|
|
|
784
|
-
def list
|
|
785
|
-
|
|
875
|
+
def list(page_number: nil, page_size: nil)
|
|
876
|
+
opts = {}
|
|
877
|
+
opts[:page_number] = page_number unless page_number.nil?
|
|
878
|
+
opts[:page_size] = page_size unless page_size.nil?
|
|
879
|
+
response = ErrorMapping.call { @api.list_log_groups(opts) }
|
|
786
880
|
(response.data || []).map do |r|
|
|
787
881
|
Smplkit::Logging::Helpers.log_group_resource_to_model(self, ResourceShim.from_model(r))
|
|
788
882
|
end
|
|
@@ -816,8 +910,35 @@ module Smplkit
|
|
|
816
910
|
Smplkit::Logging::Helpers.log_group_resource_to_model(self, ResourceShim.from_model(response.data))
|
|
817
911
|
end
|
|
818
912
|
|
|
913
|
+
# Runtime entry — walks every page and returns an id-keyed Hash of
|
|
914
|
+
# resolution-cache entries (+level+, +group+, +environments+). The
|
|
915
|
+
# +group+ key carries the *parent group id* so the resolution
|
|
916
|
+
# algorithm can walk the chain with the same key shape it uses for
|
|
917
|
+
# loggers.
|
|
918
|
+
def list_group_entries
|
|
919
|
+
rows = PaginatedFetch.collect { |opts| @api.list_log_groups(opts) }
|
|
920
|
+
rows.to_h { |r| group_entry_from_resource(ResourceShim.from_model(r)) }
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
def get_group_entry(key)
|
|
924
|
+
response = ErrorMapping.call { @api.get_log_group(key) }
|
|
925
|
+
group_entry_from_resource(ResourceShim.from_model(response.data))
|
|
926
|
+
end
|
|
927
|
+
|
|
819
928
|
private
|
|
820
929
|
|
|
930
|
+
def group_entry_from_resource(resource)
|
|
931
|
+
attrs = resource["attributes"] || {}
|
|
932
|
+
[
|
|
933
|
+
resource["id"],
|
|
934
|
+
{
|
|
935
|
+
"level" => attrs["level"],
|
|
936
|
+
"group" => attrs["parent_id"],
|
|
937
|
+
"environments" => attrs["environments"] || {}
|
|
938
|
+
}
|
|
939
|
+
]
|
|
940
|
+
end
|
|
941
|
+
|
|
821
942
|
def log_group_body(group)
|
|
822
943
|
# LogGroup server schema: name, level, parent_id (no description).
|
|
823
944
|
SmplkitGeneratedClient::Logging::LogGroupResponse.new(
|
data/lib/smplkit.rb
CHANGED
|
@@ -55,6 +55,7 @@ require_relative "smplkit/logging/normalize"
|
|
|
55
55
|
require_relative "smplkit/logging/sources"
|
|
56
56
|
require_relative "smplkit/logging/models"
|
|
57
57
|
require_relative "smplkit/logging/helpers"
|
|
58
|
+
require_relative "smplkit/logging/resolution"
|
|
58
59
|
require_relative "smplkit/logging/adapters/base"
|
|
59
60
|
require_relative "smplkit/logging/adapters/stdlib_logger_adapter"
|
|
60
61
|
require_relative "smplkit/logging/client"
|
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.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Smpl Solutions LLC
|
|
@@ -701,6 +701,7 @@ files:
|
|
|
701
701
|
- lib/smplkit/logging/levels.rb
|
|
702
702
|
- lib/smplkit/logging/models.rb
|
|
703
703
|
- lib/smplkit/logging/normalize.rb
|
|
704
|
+
- lib/smplkit/logging/resolution.rb
|
|
704
705
|
- lib/smplkit/logging/sources.rb
|
|
705
706
|
- lib/smplkit/management/audit.rb
|
|
706
707
|
- lib/smplkit/management/buffer.rb
|