Class ModuleScanDiagnostics

java.lang.Object
com.ultikits.ultitools.utils.ModuleScanDiagnostics

@Internal public final class ModuleScanDiagnostics extends Object
Turns a silently-skipped class from a module's startup scan into a single, named, loud line an operator actually reads (D-19).

ComponentScanner and PluginManager both catch ClassNotFoundException | LinkageError per class during a module's scan and skip-and-continue — the module still loads, every other command still registers, and the one stale command silently does not exist. This class does not change that shape; it only makes it loud. Its API is deliberately small: recordSkippedClass(String, String, Throwable) accumulates one skipped class for the module currently being scanned, and emitSummary(String) — called once, after that module's scan loop finishes — turns the accumulated list into exactly ONE Level.SEVERE record naming the module, every skipped class, and pointing at COMPATIBILITY.md. A module that skips nothing never calls the emitter at all, so a healthy startup produces no output from this class whatsoever.

Never routes through the root logger. DIAGNOSTICS_LOGGER disables parent handlers, exactly as RemoteActionLog does and for the same measured reason: SystemLogHandler attaches only to the root (empty-name) logger and auto-reports any Level.SEVERE record carrying a Throwable into ErrorReportCollector — which would turn this per-module summary into an error report and risk the circular logging both classes' javadoc already warns about. The summary emitted by emitSummary(String) deliberately carries no Throwable for the same reason; the individual causes live only on the per-class Level.FINE detail recorded by recordSkippedClass(String, String, Throwable). Disabling parent handlers also disconnects this logger from the console/logs/latest.log sink the server installs on the root logger, so a dedicated ConsoleHandler is attached directly — see the static initializer below and 07-JAPICMP-BASELINE.md's "D-19 diagnostic observation" section for the real-server confirmation that this line reaches logs/latest.log.

Since:
6.3.0
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private static final Logger
     
    private static final Map<String,List<String>>
    One accumulator entry per module currently being scanned; cleared as each is emitted.
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    private
     
  • Method Summary

    Modifier and Type
    Method
    Description
    static void
    emitSummary(String moduleName)
    Emits ONE Level.SEVERE summary for moduleName if, and only if, at least one class was recorded for it since the last call — then resets the accumulator for that module, so a subsequent, cleanly-skip-free re-scan of the same module never repeats it.
    private static boolean
    isBlank(String value)
     
    static void
    recordSkippedClass(String moduleName, String className, Throwable cause)
    Records one class that failed to load during moduleName's scan, and immediately logs the per-class detail at Level.FINE with cause attached — matching the shape the GEN-07 audit evaluator (D-14) also uses, so the two diagnostics read as one pattern.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • DIAGNOSTICS_LOGGER

      private static final Logger DIAGNOSTICS_LOGGER
    • SKIPPED_CLASSES_BY_MODULE

      private static final Map<String,List<String>> SKIPPED_CLASSES_BY_MODULE
      One accumulator entry per module currently being scanned; cleared as each is emitted.
  • Constructor Details

    • ModuleScanDiagnostics

      private ModuleScanDiagnostics()
  • Method Details

    • recordSkippedClass

      public static void recordSkippedClass(String moduleName, String className, Throwable cause)
      Records one class that failed to load during moduleName's scan, and immediately logs the per-class detail at Level.FINE with cause attached — matching the shape the GEN-07 audit evaluator (D-14) also uses, so the two diagnostics read as one pattern.

      A null or blank moduleName or className is ignored entirely — neither the accumulator nor the FINE detail record it — rather than producing a summary entry reading the literal text "null".

      Parameters:
      moduleName - the module (or, from ComponentScanner, the base package) whose scan is in progress
      className - the class that failed to load
      cause - the exception the scan caught; may be null
    • emitSummary

      public static void emitSummary(String moduleName)
      Emits ONE Level.SEVERE summary for moduleName if, and only if, at least one class was recorded for it since the last call — then resets the accumulator for that module, so a subsequent, cleanly-skip-free re-scan of the same module never repeats it. A module with nothing recorded is a silent no-op: the emitter is never invoked at all.
      Parameters:
      moduleName - the module whose scan just finished
    • isBlank

      private static boolean isBlank(String value)