Class: Quonfig::Client
- Inherits:
-
Object
- Object
- Quonfig::Client
- Defined in:
- lib/quonfig/client.rb
Overview
Public Quonfig SDK client.
Wires the JSON stack: Quonfig::ConfigStore + Quonfig::Evaluator + Quonfig::Resolver. Three modes are supported:
datadir:(offline) -- load a workspace from the local filesystem.store:(test harness) -- caller-supplied ConfigStore, no I/O.- network mode (default) -- HTTP fetch from
api_urlspopulates the ConfigStore, then (if enabled) an SSE subscription keeps it live.
Network mode is the happy path for production SDK usage. The protobuf stack was retired in qfg-dk6.32; HTTP + SSE were wired back through Client in qfg-s7h.
Constant Summary collapse
- LOG =
Quonfig::InternalLogger.new(self)
Instance Attribute Summary collapse
-
#config_loader ⇒ Object
readonly
Returns the value of attribute config_loader.
-
#evaluator ⇒ Object
readonly
Returns the value of attribute evaluator.
-
#instance_hash ⇒ Object
readonly
Returns the value of attribute instance_hash.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#resolver ⇒ Object
readonly
Returns the value of attribute resolver.
-
#store ⇒ Object
readonly
Returns the value of attribute store.
-
#telemetry_reporter ⇒ Object
readonly
Returns the value of attribute telemetry_reporter.
Class Method Summary collapse
-
.each_instance(&block) ⇒ Object
Iterate live Client instances.
- .register_instance(client) ⇒ Object
Instance Method Summary collapse
-
#after_fork_in_child ⇒ Object
qfg-ryov: post-fork (in child) hook.
-
#before_fork_in_parent ⇒ Object
qfg-ryov: pre-fork hook.
-
#config_install_count ⇒ Object
Count of envelopes actually installed.
-
#connection_state ⇒ Object
Aggregate connection state.
- #defined?(key) ⇒ Boolean
- #enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED) ⇒ Boolean
- #fork ⇒ Object
-
#get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED) ⇒ Object
---- Lookup --------------------------------------------------------.
- #get_bool(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
-
#get_bool_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
---- Details getters ----------------------------------------------.
- #get_duration(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_float(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_float_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_int(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_int_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_json(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_json_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_string(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_string_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_string_list(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
- #get_string_list_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
-
#held_generation ⇒ Object
Meta.generation of the currently-held envelope (0 before the first install or when the backend does not emit a generation).
-
#in_context(properties, &block) ⇒ Object
deprecated
Deprecated.
Use #with_context instead.
-
#initialize(options = nil, store: nil, **option_kwargs) ⇒ Client
constructor
A new instance of Client.
- #inspect ⇒ Object
- #keys ⇒ Object
-
#last_successful_refresh ⇒ Object
Wall-clock time of the last installed envelope (any source: datadir, initial HTTP fetch, SSE, or polling fallback).
-
#logger_key ⇒ Object
The configured
logger_keyfrom Options — the Quonfig config key the higher-levelshould_log?helper evaluates per-logger. - #on_update(&block) ⇒ Object
-
#ready? ⇒ Boolean
True once the SDK has installed at least one envelope (any source).
-
#resolved_from ⇒ Object
'primary' / 'secondary' / '' — which config_api_urls leg produced the currently-held config.
-
#semantic_logger_filter(config_key:) ⇒ Object
---- Filters & helpers --------------------------------------------.
-
#should_log?(logger_path:, desired_level:, contexts: {}) ⇒ Boolean
Higher-level log-level check — a convenience on top of the primitive
get. -
#sse_failed_over_to_secondary? ⇒ Boolean
True if the live SSE stream has ever repointed to a non-primary leg.
-
#stdlib_formatter(logger_name: nil) ⇒ Proc
Build a formatter Proc for Ruby's built-in
::Logger. - #stop ⇒ Object
-
#terminal_failure? ⇒ Boolean
qfg-i5xv: true once the SSE layer has classified an HTTP response as terminal (401/403/404) — bad SDK key, revoked workspace permission, or wrong endpoint.
-
#with_context(properties) ⇒ Object
Bind
propertiesas a context. -
#worker_restart_total(layer: nil) ⇒ Object
quonfig_sdk_worker_restart_total counter (Tier 1 supervisor contract).
Constructor Details
#initialize(options = nil, store: nil, **option_kwargs) ⇒ Client
Returns a new instance of Client.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/quonfig/client.rb', line 49 def initialize( = nil, store: nil, **option_kwargs) @options = if .is_a?(Quonfig::Options) elsif .is_a?(Hash) Quonfig::Options.new(.merge(option_kwargs)) else Quonfig::Options.new(option_kwargs) end Quonfig::InternalLogger.user_logger = @options.logger if @options.logger @global_context = build_initial_global_context(@options) @instance_hash = SecureRandom.uuid @store = store || Quonfig::ConfigStore.new @evaluator = Quonfig::Evaluator.new(@store, env_id: @options.environment) @resolver = Quonfig::Resolver.new(@store, @evaluator) @semantic_logger_filters = {} @sse_client = nil @poll_supervisor = nil @stopped = false @telemetry_reporter = nil # Shared failover-telemetry aggregator (qfg-41nh.18). Created once and # shared between the ConfigLoader (which records hedge-fired / # guard-rejected / resolved-from at the failover call sites) and the # TelemetryReporter (which drains it on each flush). Created before the # ConfigLoader so the initial HTTP fetch — often the ONLY HTTP fetch when # SSE is healthy — is captured. Recording is a per-config-refresh mutex + # increment (negligible); nothing is EMITTED unless telemetry is enabled # (the reporter, which owns the drain, only starts then). Survives fork: # the pre-fork stop drains it, the child's rebuilt reporter drains onward. @failover_aggregator = Quonfig::Telemetry::FailoverAggregator.new @state_mutex = Mutex.new @last_successful_refresh = nil @sse_state = :idle @sse_ever_connected = false @fallback_engage_timer = nil @sse_terminal_failure = false # If the caller injected a store, we're in test/bootstrap mode; skip I/O. return if store if @options.datadir load_datadir_into_store start_datadir_watcher if @options.data_dir_auto_reload else initialize_network_mode end initialize_telemetry # Register only for non-store-injected clients (a caller-supplied store # is the test/bootstrap path; the fork hook does not apply there). self.class.register_instance(self) unless store end |
Instance Attribute Details
#config_loader ⇒ Object (readonly)
Returns the value of attribute config_loader.
46 47 48 |
# File 'lib/quonfig/client.rb', line 46 def config_loader @config_loader end |
#evaluator ⇒ Object (readonly)
Returns the value of attribute evaluator.
46 47 48 |
# File 'lib/quonfig/client.rb', line 46 def evaluator @evaluator end |
#instance_hash ⇒ Object (readonly)
Returns the value of attribute instance_hash.
46 47 48 |
# File 'lib/quonfig/client.rb', line 46 def instance_hash @instance_hash end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
46 47 48 |
# File 'lib/quonfig/client.rb', line 46 def @options end |
#resolver ⇒ Object (readonly)
Returns the value of attribute resolver.
46 47 48 |
# File 'lib/quonfig/client.rb', line 46 def resolver @resolver end |
#store ⇒ Object (readonly)
Returns the value of attribute store.
46 47 48 |
# File 'lib/quonfig/client.rb', line 46 def store @store end |
#telemetry_reporter ⇒ Object (readonly)
Returns the value of attribute telemetry_reporter.
46 47 48 |
# File 'lib/quonfig/client.rb', line 46 def telemetry_reporter @telemetry_reporter end |
Class Method Details
.each_instance(&block) ⇒ Object
Iterate live Client instances. Used by Quonfig::ForkSafety.
37 38 39 |
# File 'lib/quonfig/client.rb', line 37 def each_instance(&block) @instances_mutex.synchronize { @instances.keys }.each(&block) end |
.register_instance(client) ⇒ Object
41 42 43 |
# File 'lib/quonfig/client.rb', line 41 def register_instance(client) @instances_mutex.synchronize { @instances[client] = true } end |
Instance Method Details
#after_fork_in_child ⇒ Object
qfg-ryov: post-fork (in child) hook. Re-establish whatever threaded components the client had pre-fork. No-op if the client was already stopped (the customer asked for it to be dead — do not resurrect), or if the client is in datadir mode (no threaded components to start).
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/quonfig/client.rb', line 338 def after_fork_in_child return if @stopped if @options.datadir start_datadir_watcher if @options.data_dir_auto_reload return end return if @config_loader.nil? # never finished network init (e.g. invalid key) # SSE state machine carries flags that no longer apply in the child # (the parent had connected, the parent had errored, etc.). Reset. @state_mutex.synchronize do @sse_state = :idle @sse_ever_connected = false @sse_terminal_failure = false end sse_started = @options.enable_sse && start_sse start_polling if @options.fallback_poll_enabled && !sse_started restart_telemetry_in_child end |
#before_fork_in_parent ⇒ Object
qfg-ryov: pre-fork hook. Close the SSE worker, polling supervisor, telemetry reporter, and any fallback-engage timer. Idempotent — calling twice is safe. Does NOT set @stopped: the client is still expected to be usable post-fork via after_fork_in_child.
Why this matters: Ruby threads do not survive fork(2). If we let the child inherit a live Net::HTTP socket, both processes read from the same fd and corrupt each other's bytes. Closing in the parent before fork is the only safe shape.
328 329 330 331 332 |
# File 'lib/quonfig/client.rb', line 328 def before_fork_in_parent return if @stopped tear_down_threaded_components! end |
#config_install_count ⇒ Object
Count of envelopes actually installed. Rejected-older and same-generation snapshots do NOT bump this, so o04 can assert "no flap" via a stable count.
444 445 446 |
# File 'lib/quonfig/client.rb', line 444 def config_install_count @config_loader&.install_count || 0 end |
#connection_state ⇒ Object
Aggregate connection state. Returns one of:
:initializing— no envelope has been installed and SSE is not yet connected.:connected— SSE is live, or the SDK is delivering configs from a loaded envelope (datadir mode or post-initial-fetch with no SSE).:disconnected—stopwas called, or SSE errored and no fallback poller is active.:falling_back— the Layer 2 HTTP polling supervisor is alive and serving as the active update channel.
Diagnostic only. Do NOT wire this into a Kubernetes liveness probe — see the README "Diagnostic health signals" section.
Contract: integration-test-data/chaos/supervisor-test-contract.md (Test 6).
409 410 411 412 413 414 415 416 417 418 419 420 |
# File 'lib/quonfig/client.rb', line 409 def connection_state @state_mutex.synchronize do next :disconnected if @stopped next :falling_back if @poll_supervisor&.alive? next :connected if @sse_state == :connected next :disconnected if @sse_state == :error # No SSE state change yet: state is driven by whether any envelope # has been installed (datadir / initial fetch). @last_successful_refresh.nil? ? :initializing : :connected end end |
#defined?(key) ⇒ Boolean
188 189 190 |
# File 'lib/quonfig/client.rb', line 188 def defined?(key) !@store.get(key).nil? end |
#enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED) ⇒ Boolean
183 184 185 186 |
# File 'lib/quonfig/client.rb', line 183 def enabled?(feature_name, jit_context = NO_DEFAULT_PROVIDED) value = get(feature_name, false, jit_context) [true, 'true'].include?(value) end |
#fork ⇒ Object
464 465 466 |
# File 'lib/quonfig/client.rb', line 464 def fork self.class.new(@options.for_fork) end |
#get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED) ⇒ Object
---- Lookup --------------------------------------------------------
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/quonfig/client.rb', line 105 def get(key, default = NO_DEFAULT_PROVIDED, jit_context = NO_DEFAULT_PROVIDED) ctx = build_context(jit_context) record_context_for_telemetry(ctx) result = begin @resolver.get(key, ctx) rescue Quonfig::Errors::MissingDefaultError # The Resolver raises (matching Quonfig.get_or_raise semantics). # The Client's get applies the caller-provided default *or* the # configured on_no_default policy via handle_missing. nil end return handle_missing(key, default) if result.nil? record_evaluation_for_telemetry(result) result.unwrapped_value end |
#get_bool(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
135 136 137 |
# File 'lib/quonfig/client.rb', line 135 def get_bool(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :bool, default: default, context: context) end |
#get_bool_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
---- Details getters ----------------------------------------------
Mirrors the typed getters above but returns a Quonfig::EvaluationDetails
carrying the OpenFeature-aligned resolution reason ("STATIC",
"TARGETING_MATCH", "SPLIT", "DEFAULT", or "ERROR") plus an
+error_code+/+error_message+ on the error path. These methods never
raise — exceptions are caught and rendered as ERROR details.
159 160 161 |
# File 'lib/quonfig/client.rb', line 159 def get_bool_details(key, context: NO_DEFAULT_PROVIDED) evaluate_details(key, :bool, context) end |
#get_duration(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
143 144 145 |
# File 'lib/quonfig/client.rb', line 143 def get_duration(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :duration, default: default, context: context) end |
#get_float(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
131 132 133 |
# File 'lib/quonfig/client.rb', line 131 def get_float(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, Float, default: default, context: context) end |
#get_float_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
171 172 173 |
# File 'lib/quonfig/client.rb', line 171 def get_float_details(key, context: NO_DEFAULT_PROVIDED) evaluate_details(key, Float, context) end |
#get_int(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
127 128 129 |
# File 'lib/quonfig/client.rb', line 127 def get_int(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, Integer, default: default, context: context) end |
#get_int_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
167 168 169 |
# File 'lib/quonfig/client.rb', line 167 def get_int_details(key, context: NO_DEFAULT_PROVIDED) evaluate_details(key, Integer, context) end |
#get_json(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
147 148 149 |
# File 'lib/quonfig/client.rb', line 147 def get_json(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :json, default: default, context: context) end |
#get_json_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
179 180 181 |
# File 'lib/quonfig/client.rb', line 179 def get_json_details(key, context: NO_DEFAULT_PROVIDED) evaluate_details(key, :json, context) end |
#get_string(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
123 124 125 |
# File 'lib/quonfig/client.rb', line 123 def get_string(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, String, default: default, context: context) end |
#get_string_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
163 164 165 |
# File 'lib/quonfig/client.rb', line 163 def get_string_details(key, context: NO_DEFAULT_PROVIDED) evaluate_details(key, String, context) end |
#get_string_list(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) ⇒ Object
139 140 141 |
# File 'lib/quonfig/client.rb', line 139 def get_string_list(key, default: NO_DEFAULT_PROVIDED, context: NO_DEFAULT_PROVIDED) typed_get(key, :string_list, default: default, context: context) end |
#get_string_list_details(key, context: NO_DEFAULT_PROVIDED) ⇒ Object
175 176 177 |
# File 'lib/quonfig/client.rb', line 175 def get_string_list_details(key, context: NO_DEFAULT_PROVIDED) evaluate_details(key, :string_list, context) end |
#held_generation ⇒ Object
Meta.generation of the currently-held envelope (0 before the first install or when the backend does not emit a generation). Canonical ordering: an established client never regresses to a lower generation.
438 439 440 |
# File 'lib/quonfig/client.rb', line 438 def held_generation @config_loader&.held_generation || 0 end |
#in_context(properties, &block) ⇒ Object
Use #with_context instead.
Bind properties as a context. With a block, yields a
BoundClient and returns the block's value. Without a block,
returns the BoundClient directly.
qfg-e0kk: kept as a deprecated alias of #with_context. The two methods
have always been runtime-identical; sdk-1.0 unifies on with_context
across all SDKs. No runtime warning is emitted (Prefab-fork lineage,
heavy customer usage). Slated for removal in 2.0.0.
208 209 210 |
# File 'lib/quonfig/client.rb', line 208 def in_context(properties, &block) with_context(properties, &block) end |
#inspect ⇒ Object
468 469 470 |
# File 'lib/quonfig/client.rb', line 468 def inspect "#<Quonfig::Client:#{object_id} environment=#{@options.environment.inspect}>" end |
#keys ⇒ Object
192 193 194 |
# File 'lib/quonfig/client.rb', line 192 def keys @store.keys end |
#last_successful_refresh ⇒ Object
Wall-clock time of the last installed envelope (any source: datadir,
initial HTTP fetch, SSE, or polling fallback). nil before the first
install. Preserved after stop.
Diagnostic only. Do NOT wire this into a Kubernetes liveness probe — a transient network blip will trip any freshness threshold and cause a rolling restart cascade. See the README "Diagnostic health signals" section.
Contract: integration-test-data/chaos/supervisor-test-contract.md (Test 6).
390 391 392 |
# File 'lib/quonfig/client.rb', line 390 def last_successful_refresh @state_mutex.synchronize { @last_successful_refresh } end |
#logger_key ⇒ Object
The configured logger_key from Options — the Quonfig config key the
higher-level should_log? helper evaluates per-logger. nil if the
client was not configured for dynamic log levels.
254 255 256 |
# File 'lib/quonfig/client.rb', line 254 def logger_key @options.logger_key end |
#on_update(&block) ⇒ Object
310 311 312 |
# File 'lib/quonfig/client.rb', line 310 def on_update(&block) @on_update = block end |
#ready? ⇒ Boolean
True once the SDK has installed at least one envelope (any source). The failover scenarios assert the client reaches readiness off the secondary leg inside the init budget even when the primary is refused/hung/slow.
431 432 433 |
# File 'lib/quonfig/client.rb', line 431 def ready? !last_successful_refresh.nil? end |
#resolved_from ⇒ Object
'primary' / 'secondary' / '' — which config_api_urls leg produced the currently-held config. Used to assert HTTP config-fetch failover (f01-f04).
450 451 452 |
# File 'lib/quonfig/client.rb', line 450 def resolved_from @config_loader&.resolved_from || '' end |
#semantic_logger_filter(config_key:) ⇒ Object
---- Filters & helpers --------------------------------------------
223 224 225 226 |
# File 'lib/quonfig/client.rb', line 223 def semantic_logger_filter(config_key:) @semantic_logger_filters[config_key] ||= Quonfig::SemanticLoggerFilter.new(self, config_key: config_key) end |
#should_log?(logger_path:, desired_level:, contexts: {}) ⇒ Boolean
Higher-level log-level check — a convenience on top of the primitive
get. Evaluates the client's logger_key config and returns whether
a message at desired_level should be emitted for logger_path.
The SDK injects logger_path under the quonfig-sdk-logging named
context with property key so a single log-level config can drive
per-logger overrides via the normal rule engine (e.g.
PROP_STARTS_WITH_ONE_OF "MyApp::Services::").
logger_path is passed through verbatim — the SDK does not normalize
it. Callers may pass any identifier shape their host language prefers
(dotted, colon, slash, etc.) and author matching rules in the config
against that exact shape.
Parallels sdk-node's shouldLog({loggerPath}) and sdk-go's
ShouldLogPath.
Raises Quonfig::Error if logger_key was not set on the client —
use semantic_logger_filter(config_key:) directly if you want to
evaluate a specific key without declaring it at init time.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/quonfig/client.rb', line 285 def should_log?(logger_path:, desired_level:, contexts: {}) unless logger_key raise Quonfig::Error, 'logger_key must be set at init to use should_log?(logger_path:, ...). ' \ 'Pass `logger_key:` to Quonfig::Options.new, or call ' \ 'semantic_logger_filter(config_key:) / get(config_key) directly.' end logger_context = { Quonfig::SemanticLoggerFilter::LOGGER_CONTEXT_NAME => { Quonfig::SemanticLoggerFilter::LOGGER_CONTEXT_KEY_PROP => logger_path } } merged = merge_contexts(normalize_context(contexts), logger_context) configured = get(logger_key, nil, merged) return true if configured.nil? desired_severity = Quonfig::SemanticLoggerFilter::LEVELS[normalize_log_level(desired_level)] || Quonfig::SemanticLoggerFilter::LEVELS[:debug] min_severity = Quonfig::SemanticLoggerFilter::LEVELS[normalize_log_level(configured)] || Quonfig::SemanticLoggerFilter::LEVELS[:debug] desired_severity >= min_severity end |
#sse_failed_over_to_secondary? ⇒ Boolean
True if the live SSE stream has ever repointed to a non-primary leg. The failover epic asserts this stays false (f05): SSE does not fail over.
456 457 458 459 460 461 462 |
# File 'lib/quonfig/client.rb', line 456 def sse_failed_over_to_secondary? sse = @sse_client return false if sse.nil? return false unless sse.respond_to?(:failed_over_to_secondary?) sse.failed_over_to_secondary? end |
#stdlib_formatter(logger_name: nil) ⇒ Proc
Build a formatter Proc for Ruby's built-in ::Logger. The returned
proc honors dynamic log levels from the client's logger_key config:
for each log call, it evaluates should_log? and either formats the
record or returns an empty string (suppressing output).
Matches ReforgeHQ's stdlib_formatter API name (snake_case).
Usage:
logger = ::Logger.new($stdout)
logger.formatter = client.stdlib_formatter # uses progname
logger.formatter = client.stdlib_formatter(logger_name: 'MyApp') # fixed name
Raises Quonfig::Error if logger_key was not set at init — parallels
+should_log?+'s behavior.
247 248 249 |
# File 'lib/quonfig/client.rb', line 247 def stdlib_formatter(logger_name: nil) Quonfig::StdlibFormatter.build(self, logger_name: logger_name) end |
#stop ⇒ Object
314 315 316 317 |
# File 'lib/quonfig/client.rb', line 314 def stop @stopped = true tear_down_threaded_components! end |
#terminal_failure? ⇒ Boolean
qfg-i5xv: true once the SSE layer has classified an HTTP response as
terminal (401/403/404) — bad SDK key, revoked workspace permission,
or wrong endpoint. The classification latches: the SDK will not
auto-recover, and a customer-supplied retry must rebuild the client.
Surfaced for operator alerting; connection_state still reports
:disconnected to honor the documented connection_state vocabulary
(supervisor-test-contract.md §"connectionState()" — values fixed).
609 610 611 |
# File 'lib/quonfig/client.rb', line 609 def terminal_failure? @state_mutex.synchronize { @sse_terminal_failure } end |
#with_context(properties) ⇒ Object
Bind properties as a context. With a block, yields a
BoundClient and returns the block's value. Without a block,
returns the BoundClient directly — useful for passing a context-bound
handle down the stack.
216 217 218 219 |
# File 'lib/quonfig/client.rb', line 216 def with_context(properties) bound = Quonfig::BoundClient.new(self, properties) block_given? ? yield(bound) : bound end |
#worker_restart_total(layer: nil) ⇒ Object
quonfig_sdk_worker_restart_total counter (Tier 1 supervisor contract). Layer 1 (SSE) is tracked on Quonfig::SSEConfigClient#restart_total — incremented once per reconnect attempt by the SDK-owned reconnect loop (qfg-35sm). Layer 2 (HTTP polling fallback) is wired through Quonfig::WorkerSupervisor.
Pass layer: ('1' or '2') to read a single layer; default returns the
sum across both layers so the chaos harness (and operators) can pull
per-layer values explicitly while preserving the previous single-number
diagnostic surface.
372 373 374 375 376 377 378 |
# File 'lib/quonfig/client.rb', line 372 def worker_restart_total(layer: nil) case layer&.to_s when '1' then sse_restart_total when '2' then poll_restart_total else sse_restart_total + poll_restart_total end end |