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

com.diffplug.common.debug.StackDumper Maven / Gradle / Ivy

/*
 * Copyright 2016 DiffPlug
 *
 * 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
 *
 *     http://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 com.diffplug.common.debug;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.diffplug.common.base.StringPrinter;

/**
 * Utility methods for dumping the stack - arbitrarily or at specific trigger points (such as when a certain string prints to console).
 * 

* If someone is printing "junk" to the console and you can't figure out why, try {@code StackDumper.dumpWhenSysOutContains("junk")}. */ public class StackDumper { static StringPrinter pristineSysErr = new StringPrinter(System.err::print); /** Dumps the given message and stack to the system error console. */ public static void dump(String message, List stack) { Objects.requireNonNull(message); printEmphasized(message + "\n" + stackTraceToString(stack)); } /** Dumps the given message and stack to the system error console. */ public static void dump(String message, StackTraceElement[] stackTrace) { dump(message, Arrays.asList(stackTrace)); } /** Dumps the given message and exception stack to the system error console */ public static void dump(String message, Throwable exception) { printEmphasized(StringPrinter.buildString(printer -> { printer.println(message); exception.printStackTrace(printer.toPrintWriter()); })); } /** Dumps the current stack to the system error console. */ public static void dump(String message) { dump(message, captureStackBelow()); } /** Dumps the first {@code stackLimit} frames of the current stack to the system error console, excluding traces from {@code classPrefixesToExclude}. */ public static void dump(String message, int stackLimit, String... classPrefixesToExclude) { Objects.requireNonNull(message); // class names to include Predicate isIncluded = trace -> { for (String prefix : classPrefixesToExclude) { if (trace.getClassName().startsWith(prefix)) { return false; } } return true; }; // filter the stack List stack = captureStackBelow().stream() .filter(trace -> trace.getLineNumber() >= 0) .filter(isIncluded) .limit(stackLimit) .collect(Collectors.toList()); dump(message, stack); } /** Dumps a stack trace anytime the trigger string is printed to System.out. */ public static void dumpWhenSysOutContains(String trigger) { System.setOut(wrapAndDumpWhenContains(System.out, trigger)); } /** Dumps a stack trace anytime trigger string is printed to System.err. */ public static void dumpWhenSysErrContains(String trigger) { System.setErr(wrapAndDumpWhenContains(System.err, trigger)); } /** * Returns a PrintStream which will redirect all of its output to the source PrintStream. If * the trigger string is passed through the wrapped PrintStream, then it will dump the * stack trace of the call that printed the trigger. * * @param source * the returned PrintStream will delegate to this stream * @param trigger * the string which triggers a stack dump * @return a PrintStream with the above properties */ public static PrintStream wrapAndDumpWhenContains(PrintStream source, String trigger) { Objects.requireNonNull(source); Objects.requireNonNull(trigger); StringPrinter wrapped = new StringPrinter(StringPrinter.stringsToLines(perLine -> { source.println(perLine); if (perLine.contains(trigger)) { dump("Triggered by " + trigger); } })); return wrapped.toPrintStream(); } /** Converts a list of stack trace elements to a String similar to Throwable.printStackTrace(). */ private static String stackTraceToString(List stack) { return StringPrinter.buildString(printer -> { for (StackTraceElement element : stack) { printer.print("at "); printer.print(element.getClassName()); printer.print("."); printer.print(element.getMethodName()); printer.print("("); printer.print(element.getFileName() != null ? element.getFileName() : "null"); printer.print(":"); printer.print(Integer.toString(element.getLineNumber())); printer.println(")"); } }); } /** Captures all of the current stack which is below the given classes. */ public static List captureStackBelow(Class... clazzes) { List> toIgnore = new ArrayList<>(clazzes.length + 1); toIgnore.addAll(Arrays.asList(clazzes)); toIgnore.add(StackDumper.class); Predicate isSkipped = element -> toIgnore.stream().anyMatch(clazz -> { String name = element.getClassName(); return name.equals(clazz.getName()) || name.startsWith(clazz.getName() + "$$Lambda"); }); List rawStack = Arrays.asList(Thread.currentThread().getStackTrace()); ListIterator iterator = rawStack.listIterator(); // iterate until we find something skipped while (iterator.hasNext() && !isSkipped.test(iterator.next())) {} boolean foundSomethingToSkip = iterator.hasNext(); if (foundSomethingToSkip) { // iterate unti we find something not skipped while (iterator.hasNext() && isSkipped.test(iterator.next())) {} // the filtering was successful! return rawStack.subList(iterator.previousIndex(), rawStack.size()); } else { // we didn't find something to skip, so we'll return the whole stack return rawStack; } } /** * Prints the given string to the the given printer, wrapped in hierarchy-friendly * braces. Useful for emphasizing a specific event from a sea of logging statements. */ private static void printEmphasized(String toPrint) { // print the triggered header pristineSysErr.println("+----------\\"); for (String line : toPrint.split("\n")) { pristineSysErr.println("| " + line); } pristineSysErr.println("+----------/"); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy