Repository navigation
Rainbow Gum Support #4032
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
agentgt
wants to merge
11
commits into
jooby-project:main
Choose a base branch
from
agentgt:feature/rainbowgum-sol-high
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+881
−6
Open
Rainbow Gum Support #4032
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
b86091d
Add Rainbow Gum logging driven by the Jooby environment
agentgt 0ea4c5f
Keep temporary logger levels independent of root property aliases
agentgt 04d2406
Use released Rainbow Gum 0.16.1 for the Jooby integration
agentgt 8e3ed32
Mark only the queued bootstrap Rainbow Gum as Jooby's
agentgt b491322
Configure logging once and allow level changes only for muted server …
agentgt 440bc5c
Add review notes for the Rainbow Gum integration
agentgt 5fea00a
Note GraalVM native image results for the Rainbow Gum integration
agentgt 1aa5fac
Compare Rainbow Gum test output by lines for Windows
agentgt cd256c3
Update Rainbow Gum to 0.16.2
agentgt 02b1cbd
Use an invalid output in the Rainbow Gum queueing test for Windows
agentgt c557e0c
Note the public API the Rainbow Gum integration adds to LoggingService
agentgt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| == Rainbow Gum | ||
|
|
||
| https://jstach.io/rainbowgum/[Rainbow Gum] is an SLF4J implementation for Java 21 and newer. | ||
| The `jooby-rainbowgum` module configures it directly from Jooby's resolved Typesafe Config environment. | ||
|
|
||
| === Usage | ||
|
|
||
| Add the dependency: | ||
|
|
||
| [dependency, artifactId="jooby-rainbowgum"] | ||
| . | ||
|
|
||
| Use this module in place of `jooby-logback`, `jooby-log4j`, or another SLF4J provider. | ||
| No `install(...)` call or separate logging configuration file is needed. The module includes the | ||
| SLF4J provider, pattern encoder and file output support. | ||
|
|
||
| .application.conf | ||
| [source, hocon] | ||
| ---- | ||
| logging { | ||
| level { | ||
| root = info | ||
| "com.myapp" = debug | ||
| } | ||
| appenders = [console] | ||
| appender.console { | ||
| output.uri = stdout | ||
| encoder { | ||
| uri = pattern | ||
| pattern.pattern = "[%thread] %-5level %logger - %msg%n" | ||
| } | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| All Rainbow Gum properties are available, including appenders, encoders, routes and asynchronous | ||
| publishers. Native HOCON lists and maps are supported. Quote dotted logger names so they remain | ||
| one object member. Use `logging.level.root` for the root level when defining other logger levels | ||
| in the same object; HOCON cannot keep `logging.level` as both a scalar and an object. | ||
|
|
||
| Jooby resolves `application.conf`, environment-specific files such as `application.prod.conf`, | ||
| substitutions and system properties before passing the environment to Rainbow Gum. Programmatic | ||
| environments supplied through `setEnvironment(...)` and `setEnvironmentOptions(...)` also configure | ||
| logging. | ||
|
|
||
| Logging is global to the JVM and configured once, from the first environment Jooby loads, as with | ||
| Jooby's other logging implementations. Later environments, such as those of additional applications | ||
| started by `runApp` or another `setEnvironment(...)` call, do not change it. | ||
|
|
||
| === Early logging | ||
|
|
||
| A bootstrap Rainbow Gum queues messages emitted before Jooby loads its environment, including messages | ||
| from static loggers. When the environment is available, the module installs the configured Rainbow | ||
| Gum, replays queued messages through its routes, and moves loggers created during bootstrap to its | ||
| levels and routes. It then closes the bootstrap instance. An explicitly installed Rainbow Gum, or one supplied by the application's own | ||
| `RainbowGumProvider`, is left under the application's control. | ||
|
|
||
| Caller information and decorators are chosen when a logger is created. For loggers created during | ||
| bootstrap, configure these capabilities before startup (for example, | ||
| `-Dlogging.caller.logger.root=true`); the later environment configuration does not retrofit them. | ||
|
|
||
| === Configuration changes | ||
|
|
||
| Jooby core does not watch configuration files or publish configuration-change events. Jooby Run's | ||
| development watcher restarts the application but keeps project dependencies loaded, so logging keeps | ||
| the configuration from the first start. Restart Jooby Run to apply logging changes. | ||
|
|
||
| Loggers have a fixed level once created. To let existing loggers follow level changes, allow them | ||
| with Rainbow Gum's `logging.change.logger` property: | ||
|
|
||
| .application.conf | ||
| [source, hocon] | ||
| ---- | ||
| logging.change.logger = level | ||
| ---- | ||
|
|
||
| The application can then replace the configuration of its environment and reread logger levels | ||
| without reopening appenders: | ||
|
|
||
| [source, java] | ||
| ---- | ||
| getEnvironment().setConfig(updatedConfig); | ||
| RainbowGum.of().config().changePublisher().publish(); | ||
| ---- | ||
|
|
||
| === Server logger muting | ||
|
|
||
| Jooby turns off some server loggers while the server starts and stops. The module allows level | ||
| changes for those loggers only: `org.eclipse.jetty.server` for Jetty, and `io.undertow`, `org.xnio` | ||
| and `org.jboss.threads` for Undertow. `logging.change.mode = off` turns off all level changes, | ||
| including this muting. | ||
|
|
||
| See the https://jstach.io/rainbowgum/[Rainbow Gum guide] for the complete configuration reference. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| # Rainbow Gum integration review notes | ||
|
|
||
| Working notes for this branch. Remove before merging. | ||
|
|
||
| ## Public API added outside this module | ||
|
|
||
| Only `io.jooby.LoggingService` changes: | ||
|
|
||
| - `default void initialize(Environment environment)`: a hook for logging systems configured from the | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 ignore my previous, comment |
||
| Jooby environment. The default does nothing, so `jooby-logback` and `jooby-log4j` are unchanged. | ||
| - `static void configure(Environment environment)`: finds the installed `LoggingService` with the | ||
| environment's class loader and calls `initialize`. It is public only because interface static | ||
| methods cannot be package private; `Jooby` is its only caller. It overloads the existing | ||
| `configure(ClassLoader, List<String>)`. | ||
|
|
||
| `Jooby` keeps its signatures, but `setEnvironment`, `setEnvironmentOptions` and the lazy load in | ||
| `getEnvironment` now all go through `setEnvironment`, which calls `LoggingService.configure`. Every | ||
| environment Jooby loads or is given now does a `ServiceLoader` lookup. | ||
|
|
||
| The BOM and the parent pom gain `jooby-rainbowgum`, and the parent pom gains `rainbowgum.version`. | ||
|
|
||
| ## Changed in review | ||
|
|
||
| - **Bootstrap marker.** The marker that lets Jooby replace the bootstrap Rainbow Gum was added by a | ||
| `PropertiesProvider`, which runs for every service loaded `LogConfig`. A Rainbow Gum the | ||
| application built from such a config, or supplied by its own `RainbowGumProvider`, was closed and | ||
| replaced when the environment loaded. `RainbowGumBootstrap` now builds its own config with the | ||
| marker and has the lowest priority. | ||
| - **Configured once.** Only the queued bootstrap instance is replaced. Loggers with a fixed level | ||
| stay bound to the instance that created them, so rebuilding logging for a later environment left | ||
| them writing to a closed instance and their events were lost. Making every logger changeable hid | ||
| this. Later environments (`runApp` with several applications, another `setEnvironment(...)`, test | ||
| suites starting an application per class) now keep the first configuration, as Logback and Log4j | ||
| do. | ||
| - **Level changes only where muting needs them.** Defaults allow level changes for the loggers Jetty | ||
| and Undertow mute (`org.eclipse.jetty.server`, `io.undertow`, `org.xnio`, `org.jboss.threads`) | ||
| instead of every logger, so other loggers keep the fixed level fast path. Rereading levels with | ||
| `changePublisher().publish()` is opt in through `logging.change.logger`. | ||
|
|
||
| ## Open on this branch | ||
|
|
||
| - **Unbounded queue.** If no Jooby environment is ever loaded (unit tests that never read the | ||
| config, command line tools), INFO and above queue in memory for the life of the JVM and are never | ||
| written. Only ERROR reaches stderr. Probably needs a Rainbow Gum side fix: a bounded queue or a | ||
| flush at shutdown. | ||
| - **Muting with an application Rainbow Gum.** Muting levels are only wired into the module's own | ||
| config, so `logOff` does nothing when the application installed its own Rainbow Gum. | ||
| - **Failed logging setup.** `Jooby.setEnvironment` stores the environment before configuring | ||
| logging. If the logging config is invalid the call throws, but `getEnvironment()` then returns that | ||
| environment and logging stays queued. | ||
| - **Jooby Run.** Dependencies stay loaded across restarts in the default mode, so logging keeps the | ||
| first start's configuration (documented). The configured instance also keeps the first | ||
| `Environment`, and with it the first project class loader, reachable. With | ||
| `useSingleClassLoader` everything reloads, so each restart configures a new instance, but nothing | ||
| closes the previous class loader's instance or removes its shutdown hook. This is from reading the | ||
| code, not tested. | ||
|
|
||
| ## GraalVM native image | ||
|
|
||
| Checked with GraalVM CE 25.0.4 `native-image` on a small Jetty application with a static logger | ||
| and a `logging` block in `application.conf`. This module needs no native image metadata: its | ||
| service providers are registered from `META-INF/services`, and the native executable printed the | ||
| same output as the JVM run (early message replayed through the configured pattern, debug levels | ||
| from the config, Jetty startup muted). | ||
|
|
||
| Jooby itself ships no native image metadata. Without it, a classpath `application.conf` is not in | ||
| the image, and Jooby silently falls back to defaults (port 8080, default logging). The application | ||
| had to add `META-INF/native-image/<group>/<artifact>/reachability-metadata.json` with | ||
| `{"resources": [{"glob": "application*.conf"}]}`. Jooby could ship that resource entry in its own | ||
| jar. | ||
|
|
||
| ## Jooby bugs outside this module | ||
|
|
||
| - **Log4j `logOff` does not restore exactly configured loggers.** `Log4jService.setLog4jLevel` reads | ||
| `logger.getLevel()` after `Configurator.setLevel`, which sets an existing `LoggerConfig` with that | ||
| exact name in place (checked against log4j-core 2.26.1). The saved level is OFF, so a muted logger | ||
| configured by name in `log4j2.xml` stays off. The first mute also adds a `LoggerConfig` with that | ||
| name, so after one start and stop every muted logger is affected on the next start in the same JVM. | ||
| Fix: read the level before setting it. | ||
| - **Jetty mutes `org.eclipse.jetty.server.Server` twice.** `JettyServer.getLoggerOff()` lists it | ||
| twice. Logback and Log4j `logOff` record the previous level per name, so the second entry records | ||
| OFF over the original and the logger stays off after startup. Fix: drop the duplicate, or apply | ||
| `distinct()` in `MutedServer`. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>io.jooby</groupId> | ||
| <artifactId>modules</artifactId> | ||
| <version>4.5.5-SNAPSHOT</version> | ||
| </parent> | ||
| <artifactId>jooby-rainbowgum</artifactId> | ||
| <name>jooby-rainbowgum</name> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>io.jooby</groupId> | ||
| <artifactId>jooby</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.jstach.rainbowgum</groupId> | ||
| <artifactId>rainbowgum-slf4j</artifactId> | ||
| <version>${rainbowgum.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.jstach.rainbowgum</groupId> | ||
| <artifactId>rainbowgum-pattern</artifactId> | ||
| <version>${rainbowgum.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.jstach.rainbowgum</groupId> | ||
| <artifactId>rainbowgum-file</artifactId> | ||
| <version>${rainbowgum.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-engine</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jacoco</groupId> | ||
| <artifactId>org.jacoco.agent</artifactId> | ||
| <classifier>runtime</classifier> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't do this for logback or log4j.