All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactor.core.publisher.Traces Maven / Gradle / Ivy

Go to download

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

There is a newer version: 3.40.2
Show 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: *

    *
  1. 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"})
  2. *
  3. 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)"})
  4. *
  5. If no user code is found in the sanitized stack, then the API reference is outputed in the later format only.
  6. *
  7. If the sanitized stack is empty, returns {@code "[no operator assembly information]"}
  8. *
* * * @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: *

    *
  1. 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"})
  2. *
  3. 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)"})
  4. *
  5. If no user code is found in the sanitized stack, then the API reference is outputed in the later format only.
  6. *
  7. If the sanitized stack is empty, returns {@code "[no operator assembly information]"}
  8. *
* * * @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