reactor.core.publisher.Traces Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson-all Show documentation
Show all versions of redisson-all Show documentation
Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC
The newest version!
/*
* Copyright (c) 2018-2023 VMware Inc. or its affiliates, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.core.publisher;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Utilities around manipulating stack traces and displaying assembly traces.
*
* @author Simon Baslé
* @author Sergei Egorov
*/
final class Traces {
/**
* If set to true, the creation of FluxOnAssembly will capture the raw stacktrace
* instead of the sanitized version.
*/
static final boolean full = Boolean.parseBoolean(System.getProperty(
"reactor.trace.assembly.fullstacktrace",
"false"));
static final String CALL_SITE_GLUE = " ⇢ ";
/**
* Transform the current stack trace into a {@link String} representation,
* each element being prepended with a tabulation and appended with a
* newline.
*/
static final Supplier> callSiteSupplierFactory = new CallSiteSupplierFactory();
/**
* Return true for strings (usually from a stack trace element) that should be
* sanitized out by {@link Traces#callSiteSupplierFactory}.
*
* @param stackTraceRow the row to check
* @return true if it should be sanitized out, false if it should be kept
*/
static boolean shouldSanitize(String stackTraceRow) {
return stackTraceRow.startsWith("java.util.function")
|| stackTraceRow.startsWith("reactor.core.publisher.Mono.onAssembly")
|| stackTraceRow.equals("reactor.core.publisher.Mono.onAssembly")
|| stackTraceRow.equals("reactor.core.publisher.Flux.onAssembly")
|| stackTraceRow.equals("reactor.core.publisher.ParallelFlux.onAssembly")
|| stackTraceRow.startsWith("reactor.core.publisher.SignalLogger")
|| stackTraceRow.startsWith("reactor.core.publisher.FluxOnAssembly")
|| stackTraceRow.startsWith("reactor.core.publisher.MonoOnAssembly.")
|| stackTraceRow.startsWith("reactor.core.publisher.MonoCallableOnAssembly.")
|| stackTraceRow.startsWith("reactor.core.publisher.FluxCallableOnAssembly.")
|| stackTraceRow.startsWith("reactor.core.publisher.Hooks")
|| stackTraceRow.startsWith("sun.reflect")
|| stackTraceRow.startsWith("java.util.concurrent.ThreadPoolExecutor")
|| stackTraceRow.startsWith("java.lang.reflect");
}
/**
* Extract operator information out of an assembly stack trace in {@link String} form
* (see {@link Traces#callSiteSupplierFactory}).
*
* Most operators will result in a line of the form {@code "Flux.map ⇢ user.code.Class.method(Class.java:123)"},
* that is:
*
* - The top of the stack is inspected for Reactor API references, and the deepest
* one is kept, since multiple API references generally denote an alias operator.
* (eg. {@code "Flux.map"})
* - The next stacktrace element is considered user code and is appended to the
* result with a {@code ⇢} separator. (eg. {@code " ⇢ user.code.Class.method(Class.java:123)"})
* - If no user code is found in the sanitized stack, then the API reference is outputed in the later format only.
* - If the sanitized stack is empty, returns {@code "[no operator assembly information]"}
*
*
*
* @param source the sanitized assembly stacktrace in String format.
* @return a {@link String} representing operator and operator assembly site extracted
* from the assembly stack trace.
*/
static String extractOperatorAssemblyInformation(String source) {
String[] parts = extractOperatorAssemblyInformationParts(source);
switch (parts.length) {
case 0:
return "[no operator assembly information]";
default:
return String.join(CALL_SITE_GLUE, parts);
}
}
static boolean isUserCode(String line) {
return !line.startsWith("reactor.core.publisher") || line.contains("Test");
}
/**
* Extract operator information out of an assembly stack trace in {@link String} form
* (see {@link Traces#callSiteSupplierFactory}) which potentially
* has a header line that one can skip by setting {@code skipFirst} to {@code true}.
*
* Most operators will result in a line of the form {@code "Flux.map ⇢ user.code.Class.method(Class.java:123)"},
* that is:
*
* - The top of the stack is inspected for Reactor API references, and the deepest
* one is kept, since multiple API references generally denote an alias operator.
* (eg. {@code "Flux.map"})
* - The next stacktrace element is considered user code and is appended to the
* result with a {@code ⇢} separator. (eg. {@code " ⇢ user.code.Class.method(Class.java:123)"})
* - If no user code is found in the sanitized stack, then the API reference is outputed in the later format only.
* - If the sanitized stack is empty, returns {@code "[no operator assembly information]"}
*
*
*
* @param source the sanitized assembly stacktrace in String format.
* @return a {@link String} representing operator and operator assembly site extracted
* from the assembly stack trace.
*/
static String[] extractOperatorAssemblyInformationParts(String source) {
String[] uncleanTraces = source.split("\n");
final List traces = Stream.of(uncleanTraces)
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
if (traces.isEmpty()) {
return new String[0];
}
int i = 0;
while (i < traces.size() && !isUserCode(traces.get(i))) {
i++;
}
String apiLine;
String userCodeLine;
if (i == 0) {
//no line was a reactor API line
apiLine = "";
userCodeLine = traces.get(0);
}
else if (i == traces.size()) {
//we skipped ALL lines, meaning they're all reactor API lines. We'll fully display the last one
apiLine = "";
userCodeLine = traces.get(i-1).replaceFirst("reactor.core.publisher.", "");
}
else {
//currently on user code line, previous one is API
apiLine = traces.get(i - 1);
userCodeLine = traces.get(i);
}
//now we want something in the form "Flux.map ⇢ user.code.Class.method(Class.java:123)"
if (apiLine.isEmpty()) return new String[] { userCodeLine };
int linePartIndex = apiLine.indexOf('(');
if (linePartIndex > 0) {
apiLine = apiLine.substring(0, linePartIndex);
}
apiLine = apiLine.replaceFirst("reactor.core.publisher.", "");
return new String[] { apiLine, "at " + userCodeLine };
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy