Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ export default defineConfig({
items: [
"paper/dev/using-databases",
"paper/dev/debugging",
"paper/dev/logging",
"paper/dev/internals",
"paper/dev/reading-stacktraces",
],
Expand Down Expand Up @@ -572,6 +573,7 @@ export default defineConfig({
velocity: "https://jd.papermc.io/velocity",
adventure: { url: "https://jd.papermc.io/adventure", module: "net.kyori.adventure.api" },
java: { url: "https://docs.oracle.com/en/java/javase/25/docs/api", module: "java.base" },
slf4j: "https://www.slf4j.org/apidocs",
},
},
],
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
124 changes: 124 additions & 0 deletions src/content/docs/paper/dev/misc/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
title: Plugin Logging
description: A guide to to everything logging related for plugin dev.
slug: paper/dev/logging
---

Adding proper logging to your plugin can help both yourself and users of your plugin to find bugs and issues quickly.
This page serves as a general overview on the different types of loggers you can use in your plugin, when to use
each type, and other tips and tricks!

## Different types of loggers

Paper plugins (through their main class, the class extending [`JavaPlugin`](jd:paper:org.bukkit.plugin.java.JavaPlugin))
have direct access to three different types of logger classes.

### The java.util logger

The [`java.util.logging.Logger`](jd:java:java.util.Logger) (accessible via
[`JavaPlugin#getLogger()`](jd:paper:org.bukkit.plugin.java.JavaPlugin#getLogger())),
commonly referred to as the **java.util logger**, is a simple Java built-in logger class. It allows for standard
level-based logging, as well as a way to log exception stacktraces directly.

Example usage:
```java
@Override
public void onEnable() {
int id = loadDatabaseId();
try {
getLogger().info("Starting database load for id = %d".formatted(id));
initDatabase(id);
} catch (SQLException e) {
getLogger().log(Level.SEVERE, "Failed to init database", e);
}
}
```

### The SLF4J logger

[SLF4J](https://slf4j.org/) is a "Simple Logging Facade for Java" that serves as an abstraction for multiple logging
frameworks to implement. You can obtain a SLF4J [`Logger`](jd:slf4j:org.slf4j.Logger) instance via the
[`JavaPlugin#getSLF4JLogger()`](jd:paper:org.bukkit.plugin.java.JavaPlugin#getSLF4JLogger()) method.

SLF4J also allows for level-based logging, however it provides a nicer interface for parameterized logging
(log messages with parameters) and better usability when printing exception stacktraces. It additionally supports
markers, which can be very useful for advanced logging setups.

Example usage:
```java
@Override
public void onEnable() {
int id = loadDatabaseId();
try {
getSLF4JLogger().info("Starting database load for id = {}", id);
initDatabase(id);
} catch (SQLException e) {
getSLF4JLogger().error("Failed to init database", e);
}
}
```

The SLF4J logger is generally shorter and easier to use than the java.util logger.

### The component logger

Adventure's [`ComponentLogger`](jd:adventure:net.kyori.adventure.text.logger.slf4j:net.kyori.adventure.text.logger.slf4j.ComponentLogger)
is an extension of the SLF4J logger (meaning it inherits all of its features) which additionally provides support
for printing [`Component`](jd:adventure:net.kyori.adventure.text.Component)s directly. This includes support for colored
log output in supported environments. An instance of it can be retrieved via the
[`JavaPlugin#getComponentLogger()`](jd:paper:org.bukkit.plugin.java.JavaPlugin#getComponentLogger())
method.

Example usage:
```java
@Override
public void onEnable() {
int id = loadDatabaseId();
try {
getComponentLogger().info(
MiniMessage.miniMessage().deserialize("Starting database load for <red>id<gray> = <aqua>{}</red>"),
id
);
initDatabase(id);
} catch (SQLException e) {
getComponentLogger().error("Failed to init database", e);
}
}
```

The component logger is the only way you can properly out components to console. The following code fragment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

out

would yield the below console output:

```java
Component output = constructComponent();
getComponentLogger().warn("Component: {}", output);
```

![](./assets/logging-component.png)

Whereas the java.util or regular SLF4J logger would simply call `#toString()` on the component,
resulting in basically unusable output:

![](./assets/logging-component-standard.png)

### Other

:::danger[System.out.println()]

Paper **strongly** advises against using `System.out.println()` calls in your code. This includes calls such as
`Exception#printStackTrace()`. You should always prefer to use a proper logger for more fine-grained control over
your logging level and output.

:::

:::caution[JavaPlugin#getLog4JLogger()]

This method has been deprecated. You should prefer to use the [SLF4J logger](#the-slf4j-logger) instead.

:::

## Which logger should you use?

Nowadays, the overall recommendation is to use the [SLF4J logger](#the-slf4j-logger), as it is the simplest, most
feature-complete logger. The [component logger](#the-component-logger) should be used if you frequently interact
with and may need to log Adventure components.
Loading