Class RemoteActionLog

java.lang.Object
com.ultikits.ultitools.manager.RemoteActionLog

@Internal public class RemoteActionLog extends Object
The framework's own durable record of what the panel actually did — every capability-gate decision, allowed or denied, as one JSON line in the active file under plugins/UltiTools/security/action.log.<generation>. Generation 0 is always the currently-active file — see init(File) for why the literal, suffix-free action.log never exists on disk.

Replaces the audit/enforce mode ROADMAP originally asked for (D-22): there is only one policy tier, so there is nothing to simulate, only something to record. This class is constructed unconditionally in UltiTools.initWebSocketManagers() and is not gated by any Capability — D-32.

Disabling this logger's parent handlers (see the static initializer below) is load-bearing. SystemLogHandler attaches only to the root logger (Logger.getLogger("")) — the sole attachment site is LogStreamManager.java line 95, rootLogger.addHandler(systemLogHandler). A non-root logger with parent handlers disabled therefore cannot reach SystemLogHandler and cannot create the feedback loop ErrorReportCollector's class javadoc warns about ("This class NEVER uses java.util.logging.Logger to prevent circular logging"). This class may therefore safely use Logger/FileHandler where ErrorReportCollector may not — that is a deliberate, narrower reading of the same hazard, not a violation of it. Internal failures of the log itself (handler creation, directory creation, config load) print to System.err, never through any Logger — the same discipline ErrorReportCollector uses for its own internal failures, for the same reason: a failure in the thing that logs must not try to log itself.

Since:
6.3.0
  • Field Details

    • LOGGER_NAME

      private static final String LOGGER_NAME
      See Also:
    • DEFAULT_MAX_SIZE_BYTES

      private static final int DEFAULT_MAX_SIZE_BYTES
      See Also:
    • DEFAULT_MAX_FILES

      private static final int DEFAULT_MAX_FILES
      See Also:
    • ACTION_LOG

      private static final Logger ACTION_LOG
    • gson

      private final com.google.gson.Gson gson
    • maxSizeBytes

      private volatile int maxSizeBytes
    • maxFiles

      private volatile int maxFiles
    • attachedHandler

      private volatile FileHandler attachedHandler
      The FileHandler this specific instance attached to the shared static ACTION_LOG — null until a successful init(File). Tracked per instance, not read back off ACTION_LOG itself, so shutdown() removes only the handler this instance is responsible for (WR-01) — never a sibling instance's handler, which matters if two instances are ever briefly alive at once.
  • Constructor Details

    • RemoteActionLog

      public RemoteActionLog()
  • Method Details

    • init

      public void init(File dataFolder)
      Attaches the rotating FileHandler at <dataFolder>/security/action.log.%g, creating the security directory if absent. Loads the rotation knobs from config.yml first — see loadConfiguration().

      The file on disk is never literally action.log. FileHandler always substitutes %g with the current generation number when the pattern contains it, and — confirmed empirically against this JDK's FileHandler, not merely inferred from its javadoc — it auto-appends the same suffix even if %g were removed from the pattern, because max-files defaults to 5 (count > 1): a generation suffix is unavoidable at any rotation-enabled setting, and only an operator setting max-files to 1 (disabling rotation entirely) would remove it. Generation 0 is always the currently-active file that new records append to; action.log.1, action.log.2, … are older rotated generations, oldest evicted first once max-files is reached. Every operator-facing reference to this file's path must say action.log.0 (the active file), not the bare, never-existing action.log — see UAT finding 5b.

      Parameters:
      dataFolder - the plugin's data folder (getDataFolder())
    • shutdown

      public void shutdown()
      Removes and closes the FileHandler this instance attached in init(File) (WR-01, 06-REVIEW.md).

      A no-op if init(File) was never called or failed to attach a handler (attachedHandler stays null). Flushes before closing so no buffered line is lost. Load-bearing for a Bukkit /reload: ACTION_LOG is a static Logger shared for the life of the JVM, so without this call a second onEnable's init(File) would attach a second FileHandler on top of this one, and every subsequent record(Entry) would be delivered to both — duplicating every action-log line from that point on, exactly the defect this method exists to prevent.

    • loadConfiguration

      private void loadConfiguration()
      Loads ultipanel.logging.action-log.max-size-bytes and .max-files, copying ErrorReportCollector.loadConfiguration()'s exact shape: UltiTools.getInstance() null-guard, instance.getConfig().getInt(path, default) per key, one try/catch whose failure branch prints to System.err and never to a Logger — this failure is about loading config for this very log, so it cannot log through itself. There is deliberately no enabled key here and no Capability consulted — D-32 makes this log's existence non-negotiable for the operator, a narrow exception to the D-04 no-floor rule (which governs which commands may run, not whether the record of what ran survives).
    • record

      public void record(RemoteActionLog.Entry entry)
      Records one entry. A no-op if entry is null.
      Parameters:
      entry - the entry to record