Skip to content

Conversation

@stephan-gh
Copy link
Contributor

This is a set of 3 commits to improve the logging implementation of Paper and allow plugins to use SLF4J as alternative logging API. The exact changes and the reasoning for them is explained in the commit messages (either directly or in the patches) so I'm not going to repeat everything here.

SLF4J is a commonly used abstraction for various logging frameworks and it is the easiest way to handle logging in an implementation-agnostic way. It is also used by many Java libraries as logging abstraction. I intend to create a similar PR for Waterfall so eventually, the same logging API can be used on Paper, Waterfall, Sponge and other modding APIs.

The primary difficulty to make this work was to move the plugin prefixes out of a custom JUL PluginLogger implementation and handle them directly in the underlying logging framework (Log4J). Otherwise, messages logged with SLF4J would be missing the plugin prefix.

The solution is pretty simple: Use the plugin name as logger name and make it visible in the log using the Log4J configuration. Hide the logger name for Minecraft and Paper log messages to make it less verbose.

@kashike kashike self-requested a review September 21, 2017 16:50
@kashike kashike added the status: needs testing Issue needs verification. label Sep 21, 2017
private FileConfiguration newConfig = null;
private File configFile = null;
- private PluginLogger logger = null;
+ private Logger logger = null; // Paper: PluginLogger -> Logger
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Paper - PluginLogger -> Logger

this.classLoader = classLoader;
this.configFile = new File(dataFolder, "config.yml");
- this.logger = new PluginLogger(this);
+ this.logger = Logger.getLogger(description.getName()); // Paper: Handle plugin prefix in implementation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Paper - Handle plugin prefix in implementation

<scope>runtime</scope>
</dependency>

+ <!-- Paper: Add additional Log4J dependencies -->
Copy link
Member

@kashike kashike Sep 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<!-- Paper - Add additional Log4J dependencies -->

</relocation>
</relocations>
<transformers>
+ <!-- Paper: Exclude log4j2-test.xml -->
Copy link
Member

@kashike kashike Sep 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<!-- Paper - Exclude log4j2-test.xml -->

<scope>compile</scope>
</dependency>

+ <!-- Paper: Add SLF4J -->
Copy link
Member

@kashike kashike Sep 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<!-- Paper - Add SLF4J -->

{
playerSample = getInt( "settings.sample-count", 12 );
- System.out.println( "Server Ping Player Sample Count: " + playerSample );
+ Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper: Use logger
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Paper - Use logger

@kashike
Copy link
Member

kashike commented Sep 21, 2017

Have you tested this with some popular plugins, such as WorldEdit and EssentialsX?

@stephan-gh
Copy link
Contributor Author

stephan-gh commented Sep 21, 2017

@kashike I tested this with the incredibly popular Bukkittest plugin: https://gist.github.com/Minecrell/4de33b1b007dba9ce037907cd573c898. I'm sure it has at least as many downloads as Essentials and WorldEdit...

this.classLoader = classLoader;
this.configFile = new File(dataFolder, "config.yml");
- this.logger = new PluginLogger(this);
+ this.logger = Logger.getLogger(description.getName()); // Paper: Handle plugin prefix in implementation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be getPrefix().

@stephan-gh
Copy link
Contributor Author

@kashike All fixed.

I tested with Essentials and WorldEdit and after some work log messages are now visible. Essentials needs a workaround because it uses the Essentials logger internally instead of the plugin logger, and redirects its messages to the plugin logger by setting that as a parent. However, since the main plugin logger is now also called Essentials this results in an infinite loop. Fixed that by disallowing plugins to modify the parent of the plugin logger. (The warning can be avoided in Essentials by checking if getLogger() == LOGGER ).

WorldEdit works correctly but it doesn't use the plugin logger either so a different logger name is displayed in some cases.

Example log: https://gist.github.com/Minecrell/53eb99d4e06eb006b30f7a67decd7be9#file-gistfile1-txt-L104-L119

@kashike kashike self-assigned this Sep 21, 2017
@kashike kashike requested a review from zachbr September 21, 2017 18:36
@stephan-gh
Copy link
Contributor Author

stephan-gh commented Sep 21, 2017

Tested a "few" more plugins. All plugins are working but mostly older, large plugins bypass the plugin logger entirely and add the plugin prefix manually. Some of these plugins (marked with ❗️) will now show the full qualified logger name additionally. We could white-list the packages manually as a workaround...

[20:47:26] [Server thread/INFO] [com.sk89q.worldguard.bukkit.WorldConfiguration]: [WorldGuard] (world) TNT ignition is PERMITTED.
  • Essentials: ✔️ Added workaround (see comment above)
  • WorldEdit: ❗️ Doesn't use plugin logger, full qualified logger name is shown additionally.
  • WorldGuard: ❗️ Like WorldEdit
  • PermissionsEx: ❗️ Like WorldEdit
  • Vault: ✔️ Plugin main class / (✔️) Hooks, like Multiverse-Core
  • ProtocolLib: ✔️
  • ServerListPlus: ✔️ (Great plugin, would recommend it)
  • ViaVersion: ✔️
  • TitleManager: ❓ Doesn't log anything after starting the server
  • LuckPerms: (✔️) Uses colored output and bypasses logger entirely
  • GriefPrevention: ✔️
  • Multiverse-Core: (✔️) Uses Minecraft logger instead of plugin logger and adds plugin prefix manually. Prefix is shown after the : (like always without this PR), works correctly otherwise:
    [20:36:45 INFO] [Multiverse-Core]: Enabling Multiverse-Core v2.5-b719
    [20:36:45 INFO]: [Multiverse-Core] Version 2.5-b719 (API v20) Enabled
    
  • Citizens: (✔️) Like Multiverse-Core
  • VoxelSniper: ✔️
  • NoCheatPlus: (✔️) Like Multiverse-Core
  • PlaceholderAPI: ✔️
  • ASkyBlock: ✔️
  • Dynmap: ✔️
  • BuycraftX: ✔️

@stephan-gh
Copy link
Contributor Author

I disabled the logger prefix manually for com.sk89q.* and ru.tehkode.* so the messages logged by WorldEdit, WorldGuard and PermissionsEx now show up correctly in the log. Adding exclusions for specific plugins is annoying but since these are the only plugins with this problem (at least from the ones I've tested) it might be the best way to fix them.

The implementation in this PR works correctly, it's only plugins that bypass the plugin loggers which may cause issues.

@kashike
Copy link
Member

kashike commented Sep 22, 2017

Could you provide an updated log, please?

@stephan-gh
Copy link
Contributor Author

@kashike I could. Maybe I will... Actually, why not? https://gist.github.com/Minecrell/35f5dbb69eefaa3137daece6224588a4

@stephan-gh
Copy link
Contributor Author

@kashike Log with latest Paper build without my changes: https://gist.github.com/Minecrell/daa3e038f47751ed380fba661d3def73

@stephan-gh
Copy link
Contributor Author

Opened similar PR for Waterfall: PaperMC/Waterfall#176

@stephan-gh
Copy link
Contributor Author

Moved plugin prefix back to behind the : as discussed in #paper so it now looks pretty much like before: https://gist.github.com/Minecrell/97145ddccc0062eb7c2f84152caffb50

Log4j2 provides an optimized implementation of PrintStream that
redirects its output to a logger. Use it instead of a custom
implementation for minor performance improvements and some fixes.

With the old implementation, each call to System.print()
results in a separate line, even though it should not result in
a line break. Log4j's implementation handles it correctly.
SLF4J is a commonly used abstraction for various logging frameworks
such as java.util.logging (JUL) or Log4j. Currently, plugins are
required to do all their logging using the provided JUL logger.
This is annoying for plugins that target multiple platforms or when
using libraries that log messages using SLF4J.

Expose SLF4J as optional logging API for plugins, so they can use
it without having to shade it in the plugin and going through
several layers of logging abstraction.
Essentials uses a custom logger name ("Essentials") instead of the
plugin logger. Log messages are redirected to the plugin logger by
setting the parent of the "Essentials" logger to the plugin logger.

With our changes, the plugin logger is now also called "Essentials",
resulting in an infinite loop. Make sure plugins can't change the
parent of the plugin logger to avoid this.
Some plugins bypass the plugin logger and add the plugin prefix
manually to the log message. Since they use other logger names
(e.g. qualified class names) these would now also appear in the
log. Disable the logger prefix for these plugins so the messages
show up correctly.
@kashike
Copy link
Member

kashike commented Sep 23, 2017

pull the lever

@kashike
Copy link
Member

kashike commented Sep 23, 2017

Thanks, @Minecrell.

@kashike kashike merged commit e5e5063 into PaperMC:master Sep 23, 2017
@stephan-gh stephan-gh deleted the slf4j branch September 23, 2017 19:57
mikroskeem added a commit to OrionMinecraft/Helios that referenced this pull request Sep 23, 2017
SLF4J-over-Log4j2 is not needed anymore as of
PaperMC/Paper#890
@mibby
Copy link

mibby commented Sep 26, 2017

Noticed a plugin merging start-up logging verbose with world loading verbose on a single line whereas it didn't before.

2nd line in particular.

[04:07:22] [Server thread/INFO]: [LibsDisguises] Discovered nms version: v1_12_R1
[04:07:23] [Server thread/INFO]: [DabCore] Attempting to load default ItemUtils[DabCore] Loaded default, enjoy :)Preparing start region for level 3 (Seed: -1991970816273548549)[LibsDisguises] Loaded 0 custom disguises
[04:07:23] [Server thread/INFO]: [ConditionalCommands] Enabling ConditionalCommands v1.4

Other than that, just EssentialsX whining.

[04:07:29] [Server thread/WARN]: [Essentials] Ignoring attempt to change parent of plugin logger

@stephan-gh
Copy link
Contributor Author

stephan-gh commented Sep 26, 2017

@mibby That's because DabCore does wonderful logging using System.out.print instead of either using a logger(!!) or inserting a new line (e.g. using println). Spigot handles this incorrectly, and I fixed it in this PR. Not sure if we should revert it just to keep incorrect behavior and to encourage plugins to continue using System.out for logging.......

The Essentials warning is unrelated but can be fixed easily in Essentials. I will try to submit a PR to fix it when I have time, but until then it's just a warning, nothing breaks due to it.

Pr0methean pushed a commit to GlowstoneMC/Glowstone that referenced this pull request Jan 28, 2018
SLF4J logging for plugins is a feature introduced in
PaperMC/Paper#890. Plugins should be able to use SLF4J as
alternative logging API.

All that is necessary to make that work is a proper SLF4J adapter
implementation. However, right now Glowstone includes the SLF4J
implementation for Log4j 1.x, which isn't used in Glowstone at all.

Include the correct SLF4J implementation for the java.util.logging
API to make SLF4J logging work correctly.
aramperes pushed a commit to GlowstoneMC/Glowstone that referenced this pull request Feb 4, 2018
SLF4J logging for plugins is a feature introduced in
PaperMC/Paper#890. Plugins should be able to use SLF4J as
alternative logging API.

All that is necessary to make that work is a proper SLF4J adapter
implementation. However, right now Glowstone includes the SLF4J
implementation for Log4j 1.x, which isn't used in Glowstone at all.

Include the correct SLF4J implementation for the java.util.logging
API to make SLF4J logging work correctly.
Machine-Maker added a commit to Machine-Maker/Paper that referenced this pull request Jul 17, 2021
Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
5662c2b SPIGOT-6642: Throw better message if plugin.yml is empty
aa8e035 PaperMC#636: Add FurnaceStartSmeltEvent
52073b3 SPIGOT-6646: Issue with map palette ranges
5f772da PaperMC#640: Add new Causes for LightningStrikeEvent

CraftBukkit Changes:
4e18704 PaperMC#874: Add FurnaceStartSmeltEvent
ed2b91c SPIGOT-6649: Call BlockFadeEvent when Nylium fades to Netherrack
b7e3ce0 PaperMC#890: Include yaw in player's spawn location
aeb711d PaperMC#889: Fix CraftChest close() sound being replaced with open sound.
ca0fe5b SPIGOT-5561: Warning in logs when changing a Mob Spawner to Air on chunk load
2f038f2 PaperMC#886: Add new Causes for LightningStrikeEvent

Spigot Changes:
38e6c03d Rebuild patches
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: needs testing Issue needs verification.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants