Class ConsoleMirror
Why it exists. The stream's handler, SystemLogHandler, sits on the
java.util.logging root logger, which receives every plugin's lines. Paper prints its own
output through Log4j instead: command feedback (MinecraftServer#sendSystemMessage), a
module's reply to the console sender (TerminalConsoleCommandSender, a logger named after
Log4j's root logger), joins, chat, vanilla warnings and player command lines. Log4j never hands
those to java.util.logging, so before this class none of them reached the panel -- in
particular not the output of a command the panel itself sent.
How. An appender on Log4j's root logger converts each event into a LogRecord and
hands it to the stream handler currently attached to the java.util.logging root logger:
EarlyLogCapture before the panel connection opens, SystemLogHandler after. Every
filter of that path therefore applies unchanged -- excluded loggers, enabled levels, batching,
the start-up replay, the same-thread re-entry guard, error reporting. ANSI colour sequences are
removed, as Paper's own latest.log does.
No duplicates. Paper also forwards every java.util.logging record into Log4j,
through org.bukkit.craftbukkit.util.ForwardLogHandler on the root logger, so a plugin
line would otherwise arrive twice. The java.util.logging handler stays the only route
for those lines -- it is the one that sees the panel-connection mark
(PanelConnectionLog) and the levels below INFO -- and the appender skips what
Paper's forwarder produces: while installed, each forwarder is wrapped in a handler that marks
the current thread for the duration of its own publish, and Log4j calls the appender on
that same thread. Uninstalling puts the original forwarder back.
Asynchronous loggers. If the server's Log4j configuration uses asynchronous loggers, the appender runs on another thread, where neither the forwarder mark nor the re-entry guard holds. The mirror is then not installed and a warning says so; the stream carries the plugin lines only, as before 6.3.0.
No forwarder, no mirror (fail closed, UltiTools-Reborn#583 F1). The appender can tell a
forwarded plugin line from a console line only through the forwarder it wrapped. When no root
handler has the binary name org.bukkit.craftbukkit.util.ForwardLogHandler -- a fork, a build that relocates
CraftBukkit, something that replaced the forwarder -- nothing is wrapped, and an appender would
deliver every plugin line a second time (and stream panel-connection lines the panel must never
get back). The mirror is then not installed: install() returns false, nothing is
added to Log4j, and one WARNING per server run says the panel will not mirror the console; the
stream carries the plugin lines only, as before 6.3.0.
- Since:
- 6.3.0
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static final classMarks the current thread while Paper's forwarder copies a record into Log4j.private static final classThe appender on Log4j's root logger. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final PatternAn ANSI CSI sequence, such as a colour code.private static ConsoleMirror.MirrorAppenderThe installed appender, ornull.static final StringThe name of the appender on Log4j's root logger.private static org.apache.logging.log4j.core.LoggerContextThe Log4j context the appender is installed in.(package private) static final StringThe binary name of Paper'sjava.util.loggingto Log4j forwarder.private static booleanWhether the missing-forwarder WARNING was logged in this server run (#583 F1):install()runs again on every panel reconnect, and the outcome cannot change while the server runs.private static final ThreadLocal<int[]> How deep the current thread is inside a wrapped forwarder'spublish.private static final Object -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescription(package private) static voidHands a converted Log4j event to the stream handler attached to thejava.util.loggingroot logger, if any.static booleaninstall()Installs the mirror; does nothing when it is already installed.private static booleanstatic booleanWhether the mirror is installed.private static LeveltoJulLevel(org.apache.logging.log4j.Level level) (package private) static LogRecordtoRecord(org.apache.logging.log4j.core.LogEvent event) Converts a Log4j event into the record the stream handler expects.static voidRemoves the mirror and puts Paper's forwarder back; does nothing when it is not installed.private static voidprivate static booleanusesAsyncLoggers(org.apache.logging.log4j.core.LoggerContext ctx, org.apache.logging.log4j.core.config.Configuration configuration) private static voidprivate static intWraps every Paper forwarder on the root logger.
-
Field Details
-
APPENDER_NAME
The name of the appender on Log4j's root logger.- See Also:
-
FORWARD_LOG_HANDLER
The binary name of Paper'sjava.util.loggingto Log4j forwarder.- See Also:
-
ANSI
An ANSI CSI sequence, such as a colour code. -
FORWARDING
How deep the current thread is inside a wrapped forwarder'spublish. -
LOCK
-
appender
The installed appender, ornull. Guarded byLOCK. -
context
private static org.apache.logging.log4j.core.LoggerContext contextThe Log4j context the appender is installed in. Guarded byLOCK. -
forwarderMissingWarned
private static boolean forwarderMissingWarned
-
-
Constructor Details
-
ConsoleMirror
private ConsoleMirror()
-
-
Method Details
-
install
public static boolean install()Installs the mirror; does nothing when it is already installed. Not installed -- andfalsereturned -- when the server's Log4j uses asynchronous loggers, or when Paper's forwarder is not on the root logger (fail closed, see the class description).- Returns:
- whether the mirror is installed afterwards
-
installLocked
private static boolean installLocked() -
uninstall
public static void uninstall()Removes the mirror and puts Paper's forwarder back; does nothing when it is not installed. -
isInstalled
public static boolean isInstalled()Whether the mirror is installed.- Returns:
- whether the mirror is installed
-
usesAsyncLoggers
private static boolean usesAsyncLoggers(org.apache.logging.log4j.core.LoggerContext ctx, org.apache.logging.log4j.core.config.Configuration configuration) -
wrapForwarders
private static int wrapForwarders()Wraps every Paper forwarder on the root logger.- Returns:
- how many were wrapped;
0means none was found and nothing was changed
-
unwrapForwarders
private static void unwrapForwarders() -
deliver
Hands a converted Log4j event to the stream handler attached to thejava.util.loggingroot logger, if any. -
toRecord
Converts a Log4j event into the record the stream handler expects. -
toJulLevel
-
warn
-