Class EarlyLogCapture

java.lang.Object
java.util.logging.Handler
com.ultikits.ultitools.manager.EarlyLogCapture

@Internal public final class EarlyLogCapture extends Handler
Holds the log records written between UltiTools#onLoad() and the moment the panel's log stream starts (LogStreamManager#initialize), so they reach the panel too (#487).

Before 6.3.0 the stream's capture was attached inside initialize(), which runs only once the panel connection is open, so everything the server logged before it -- module loading, dependency resolution, the framework's own start-up diagnostics -- never reached the panel. This handler is attached to the java.util.logging root logger in onLoad(); when the stream starts, drainInto(java.util.logging.Handler, java.lang.Runnable) replays what it kept, oldest first, attaches the live handler, and detaches itself. LogStreamManager replays into the stream handler's replay path, so the transmitter delivers the early records in log_batch messages of at most 64 KiB, about one per second, whatever the live batch settings (as of 6.3.0); a live record can therefore arrive before the last early messages.

The buffer is bounded three ways, so a server that never connects to the panel cannot grow it: by record count, by an estimate of the bytes it holds, and by time -- after RELEASE_AFTER_MS without the stream starting it detaches itself and keeps nothing. A record past the count or byte bound is not kept and is counted; drainInto(java.util.logging.Handler, java.lang.Runnable) returns the count. It applies the live stream's default filters as records arrive (level INFO and above, the configured excluded-loggers), so it never holds a record the stream would not send; the live handler applies the current configuration again when the records are replayed.

Since:
6.3.0
  • Field Details

    • MAX_RECORDS

      static final int MAX_RECORDS
      Most records kept.
      See Also:
    • MAX_BYTES

      static final long MAX_BYTES
      Most bytes kept, estimated from each record's message, logger name and exception.
      See Also:
    • RELEASE_AFTER_MS

      public static final long RELEASE_AFTER_MS
      How long the capture waits for the stream to start before it releases what it holds.
    • THROWN_ESTIMATE_BYTES

      private static final int THROWN_ESTIMATE_BYTES
      Charged per record carrying an exception, whose stack trace the stream will render.
      See Also:
    • LOCK

      private static final Object LOCK
    • active

      private static EarlyLogCapture active
      The capture attached to the root logger, or null. Guarded by LOCK.
    • records

      private final Deque<LogRecord> records
    • excludedLoggers

      private final List<String> excludedLoggers
    • maxRecords

      private final int maxRecords
    • maxBytes

      private final long maxBytes
    • deadline

      private final long deadline
    • clock

      private final LongSupplier clock
    • bytes

      private long bytes
    • dropped

      private int dropped
    • draining

      private boolean draining
  • Constructor Details

    • EarlyLogCapture

      private EarlyLogCapture(Collection<String> excludedLoggers, int maxRecords, long maxBytes, long releaseAfterMs, LongSupplier clock)
  • Method Details

    • start

      public static void start(Collection<String> excludedLoggers)
      Attaches the capture to the root logger, replacing one already attached, whatever the logs capability says; UltiTools#onLoad() goes through startIfLogsEnabled(java.util.Collection<java.lang.String>).
      Parameters:
      excludedLoggers - the configured ultipanel.logging.excluded-loggers, applied as records arrive
    • startIfLogsEnabled

      public static boolean startIfLogsEnabled(Collection<String> excludedLoggers)
      Attaches the capture only when the panel receives the log stream: with ultipanel.capabilities.logs: false nothing is collected, as a disabled capability prevents collection rather than collecting and discarding (D-12). Called from UltiTools#onLoad(), after the plugin instance is set, so the configured value is read.
      Parameters:
      excludedLoggers - the configured ultipanel.logging.excluded-loggers, applied as records arrive
      Returns:
      whether the capture was attached
    • start

      static void start(Collection<String> excludedLoggers, int maxRecords, long maxBytes, long releaseAfterMs, LongSupplier clock)
    • drainInto

      public static int drainInto(Handler live, Runnable attachLive)
      Replays every record the capture kept to live, oldest first, then runs attachLive (which attaches the live handler to the root logger) and detaches the capture. Records logged while the replay runs are kept and replayed too, before the live handler is attached. When no capture is attached -- a reconnect after the first stream, or a capture already released -- this only runs attachLive.
      Parameters:
      live - the live stream's handler
      attachLive - attaches live to the root logger
      Returns:
      how many records the capture could not keep because a bound was reached
    • release

      public static void release()
      Detaches the capture and discards what it holds -- when the stream cannot start (the logs capability is off, or the framework is disabling) or its time is up.
    • releaseIfExpired

      public static void releaseIfExpired()
      Releases the capture if the stream did not start within RELEASE_AFTER_MS. Scheduled once from UltiTools#onEnable() so a quiet server does not wait for its next log line.
    • detachActive

      private static void detachActive()
    • publish

      public void publish(LogRecord record)
      Specified by:
      publish in class Handler
    • expired

      private boolean expired()
    • isExcluded

      private boolean isExcluded(String loggerName)
    • takeAll

      private List<LogRecord> takeAll()
    • estimateBytes

      private static long estimateBytes(LogRecord record)
    • flush

      public void flush()
      Specified by:
      flush in class Handler
    • close

      public void close()
      Specified by:
      close in class Handler