PaperMC / PaperMC/Paper

Proper logging for plugin developers

Open
#12,408 15 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: input wanted
Dominant language
Java
Stars
12.7k
Forks
3.5k
Avg merge
3d 13h
Merged PRs (30d)
11

Description

Is your feature request related to a problem?

This issue contains both:

  • a bug report (and a request for completing documentation about how to set the log level for plugin developers and server administrators)
  • a feature request.

Feature request

Java.util.Logger logger = myPluginInstance.getLogger(); is a pain for plugin developers (or is it just me).

It does not support the basic logging features we find in every modern logging framework:

  • simple parametrization using {} instead of %s
  • setting log level statically from env/startup parameters/config files/... .
  • setting log level dynamically from code (during execution): plugin.getLogger().setLevel() does not work (at least in 1.20.4 latest build). See bug report bellow.
  • setting log level per plugin
  • setting log level per package/class
  • completing prefix with the class that produced a given log
  • Mapped diagnostic context
  • ... (see SLF4J/Logback/log4j2 documentation for a more complete feature list)

Bug report

plugin.getLogger().setLevel() method does not work

Reproduce

pom.xml (with or without provided scope has no impact)

        <!-- SLF4J binder to forward logs to Log4j -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j18-impl</artifactId>
            <version>2.17.2</version>
            <scope>provided</scope>
        </dependency>
        <!-- Log4j API -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.19.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- Log4j Core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.19.0</version>
            <scope>provided</scope>
        </dependency>

LogExample.java

public class LogExample {
    private final java.util.logging.Logger logger = plugin.getLogger();
    private final Logger slf4jLogger = LoggerFactory.getLogger("");
    
   public LogExample(JavaPlugin plugin) {
        //log with slf4j
        slf4jLogger.trace("test trace");
        slf4jLogger.debug("test debug");
        slf4jLogger.info("test info");
        slf4jLogger.warn("test warn");
        slf4jLogger.error("test error");
        //log with plugin logger
        logger.log(java.util.logging.Level.FINEST, "test trace");
        logger.log(java.util.logging.Level.FINE, "test debug");
        logger.log(java.util.logging.Level.INFO, "test info");
        logger.log(java.util.logging.Level.WARNING, "test warn");
        logger.log(java.util.logging.Level.SEVERE, "test error");

        // Set slf4j log level
        // This is only one of the 2 ways decribed in https://stackoverflow.com/questions/23434252/programmatically-change-log-level-in-log4j2 post
        org.apache.logging.log4j.core.LoggerContext ctx = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(true);
        Configuration config = ctx.getConfiguration();
        LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
        loggerConfig.setLevel(Level.ALL);
        loggerConfig = config.getLoggerConfig("eu.lasersenigma.noneuclideanspacebuilder");
        loggerConfig.setLevel(Level.ALL);
        ctx.updateLoggers();

        //set plugin logger log level
        logger.setLevel(java.util.logging.Level.FINEST);

        //log with slf4j
        slf4jLogger.trace("test trace (post setLevel ALL)");
        slf4jLogger.debug("test debug (post setLevel ALL)");
        slf4jLogger.info("test info (post setLevel ALL)");
        slf4jLogger.warn("test warn (post setLevel ALL)");
        slf4jLogger.error("test error (post setLevel ALL)");

        //log with plugin logger
        logger.log(java.util.logging.Level.FINEST, "test trace (post setLevel ALL)");
        logger.log(java.util.logging.Level.FINE, "test debug (post setLevel ALL)");
        logger.log(java.util.logging.Level.INFO, "test info (post setLevel ALL)");
        logger.log(java.util.logging.Level.WARNING, "test warn (post setLevel ALL)");
        logger.log(java.util.logging.Level.SEVERE, "test error (post setLevel ALL)");
   }
}

Result:

[13:18:29 INFO]: test info
[13:18:29 WARN]: test warn
[13:18:29 ERROR]: test error
[13:18:29 INFO]: [NonEuclideanSpaceBuilder] test info
[13:18:29 WARN]: [NonEuclideanSpaceBuilder] test warn
[13:18:29 ERROR]: [NonEuclideanSpaceBuilder] test error
[13:18:29 INFO]: test info (post setLevel ALL)
[13:18:29 WARN]: test warn (post setLevel ALL)
[13:18:29 ERROR]: test error (post setLevel ALL)
[13:18:29 INFO]: [NonEuclideanSpaceBuilder] test info (post setLevel ALL)
[13:18:29 WARN]: [NonEuclideanSpaceBuilder] test warn (post setLevel ALL)
[13:18:29 ERROR]: [NonEuclideanSpaceBuilder] test error (post setLevel ALL)

Summary

As you can see above, I tried to dynamically change the log level of both a SLF4J logger or the default plugin logger. None worked.

I also tried to change log level statically using other ways:

  • A custom log4j config in paper run command -Dlog4j.configurationFile=log4j2.xml (overriding paper log4j2 default config)
  • A custom log4j config in my resources
  • A paper related parameter: "-Dpaper.log-level=FINE". By the way this paper.log-level is badly documented since it's in the header of this documentation section but not in the following list of system properties.

And whatever I try, I can not change the log level.

I only found unclear/outdated posts online and nothing clear in paper documentation (except the paper.log-level system property that do not work as exaplained above).

Describe the solution you'd like.

Feature request:

As a plugin developer :

  • I want to be able to set the log level for my plugin
  • I want to be able to set the log level for a given package/class
  • I want clear levels like SLF4J (instead of the outdated and strange FINE / FINEST / CONFIG java.util levels)
  • I want easy log parametrization like what we can find in SLF4J (Example: log.error("Player {} not found",player.getName())) instead of using '%s' java formaters syntax.
  • I want to be able to change the log level for a given package dynamically.
  • I want to be able to use both:
    • A simple logger that will just add the "[PluginName] " prefix.
    • A class related logger that will add the "[PluginName][package.Class] " prefix.
  • [Optional] I want to retrieve a logger from anywhere using a static method like in SLF4J instead of having to use the plugin instance getLogger() method

As a server administrator :

  • I want to be able to override the log level, for a given plugin (or package).
    Use case: A server owner find a bug. A plugin developer request to set the log level. The server owner set the log level, reproduce the bug and then send the logs.
  • [Optional] I want to be able to override the log level without having to restart my server (see componentScan feature in log4j2)
Describe alternatives you've considered.

See [Optional] points in the above section

Other

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Begin with the supplied LogExample reproduction and compare the java.util.logging, SLF4J, and Log4j behavior it demonstrates. Read the linked Paper system-properties documentation, especially the paper.log-level entry, and establish the intended scope for plugin and server log-level control. Done should include a decided solution or clarified scope for the requested logging features and the reported setLevel behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend, observability-sre
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.