This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch docs
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/docs by this push:
new f2eba783fb REST debug 329 follow-ups: quick wins + revised specs
f2eba783fb is described below
commit f2eba783fb4d46911bb78194e6fbb41b848da9a4
Author: James Bognar <[email protected]>
AuthorDate: Sat Aug 15 18:49:04 2026 -0400
REST debug 329 follow-ups: quick wins + revised specs
- Implement READY-369 (capture caps + complete caching wrappers), READY-371
(mixin op-logger naming/contract + tests), READY-373 (blast-radius
migration/javadoc cleanup); archive to finished/.
- Revise TODO-368 (JUL/Spring control plane), TODO-370 (secret-surface
hardening; 2 review rounds), and split/revise TODO-372 into 372a/372b per
adversarial cross-model review.
---
.../topics/10.32.RestServerLoggingAndDebugging.md | 54 ++++++++++++++++++++--
pages/topics/27.V10MigrationGuide.md | 34 +++++++++++++-
2 files changed, 84 insertions(+), 4 deletions(-)
diff --git a/pages/topics/10.32.RestServerLoggingAndDebugging.md
b/pages/topics/10.32.RestServerLoggingAndDebugging.md
index e0a4477fb5..a57f014ec2 100644
--- a/pages/topics/10.32.RestServerLoggingAndDebugging.md
+++ b/pages/topics/10.32.RestServerLoggingAndDebugging.md
@@ -22,9 +22,12 @@ that class's endpoints, at three cumulative tiers.
Two JUL loggers are in play for any given request:
-- **Resource logger** — `Logger.getLogger(resourceClass.getName())`.
-- **Operation logger** — `Logger.getLogger(resourceClass.getName() + "." +
methodName)`, once the request has been
- routed to a specific `@RestOp` method.
+- **Resource logger** — the resource's *resolved* logger:
`Logger.getLogger(resourceClass.getName())` by default,
+ or whatever a `@Bean`-annotated `RichLogger` factory method overrides it to
(see
+ [Bean-overridden resource loggers](#bean-overridden-resource-loggers) below).
+- **Operation logger** — `Logger.getLogger(resourceLoggerName + "." +
methodName)`, once the request has been
+ routed to a specific `@RestOp` method, where `resourceLoggerName` is the
resolved resource logger's name from
+ above (i.e. the operation logger is always a JUL child of the *resolved*
resource logger, override or not).
Juneau resolves the *operation* logger first, falling back to the *resource*
logger on unrouted/404 paths. Because
this is a plain <a
href="https://docs.oracle.com/en/java/javase/17/docs/api/java.logging/java/util/logging/Logger.html"
target="_blank">java.util.logging.Logger</a>
@@ -45,6 +48,51 @@ With the configuration above:
This is exactly the "set the resource logger to `FINE`, elevate one method to
`FINEST`" pattern — per-operation
control falls out of ordinary JUL inheritance, no Juneau-specific per-method
attribute required.
+### Mixin operations resolve against the host
+
+An operation contributed by a composed
[`@Rest(mixins=...)`](/docs/topics/RestServerMixinSubContexts) mixin uses
+the **host / top-level resource that composed the mixin** for its
resource-logger portion — not the mixin class
+itself:
+
+```java
+public class EchoMixin { @RestGet(path="/echo/*") public ... echo(...) {...} }
+
+@Rest(mixins=EchoMixin.class)
+public class MyResource extends RestServlet {...}
+```
+
+Here the mixin-served `echo` operation's logger is named
`com.example.MyResource.echo`, **not**
+`com.example.EchoMixin.echo`. Consequences:
+
+- Raising `com.example.MyResource`'s own JUL level cascades to `echo` (and
every other mixin-served operation) —
+ ordinary JUL parent inheritance, same as any other operation on the host.
+- The same mixin class composed into two different host resources resolves to
two independent, host-isolated
+ operation loggers (`HostA.echo` and `HostB.echo`) — raising one host's level
never leaks into the other.
+
+### Bean-overridden resource loggers
+
+If a resource replaces its resolved logger via a `@Bean`-annotated
`RichLogger` factory method:
+
+```java
+public class MyResource extends RestServlet {
+ @Bean
+ public RichLogger logger() {
+ return RichLogger.getLogger("my.app.custom.logger.name");
+ }
+}
+```
+
+every operation logger on that resource is named as a child of the
*overridden* name
+(`my.app.custom.logger.name.<methodName>`), not the raw resource class name —
so raising the overridden logger's
+level still cascades to its operations exactly as it would for the default,
class-named logger.
+
+### 404 / unrouted requests
+
+When no operation is resolved (the request 404s, or otherwise never reaches a
matched `@RestOp` method), Juneau
+falls back to the resource logger, but only ever emits the basic status-line
summary — headers and bodies are
+never rendered on this path, no matter how high the resolved logger's level is
set. Raising a resource to `FINEST`
+makes its *operations* log full detail; it does not retroactively add
header/body detail to 404s.
+
## Tier table
| Level | Content |
diff --git a/pages/topics/27.V10MigrationGuide.md
b/pages/topics/27.V10MigrationGuide.md
index 995c2b7c76..6511824f63 100644
--- a/pages/topics/27.V10MigrationGuide.md
+++ b/pages/topics/27.V10MigrationGuide.md
@@ -116,7 +116,7 @@ Single-type imports follow the same rename, e.g.:
| Class-level `@RestInit public void init(RestContext.Builder b) { ... }` hook
— the framework added the in-flight `RestContext.Builder` to the resource's
bean store so any `@RestInit` method that declared a `RestContext.Builder`
parameter received it and could imperatively configure the resource-level
context (`builder.path(...)`, `builder.children(...)`, `builder.encoders(...)`,
etc.). | **Removed.** The class-level Builder-injection protocol is gone —
`RestContext.Builder` is no longe [...]
| Custom annotation appliers — user code that subclassed the internal
`AnnotationApplier<Rest, RestContext.Builder>` (or `AnnotationApplier<RestOp,
RestOpContext.Builder>`) to extend the annotation-processing pass (the
`apply(AnnotationInfo<A>, B builder)` hook invoked once per annotation during
context construction). | **Removed.** The builder-based apply-pass is gone;
`RestAnnotation.Apply` (`RestContextApply`) is now a package-private nested
class inside `RestContext` and is not exten [...]
| Custom `RestAnnotation.create(...)` / `RestOpAnnotation.create(...)`
builder-of-builders patterns — programmatic construction of `@Rest` / `@RestOp`
annotation proxies used to feed synthetic annotations into the builder
apply-pass (common in test fixtures and extension libraries). | The annotation
proxy builders still exist for test use (`RestAnnotation.create()` /
`RestOpAnnotation.create()` are still available via annotation-test helpers),
but they no longer feed into a builder apply [...]
-| All prior REST debug/logging mechanisms, in order of vintage: `@Rest(debug)`
/ `@RestOp(debug)` plain-string attributes; the nested `@Debug` annotation
(`value()`/`on()`/`format()`/`level()`/`config()`) and its
`debugDefault`/`debugEnablement`/`debugOn` siblings;
`DebugEnablement`/`BasicDebugEnablement`/`DebugConfig`/`DebugRule`; and the
`CallLogger`/`CallLoggerRule`/`BasicCallLogger` logging SPI (including the
`juneau.restLogger.*` config keys). | **Removed — replaced by a single JUL
[...]
+| All prior REST debug/logging mechanisms, in order of vintage: `@Rest(debug)`
/ `@RestOp(debug)` plain-string attributes; the nested `@Debug` annotation
(`value()`/`on()`/`format()`/`level()`/`config()`) and its
`debugDefault`/`debugEnablement`/`debugOn` siblings;
`DebugEnablement`/`BasicDebugEnablement`/`DebugConfig`/`DebugRule`; and the
`CallLogger`/`CallLoggerRule`/`BasicCallLogger` logging SPI (including the
`juneau.restLogger.*` config keys). | **Removed — replaced by a single JUL
[...]
## REST Debug/Logging Redesign (JUL-Level-Driven `RestDebugFormatter`)
@@ -125,6 +125,21 @@ Juneau 10.0 replaces every prior REST debug/logging
mechanism with a single sign
[Logging / Debugging](/docs/topics/RestServerLoggingAndDebugging) for the full
new model (level resolution, tier
table, the `RestDebugFormatter` SPI, and secure-by-default redaction + body
cap).
+:::danger `Enablement` was public 9.x API in the `juneau-marshall` core
artifact — this is not just a rest-server-internal removal
+`org.apache.juneau.Enablement` (in 9.x; it would have been
`org.apache.juneau.marshall.Enablement` under the 10.0
+"Marshall Package Rename" described earlier in this guide, had it survived)
was a **released public enum** —
+`ALWAYS` / `CONDITIONAL` / `NEVER`, with `fromString(String)`,
+`isEnabled(boolean)`, and `isOneOf(Enablement...)` — shipped in the
`juneau-marshall` core artifact, not a
+rest-server-only type. It has been **deleted with no replacement in 10.0**.
+
+- If you used `Enablement` **only** for `@Rest(debug=...)` /
`@RestOp(debug=...)` (its sole in-framework consumer),
+ no action is needed beyond the debug/logging migration below — the JUL
logger level replaces it entirely, and
+ there is no `CONDITIONAL`-equivalent (capture is derived from the logger's
level, not evaluated per-request).
+- If your **own code** imported `Enablement` as a general-purpose tri-state
enum unrelated to REST debug, define
+ your own replacement enum — Juneau 10.0 does not ship one. This is easy to
miss because the class lived in a
+ core marshalling artifact most projects already depend on, not an opt-in
rest-server module.
+:::
+
| Removed | Replacement |
|---|---|
| `@Rest(debug=...)` / `@RestOp(debug=...)` and the nested `@Debug` annotation
(`value()`, `on()`, `format()`, `level()`, `config()`) | No annotation
replacement. Set your resource's JUL logger level (`INFO`/`FINE`/`FINEST`)
instead. |
@@ -133,6 +148,23 @@ table, the `RestDebugFormatter` SPI, and secure-by-default
redaction + body cap)
| Config keys `juneau.restLogger.level` / `.enabled` / `.requestDetail` /
`.responseDetail` / `.logger`, and `RestContext.debugDefault` | **Removed, no
replacement.** The resource's own logger level is the only configuration
surface. |
| `RestContextArgs`-injectable `DebugEnablement` parameter type on `@RestOp`
methods | **Removed, not replaced.** `DebugEnablement` is gone; there is no
injectable equivalent. |
+### Per-operation logger naming (mixins and bean-overridden loggers)
+
+The per-operation logger name is `<hostLoggerName>.<methodName>`, where
`hostLoggerName` is the **host** resource's
+*resolved* logger name (see [Logging /
Debugging](/docs/topics/RestServerLoggingAndDebugging)) — not necessarily
+the raw resource class name:
+
+- **Mixins.** An operation contributed by a composed
[`@Rest(mixins=...)`](/docs/topics/RestServerMixinSubContexts)
+ mixin resolves its logger name against the **host / top-level resource that
composed the mixin**, not the mixin
+ class itself. Raising the host resource's own JUL level cascades to every
mixin-served operation as a result, and
+ the same mixin class composed into two different hosts resolves to two
independent, host-isolated operation
+ loggers — one per host.
+- **Bean-overridden resource loggers.** If a resource replaces its resolved
logger via a `@Bean`-annotated
+ `RichLogger` factory method, the per-operation logger is a JUL child of that
*overridden* name, not the raw
+ resource class name — so raising the overridden logger's level still
cascades to its operations.
+- **404 / unrouted requests.** When no operation is resolved, only the basic
status-line summary is ever logged;
+ headers and bodies are never rendered on this path, regardless of how high
the resolved logger's level is set.
+
:::warning Footgun: raising a logger's level for ordinary app logging also
turns on REST capture
Because the signal is the resource's **own** JUL logger, raising it to
`FINE`/`FINEST` for reasons that have
nothing to do with REST debugging — e.g. turning up verbose application
logging on the resource class — **also**