-
-
Notifications
You must be signed in to change notification settings - Fork 999
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
implement putAll and putAllCloseable #274
base: master
Are you sure you want to change the base?
Conversation
35adca4
to
05fba96
Compare
relates to https://jira.qos.ch/browse/SLF4J-387 |
@ceki If there's anything from me you need to get this merged please let me know and I'll have a look. |
It's in the pipeline. |
05fba96
to
3fd7bc1
Compare
Implements putAll and putAllCloseable described in SLF4J-387 Signed-off-by: Nikolas Grottendieck <[email protected]>
3fd7bc1
to
999001e
Compare
Updated the pull request to resolve merge conflicts with master and removed an unused import I accidentally introduced. If there's anything else you need to continue please let me know and I'll have a look. |
@ceki is there a chance you or someone else from SLF4J can take a look to get this merged into a 2.x or 2.0.x release? |
@Okeanos Would you be satisfied with |
I am not sure I follow – do you have a minimal example for:
Additionally, what would we use as keys then? The idea/intent is to put known key-value-pairs into the MDC for operations that should have no long-term effect on the thread local MDC but we in the end we can still filter log lines by MDC keys consistently. |
@Okeanos Here is what I had in mind:
|
I think I understand what you intend, thanks for taking the time! I'll get back to you next week, when I have a little more time to illustrate my own intentions with proposing |
Okay, I had some time to write a real-world inspired example of why I think public String someFunction(final UUID id) {
ResponseEntity<String> response = webClient
.get()
.uri(uriBuilder -> uriBuilder.pathSegment("entry", "{id}").build(id))
// ... handle request
.doOnEach(signal -> {
if (signal.isOnError() ||signal.isOnNext() ) {
// magic MDCCloseable
try (var mdc = MDCCloseable.putAllCloseable(signal.getContextView().getOrDefault("MDC", new HashMap<>()))) {
var response = signal.get();
if (signal.isOnError()) {
log.warn(
"get pipeline failed {}", "value",
kv("response_status", response.getStatusCode()),
kv("response_body", response.getBody())
);
} else {
log.debug(
"get pipeline did stuff",
kv("response_status", response.getStatusCode()),
kv("response_body", response.getBody())
);
}
}
} else {
return;
}
})
//.. do more stuff
// adds MDC to actual async call stack as context variable
.contextWrite(Context.of("MDC", Optional.ofNullable(MDC.getCopyOfContextMap()).orElseGet(HashMap::new)))
.block();
// do stuff with the actual response and inspect it
return response.getBody();
} If I understood your example correctly, transferring it to my example would look similar to this: public String someFunction(final UUID id) {
ResponseEntity<String> response = webClient
.get()
.uri(uriBuilder -> uriBuilder.pathSegment("entry", "{id}").build(id))
// ... handle request
.doOnEach(signal -> {
if (signal.isOnError() ||signal.isOnNext() ) {
var localMdc = signal.getContextView().getOrDefault("MDC", new HashMap<>())
try (var closeable = MDCCloseable.putAllKeys(localMdc.keySet())) { // insert keys to be removed
localMdc.forEach(MDC::put);
var response = signal.get();
if (signal.isOnError()) {
log.warn(
"something failed {}", "value",
kv("response_status", response.getStatusCode()))
);
} else {
log.debug(
"some debug",
kv("response_status", response.getStatusCode())
);
}
}
} else {
return;
}
})
//.. do more stuff
// adds MDC to actual async call stack as context variable
.contextWrite(Context.of("MDC", Optional.ofNullable(MDC.getCopyOfContextMap()).orElseGet(HashMap::new)))
.block();
// do stuff with the actual response and inspect it
return response.getBody();
} It's not a huge difference but there's certainly a couple of "gotchas"/caveats here adding noise to the overall implementation. I already pointed out that |
* in case the "entries" parameter is null | ||
* @since 2.0.0 | ||
*/ | ||
public static MDCCloseable putAllCloseable(Map<String, String> entries) throws IllegalArgumentException { |
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.
Would an override like java.util.Map.of(String, String, ...)
be useful here?
In many of the places in applications where I have wanted to use this sort of functionality, I have usually wanted to add several entries at once. In this case it would read something like
try (MDC.putAllCloseable(
"X-Request-ID", request.getHeader("X-Request-ID"),
"User-Agent", request.getHeader("User-Agent"),
"From", request.getHeader("From")
)) {
processRequest();
}
versus having to declare this separately.
If not, I wonder if having the ability to chain additional put
operations onto the closeable may be useful, along with a functional method to run the closeable in a closure directly.
MDC.putCloseable("foo", "bar")
.put("baz", "bork")
.put("qux", "quxx")
.run(() -> {
processRequest();
});
where run
has a signature of something like
@FunctionalInterface
public interface ThrowingRunnable<T extends Throwable> {
void run() throws T;
}
@FunctionalInterface
public interface ThrowingSupplier<R, T extends Throwable> {
R get() throws T;
}
public void <T extends Throwable> run(ThrowingRunnable<T> runnable) throws T {
try (this) {
runnable.run();
}
}
public void <R, T extends Throwable> run(ThrowingSupplier<R, T> supplier) throws T {
try (this) {
return supplier.get();
}
}
Probably out of scope for this PR, but wondering if it may compliment this functionality in the future perhaps?
SLF4J-557 makes a very valid point. |
That is indeed a fair point. I am now wondering how to work around that because the auto-closeable neatness I tried showing in my examples above would be sorely missed :/. A clean, consistent, and predictable API does strike me as more important though. |
Implements putAll and putAllCloseable described in SLF4J-387 – I ran into the same problem a short while ago that I needed a closeable MDC that discards multiple entries after the try-with resource is closed.
If more tests or other changes are required I'll happily take another look at this.