freezeInheritance method

int freezeInheritance({
  1. bool force = false,
})

Freezes the current inherited configurations into descendant loggers.

"Bakes" this logger's effective configs down the logger tree where children have not explicitly set a field. Useful for performance (reduces resolution depth) or to snapshot state so future parent changes don't propagate dynamically.

Parameters:

  • force: When true, re-snapshots fields that are already frozen on descendants (updates them to this logger's current effective value). Explicit user overrides are never overwritten by force. Defaults to false (original behaviour — skips already-set fields).

Returns the total number of fields written across all affected descendants. A return value of 0 means the call was a complete no-op.

How to use:

  • Call on a logger to apply to all descendants recursively via registry.
  • force: true is useful after a parent config change to re-snapshot.

Example: final written = parentLogger.freezeInheritance(); parentLogger.freezeInheritance(force: true); // re-snapshot

Implementation

int freezeInheritance({final bool force = false}) {
  final callerConfig = _registry[name];
  if (callerConfig != null &&
      callerConfig.implicit &&
      callerConfig.frozenFields.isEmpty) {
    InternalLogger.log(
      LogLevel.warning,
      "freezeInheritance() called on implicit node '$name'. "
      'This node was never explicitly configured. The freeze will propagate '
      "resolved defaults. Consider calling Logger.configure('$name', ...) "
      'first if intentional.',
    );
  }

  int writtenCount = 0;
  for (final key in _registry.keys) {
    if (key == name || _isDescendant(key, name)) {
      final childConfig = _registry[key]!;
      bool changed = false;

      // Helper: should this field be written?
      // force=false → only if null; force=true → if null OR already frozen
      // (but never if explicitly set by user).
      bool shouldWrite({
        required final String field,
        required final bool isNull,
      }) =>
          isNull || (force && childConfig.frozenFields.contains(field));

      final nextFrozenFields = Set<String>.from(childConfig.frozenFields);
      bool? newEnabled = childConfig.enabled;
      LogLevel? newLogLevel = childConfig.logLevel;
      bool? newIncludeFileLineInHeader = childConfig.includeFileLineInHeader;
      bool? newIncludeOrigin = childConfig.includeOrigin;
      Map<LogLevel, int>? newStackMethodCount = childConfig.stackMethodCount;
      Timestamp? newTimestamp = childConfig.timestamp;
      StackTraceParser? newStackTraceParser = childConfig.stackTraceParser;
      List<Handler>? newHandlers = childConfig.handlers;
      bool? newAutoSinkBuffer = childConfig.autoSinkBuffer;

      if (shouldWrite(
        field: 'enabled',
        isNull: childConfig.enabled == null,
      )) {
        newEnabled = enabled;
        nextFrozenFields.add('enabled');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'logLevel',
        isNull: childConfig.logLevel == null,
      )) {
        newLogLevel = logLevel;
        nextFrozenFields.add('logLevel');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'includeFileLineInHeader',
        isNull: childConfig.includeFileLineInHeader == null,
      )) {
        newIncludeFileLineInHeader = includeFileLineInHeader;
        nextFrozenFields.add('includeFileLineInHeader');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'includeOrigin',
        isNull: childConfig.includeOrigin == null,
      )) {
        newIncludeOrigin = includeOrigin;
        nextFrozenFields.add('includeOrigin');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'stackMethodCount',
        isNull: childConfig.stackMethodCount == null,
      )) {
        newStackMethodCount = Map.from(stackMethodCount);
        nextFrozenFields.add('stackMethodCount');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'timestamp',
        isNull: childConfig.timestamp == null,
      )) {
        newTimestamp = timestamp;
        nextFrozenFields.add('timestamp');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'stackTraceParser',
        isNull: childConfig.stackTraceParser == null,
      )) {
        newStackTraceParser = stackTraceParser;
        nextFrozenFields.add('stackTraceParser');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'handlers',
        isNull: childConfig.handlers == null,
      )) {
        newHandlers = List.from(handlers);
        nextFrozenFields.add('handlers');
        changed = true;
        writtenCount++;
      }
      if (shouldWrite(
        field: 'autoSinkBuffer',
        isNull: childConfig.autoSinkBuffer == null,
      )) {
        newAutoSinkBuffer = autoSinkBuffer;
        nextFrozenFields.add('autoSinkBuffer');
        changed = true;
        writtenCount++;
      }
      if (changed) {
        _registry[key] = childConfig.copyWith(
          enabled: newEnabled,
          logLevel: newLogLevel,
          includeFileLineInHeader: newIncludeFileLineInHeader,
          includeOrigin: newIncludeOrigin,
          stackMethodCount: newStackMethodCount,
          timestamp: newTimestamp,
          stackTraceParser: newStackTraceParser,
          handlers: newHandlers,
          autoSinkBuffer: newAutoSinkBuffer,
          version: childConfig.version + 1,
          frozenFields: nextFrozenFields,
        );
        LoggerCache.invalidate(key);
      }
    }
  }
  return writtenCount;
}