Class: ConvertSdk::LogManager

Inherits:
Object
  • Object
show all
Defined in:
lib/convert_sdk/log_manager.rb

Overview

Multi-sink, level-gated logger with secret redaction wired in by construction.

+LogManager+ is consumed by every manager from the HTTP client (Story 1.5) onward. It fans messages out to any number of stdlib-+Logger+-compatible sinks and guarantees, structurally, that no message reaches a sink without first passing through the Redactor: every public level method funnels through the single private +#emit+ path, and that path applies the +loggable+ conversion boundary and redaction before touching a sink. There is no public method that bypasses +#emit+.

== Levels

Verbosity is gated by the JS-parity LogLevel values (TRACE=0 … SILENT=5). A call at level +L+ emits only when +L >= configured_level+; +SILENT+ suppresses everything. The stdlib +Logger+ has no +trace+, so both #trace and #debug dispatch to the sink's +#debug+ — the numeric level value (0 vs 1), not the sink method, decides whether they emit.

Level conventions (callers choose the level by intent):

  • +trace+ / +debug+ — decisioning internals (bucketing, rule evaluation).
  • +info+ — lifecycle events (SDK ready, config refreshed).
  • +warn+ — recoverable conditions (stale config, retry).
  • +error+ — internal failures (parse error, exhausted retries).

== Message format

Callers pass messages already formatted as {ClassName}#{method}: {message}. +LogManager+ does not prepend the class name itself — the format is a usage convention, documented here and enforced at call sites.

== Thread safety

The sink list is guarded by +@sinks_mutex+. Compound operations on the list happen inside the lock; the (potentially slow, potentially raising) sink I/O happens outside the lock by iterating a +dup+ snapshot. A sink that raises is contained (rescue +StandardError+) so a broken sink never crashes the host or starves the other sinks.

Constant Summary collapse

REQUIRED_SINK_METHODS =

The methods every valid sink must respond to (stdlib +Logger+ contract).

%i[debug info warn error].freeze

Instance Method Summary collapse

Constructor Details

#initialize(level: LogLevel::ERROR, sink: nil, secrets: []) ⇒ LogManager

Returns a new instance of LogManager.

Parameters:

  • level (Integer) (defaults to: LogLevel::ERROR)

    a ConvertSdk::LogLevel threshold; messages below it are suppressed. Defaults to ERROR (quiet by default).

  • sink (Object, nil) (defaults to: nil)

    an optional initial sink (anything responding to debug/info/warn/error). Invalid sinks are rejected, not raised.

  • secrets (Array<String>) (defaults to: [])

    secret values to redact from every message. More can be added later via #register_secret.



50
51
52
53
54
55
56
57
# File 'lib/convert_sdk/log_manager.rb', line 50

def initialize(level: LogLevel::ERROR, sink: nil, secrets: [])
  @level = level
  @redactor = Redactor.new(secrets)
  @sinks = []
  # Thread safety: guarded by @sinks_mutex.
  @sinks_mutex = Thread::Mutex.new
  add_sink(sink) unless sink.nil?
end

Instance Method Details

#add_sink(sink) ⇒ self

Register a sink. Accepted iff it duck-types to the stdlib +Logger+ contract (responds to debug/info/warn/error). An invalid sink is rejected with a logged error rather than raising — registration must never crash the host.

Parameters:

  • sink (Object)

    the candidate sink.

Returns:

  • (self)

    for chaining. A rejected sink is logged, not registered.



69
70
71
72
73
74
75
76
77
# File 'lib/convert_sdk/log_manager.rb', line 69

def add_sink(sink)
  if REQUIRED_SINK_METHODS.all? { |m| sink.respond_to?(m) }
    @sinks_mutex.synchronize { @sinks << sink }
  else
    emit(LogLevel::ERROR, "LogManager#add_sink: rejected sink #{sink.class} " \
                          "(must respond to #{REQUIRED_SINK_METHODS.join("/")})")
  end
  self
end

#debug(message) ⇒ Object

Log at DEBUG — decisioning internals. Dispatches to sink +#debug+.



# File 'lib/convert_sdk/log_manager.rb', line 88

#error(message) ⇒ Object

Log at ERROR — internal failures.



# File 'lib/convert_sdk/log_manager.rb', line 88

#info(message) ⇒ Object

Log at INFO — lifecycle events.



# File 'lib/convert_sdk/log_manager.rb', line 88

#register_secret(secret) ⇒ void

This method returns an undefined value.

Register an additional secret to redact (e.g. once the SDK key is known at +ConvertSdk.create+ time). nil/blank is a no-op.

Parameters:

  • secret (String, nil)


84
85
86
# File 'lib/convert_sdk/log_manager.rb', line 84

def register_secret(secret)
  @redactor.register_secret(secret)
end

#trace(message) ⇒ void

This method returns an undefined value.

Returns log at TRACE (finest-grained); dispatches to the sink's +#debug+.

Parameters:

  • message (String)

    the already-formatted message.



# File 'lib/convert_sdk/log_manager.rb', line 88

#warn(message) ⇒ Object

Log at WARN — recoverable conditions.



# File 'lib/convert_sdk/log_manager.rb', line 88