Proper logging for plugin developers
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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