|
|
|
package com.fr.design.carton;
|
|
|
|
|
|
|
|
import com.fr.log.FineLoggerFactory;
|
|
|
|
import com.fr.stable.ArrayUtils;
|
|
|
|
import com.fr.stable.StableUtils;
|
|
|
|
import com.fr.stable.StringUtils;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
|
|
|
|
import java.io.BufferedWriter;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileWriter;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.lang.management.ManagementFactory;
|
|
|
|
import java.lang.management.ThreadInfo;
|
|
|
|
import java.lang.management.ThreadMXBean;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
|
|
|
|
import static com.fr.design.carton.CartonConstants.EASY_CHECKER_FILE_NAME;
|
|
|
|
import static com.fr.design.carton.CartonConstants.JOURNAL_FILE_PATH;
|
|
|
|
import static com.fr.design.carton.CartonConstants.TIMER_CHECKER_FILE_NAME;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 设计器卡顿业务工具类
|
|
|
|
*
|
|
|
|
* @author Levy.Xie
|
|
|
|
* @since 11.0
|
|
|
|
* Created on 2024/07/10
|
|
|
|
*/
|
|
|
|
public class CartonUtils {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 堆栈元素输出为文本
|
|
|
|
*
|
|
|
|
* @param stackTrace 堆栈元素
|
|
|
|
* @return 文本
|
|
|
|
*/
|
|
|
|
public static String stackTraceToString(StackTraceElement[] stackTrace) {
|
|
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
for (StackTraceElement stackTraceElement : stackTrace) {
|
|
|
|
String indentation = " ";
|
|
|
|
result.append("~").append(indentation).append(stackTraceElement);
|
|
|
|
}
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 命令行显示堆栈信息
|
|
|
|
*
|
|
|
|
* @param stackTrace 堆栈元素
|
|
|
|
* @return 文本
|
|
|
|
*/
|
|
|
|
public static String stackTraceToStringForConsole(StackTraceElement[] stackTrace) {
|
|
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
for (StackTraceElement stackTraceElement : stackTrace) {
|
|
|
|
String indentation = " ";
|
|
|
|
result.append("\r\n").append(indentation).append(stackTraceElement);
|
|
|
|
}
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 堆栈是否一致
|
|
|
|
*
|
|
|
|
* @param a 堆栈A
|
|
|
|
* @param b 堆栈B
|
|
|
|
* @return 是否一致
|
|
|
|
*/
|
|
|
|
public static boolean stacksEqual(@NotNull StackTraceElement[] a, @NotNull StackTraceElement[] b) {
|
|
|
|
if (!ArrayUtils.isSameLength(a, b)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < a.length; ++i) {
|
|
|
|
if (!a[i].equals(b[i])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 检查死锁
|
|
|
|
*/
|
|
|
|
public static void checkForDeadlock() {
|
|
|
|
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
|
|
|
|
long[] threadIds = threadBean.findDeadlockedThreads();
|
|
|
|
if (threadIds == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
FineLoggerFactory.getLogger().warn("deadlock detected involving the following threads:");
|
|
|
|
ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
|
|
|
|
for (ThreadInfo info : threadInfos) {
|
|
|
|
FineLoggerFactory.getLogger().warn("Thread # {} {} ( {} ) waiting on {} held by {} {}", info.getThreadId(), info.getThreadName(),
|
|
|
|
info.getThreadState(), info.getLockName(), info.getLockOwnerName(), CartonUtils.stackTraceToStringForConsole(info.getStackTrace()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 输出卡顿日志
|
|
|
|
*
|
|
|
|
* @param message 文本信息
|
|
|
|
* @param flag 类型,true时为简单检查、false时为定时检查
|
|
|
|
*/
|
|
|
|
public static void outPutJournalLog(String message, int flag) {
|
|
|
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
String date = simpleDateFormat.format(System.currentTimeMillis());
|
|
|
|
String filename = flag == SwitchForSwingChecker.EASY_CHECK_FLAG ? EASY_CHECKER_FILE_NAME : TIMER_CHECKER_FILE_NAME;
|
|
|
|
String[] split = date.split("-");
|
|
|
|
int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]);
|
|
|
|
String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date);
|
|
|
|
File dirFile = new File(dirPath);
|
|
|
|
File file = new File(StableUtils.pathJoin(dirPath, filename));
|
|
|
|
try {
|
|
|
|
if (!file.exists()) {
|
|
|
|
if (!dirFile.exists()) {
|
|
|
|
dirFile.mkdirs();
|
|
|
|
}
|
|
|
|
file.createNewFile();
|
|
|
|
}
|
|
|
|
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true))) {
|
|
|
|
String outputMessage = message.replaceAll("~", "\r\n") + "," + "\r\n";
|
|
|
|
bufferedWriter.write(outputMessage);
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
FineLoggerFactory.getLogger().error("output fail", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 用于判断是不是特定的堆栈
|
|
|
|
*/
|
|
|
|
public static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) {
|
|
|
|
return e.getClassName().equals(className) && e.getMethodName().equals(methodName) && e.isNativeMethod() == isNative;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 用于判断某个堆栈是否在等待另一个事件
|
|
|
|
* 取当前堆栈前三层判断是是不是匹配等待堆栈的格式
|
|
|
|
*/
|
|
|
|
public static boolean isWaitingForNextEvent(StackTraceElement[] currentStack) {
|
|
|
|
|
|
|
|
return currentStack != null && currentStack.length >= 3 &&
|
|
|
|
stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true)
|
|
|
|
&& stackTraceElementIs(currentStack[1], "java.lang.Object", "wait", false)
|
|
|
|
&& stackTraceElementIs(currentStack[2], "java.awt.EventQueue", "getNextEvent", false);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|