Class: Smplkit::Logging::LoggingClient
- Inherits:
-
Object
- Object
- Smplkit::Logging::LoggingClient
- Defined in:
- lib/smplkit/logging/client.rb
Overview
The Smpl Logging client (sync).
One client exposes the full surface, reachable as client.logging (Smplkit::Client) or constructed directly:
logging = Smplkit::LoggingClient.new(environment: "production", service: "my-svc")
logging.loggers.new("sqlalchemy.engine").save
logging.install
The CRUD surface (loggers / log_groups sub-clients) works immediately. register_adapter is a pre-install configuration call. The live surface (install / on_change / refresh) requires install first; calling on_change / refresh earlier raises NotInstalledError.
Instance Attribute Summary collapse
-
#log_groups ⇒ Object
readonly
Returns the value of attribute log_groups.
-
#loggers ⇒ Object
readonly
Returns the value of attribute loggers.
Class Method Summary collapse
-
.open(**kwargs) {|client| ... } ⇒ Object
Construct a
LoggingClient, yield it to the block, and close it on exit.
Instance Method Summary collapse
-
#adapters ⇒ Array<Adapters::Base>
Registered logging adapters.
-
#close ⇒ Object
(also: #_close)
Release resources — only those this client owns.
-
#initialize(api_key = nil, environment: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, parent: nil, transport: nil, metrics: nil) ⇒ LoggingClient
constructor
A new instance of LoggingClient.
-
#install ⇒ Object
Hook smplkit into the application’s logging machinery.
-
#on_change(name = nil, &block) ⇒ Object
Register a change listener.
-
#refresh ⇒ Object
Re-fetch all loggers and groups and fire listener events for any deltas.
-
#register_adapter(adapter) ⇒ Object
Register a logging adapter.
Constructor Details
#initialize(api_key = nil, environment: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, parent: nil, transport: nil, metrics: nil) ⇒ LoggingClient
Returns a new instance of LoggingClient.
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/smplkit/logging/client.rb', line 426 def initialize(api_key = nil, environment: nil, base_url: nil, profile: nil, base_domain: nil, scheme: nil, debug: nil, extra_headers: nil, parent: nil, transport: nil, metrics: nil) @parent = parent @metrics = metrics @environment = parent.nil? ? environment : parent._environment @service = parent&._service @standalone_api_key = nil if transport.nil? @logging_http, _app_http, @app_base_url, @standalone_api_key = Logging.logging_transport( api_key: api_key, base_url: base_url, profile: profile, base_domain: base_domain, scheme: scheme, debug: debug, extra_headers: extra_headers ) else @logging_http = transport @app_base_url = nil end # Discovery buffer is owned by this client; the loggers sub-client shares # it so discovery and explicit registration drain together. @buffer = LoggerRegistrationBuffer.new @loggers = LoggersClient.new(@logging_http, buffer: @buffer) @log_groups = LogGroupsClient.new(@logging_http) # Live-surface state. @connected = false @name_map = {} # original_name → normalized_id @loggers_cache = {} # id → logger data @groups_cache = {} # id → group data @global_listeners = [] @key_listeners = Hash.new { |h, k| h[k] = [] } @adapters = [] @explicit_adapters = false @ws_manager = nil @owns_ws = false @lock = Mutex.new end |
Instance Attribute Details
#log_groups ⇒ Object (readonly)
Returns the value of attribute log_groups.
424 425 426 |
# File 'lib/smplkit/logging/client.rb', line 424 def log_groups @log_groups end |
#loggers ⇒ Object (readonly)
Returns the value of attribute loggers.
424 425 426 |
# File 'lib/smplkit/logging/client.rb', line 424 def loggers @loggers end |
Class Method Details
.open(**kwargs) {|client| ... } ⇒ Object
Construct a LoggingClient, yield it to the block, and close it on exit.
Mirrors Ruby’s File.open block form: the client is closed automatically when the block returns or raises, so a standalone client’s owned transports and WebSocket are always torn down.
Smplkit::LoggingClient.open(environment: "production") do |logging|
logging.loggers.new("sqlalchemy.engine").save
logging.install
end
625 626 627 628 629 630 631 632 |
# File 'lib/smplkit/logging/client.rb', line 625 def self.open(**kwargs) client = new(**kwargs) begin yield client ensure client.close end end |
Instance Method Details
#adapters ⇒ Array<Adapters::Base>
Registered logging adapters.
486 487 488 |
# File 'lib/smplkit/logging/client.rb', line 486 def adapters @adapters.dup end |
#close ⇒ Object Also known as: _close
Release resources — only those this client owns.
Uninstalls the adapter hooks, unsubscribes from the WebSocket, and tears down the owned WebSocket (standalone install). A wired client borrows the parent’s transport and WebSocket and closes neither.
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 |
# File 'lib/smplkit/logging/client.rb', line 587 def close Smplkit.debug("lifecycle", "LoggingClient.close() called") @adapters.each do |adapter| adapter.uninstall_hook rescue StandardError => e Smplkit.debug("logging", "adapter #{adapter.name} uninstall_hook failed: #{e.class}: #{e.}") end if @ws_manager ws_handlers.each { |event, handler| @ws_manager.off(event, handler) } if @owns_ws @ws_manager.stop @owns_ws = false end @ws_manager = nil end @connected = false end |
#install ⇒ Object
Hook smplkit into the application’s logging machinery.
Loads adapters, scans existing loggers, applies levels from the smplkit server, and wires WebSocket handlers for live updates. This IS the explicit consent gate — on_change / refresh require it first.
Idempotent — safe to call multiple times.
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
# File 'lib/smplkit/logging/client.rb', line 499 def install Smplkit.debug("lifecycle", "LoggingClient.install() called") @parent&._ensure_started return self if @connected # 0. Load adapters @adapters = Logging.auto_load_adapters if @adapters.empty? # 1. Discover existing loggers from all adapters (keep discovery and hook # installation as two passes — discover every adapter before any hook is # live, mirroring the Python SDK). # rubocop:disable Style/CombinableLoops @adapters.each do |adapter| existing = adapter.discover existing.each do |name, explicit_level, effective_level| @name_map[name] = Normalize.normalize_logger_name(name) @loggers.register(loggersource_for(name, explicit_level, effective_level)) end rescue StandardError => e Smplkit.debug("logging", "adapter #{adapter.name} discover failed: #{e.class}: #{e.}") end # 2. Install continuous discovery hooks @adapters.each do |adapter| adapter.install_hook { |name, explicit, effective| on_new_logger(name, explicit, effective) } rescue StandardError => e Smplkit.debug("logging", "adapter #{adapter.name} install_hook failed: #{e.class}: #{e.}") end # rubocop:enable Style/CombinableLoops # 3. Flush initial batch begin @loggers.flush rescue StandardError => e Smplkit.debug("registration", "bulk logger registration failed: #{e.class}: #{e.}") end # 4-6. Fetch, resolve, apply begin fetch_and_apply(trigger: "install()") rescue StandardError => e Smplkit.debug("resolution", "failed to fetch/apply logging levels during connect " \ "(logging: #{@logging_http&.config&.host}): #{e.class}: #{e.}") end # 7. Register WebSocket event handlers for real-time level updates @ws_manager = ensure_ws ws_handlers.each { |event, handler| @ws_manager.on(event, &handler) } @connected = true self end |
#on_change(name = nil, &block) ⇒ Object
Register a change listener.
client.logging.on_change { |event| ... } # global
client.logging.on_change("sqlalchemy.engine") { |e| ... } # key-scoped
Requires install first; raises NotInstalledError otherwise.
561 562 563 564 565 566 567 568 569 570 571 |
# File 'lib/smplkit/logging/client.rb', line 561 def on_change(name = nil, &block) require_installed raise ArgumentError, "on_change requires a block" unless block if name.nil? @global_listeners << block else @key_listeners[name] << block end block end |
#refresh ⇒ Object
Re-fetch all loggers and groups and fire listener events for any deltas.
Requires install first; raises NotInstalledError otherwise.
576 577 578 579 580 |
# File 'lib/smplkit/logging/client.rb', line 576 def refresh require_installed Smplkit.debug("resolution", "refresh() called, triggering full resolution pass") fetch_and_apply_deltas(trigger: "refresh()", source: "manual") end |
#register_adapter(adapter) ⇒ Object
Register a logging adapter. Must be called before install().
If called at least once, auto-loading is disabled — only explicitly registered adapters are used. This is a pre-install configuration call: it is intentionally NOT gated by install.
471 472 473 474 475 476 477 478 479 480 |
# File 'lib/smplkit/logging/client.rb', line 471 def register_adapter(adapter) raise "Cannot register adapters after install()" if @connected unless adapter.is_a?(Adapters::Base) raise ArgumentError, "adapter must implement Smplkit::Logging::Adapters::Base" end @explicit_adapters = true @adapters << adapter self end |