|
|
|
@ -2,13 +2,16 @@ package com.fr.design.carton;
|
|
|
|
|
|
|
|
|
|
import com.fr.json.JSONObject; |
|
|
|
|
import com.fr.log.FineLoggerFactory; |
|
|
|
|
import com.fr.stable.ArrayUtils; |
|
|
|
|
import com.fr.stable.ProductConstantsBase; |
|
|
|
|
import com.fr.stable.StableUtils; |
|
|
|
|
import com.fr.stable.StringUtils; |
|
|
|
|
import com.fr.third.ibm.icu.text.SimpleDateFormat; |
|
|
|
|
import org.gradle.internal.impldep.com.google.gson.JsonObject; |
|
|
|
|
import org.jetbrains.annotations.NotNull; |
|
|
|
|
|
|
|
|
|
import java.awt.*; |
|
|
|
|
import java.awt.EventQueue; |
|
|
|
|
import java.awt.Toolkit; |
|
|
|
|
import java.awt.AWTEvent; |
|
|
|
|
import java.awt.event.WindowEvent; |
|
|
|
|
import java.io.BufferedWriter; |
|
|
|
|
import java.io.File; |
|
|
|
@ -30,11 +33,6 @@ import java.util.TimerTask;
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
public final class EventDispatchThreadHangMonitor extends EventQueue { |
|
|
|
|
/** |
|
|
|
|
* 一个标识位用于区分耗时任务时长检测(简单检测)和timer检测 |
|
|
|
|
*/ |
|
|
|
|
private static final int TIMER_CHECK_FLAG = 0; |
|
|
|
|
private static final int EASY_CHECK_FLAG = 1; |
|
|
|
|
/** |
|
|
|
|
* 日期事件格式 |
|
|
|
|
*/ |
|
|
|
@ -52,29 +50,29 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
/** |
|
|
|
|
* 最大的事件允许执行时间,超过该时间则打印堆栈等相关信息 |
|
|
|
|
*/ |
|
|
|
|
private static final long UNREASONABLE_DISPATCH_DURATION_MS = 2000; |
|
|
|
|
private static final long UNREASONABLE_DISPATCH_DURATION_MS = 1500; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* /事件唯一编码,用于方便日志的查看 |
|
|
|
|
* 事件唯一编码,用于方便日志的查看 |
|
|
|
|
*/ |
|
|
|
|
private static long hangCount = 0; |
|
|
|
|
/** |
|
|
|
|
* /输出日志所在地址 |
|
|
|
|
* 输出日志所在地址 |
|
|
|
|
*/ |
|
|
|
|
private static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* /类似于一个开关,当该值为默认的false启动时,定时任务在窗口开启前都不会对执行的事件进行检查 |
|
|
|
|
* 类似于一个开关,当该值为默认的false启动时,定时任务在窗口开启前都不会对执行的事件进行检查 |
|
|
|
|
*/ |
|
|
|
|
private boolean haveShownSomeComponent = false; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* /该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove |
|
|
|
|
* 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove |
|
|
|
|
*/ |
|
|
|
|
private final LinkedList<DispatchInfo> dispatches = new LinkedList<DispatchInfo>(); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* /一个变量,用于控制easy监测模式的开关 |
|
|
|
|
* 一个变量,用于控制easy监测模式的开关 |
|
|
|
|
*/ |
|
|
|
|
private boolean easyWitch = false; |
|
|
|
|
|
|
|
|
@ -87,8 +85,7 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* /一个变量,用于记录Timer的开关。 |
|
|
|
|
* |
|
|
|
|
* 一个变量,用于记录Timer的开关。 |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
public boolean isTimerWitch() { |
|
|
|
@ -104,9 +101,15 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
private synchronized static long getHangCount() { |
|
|
|
|
return hangCount++; |
|
|
|
|
} |
|
|
|
|
public static boolean stacksEqual(StackTraceElement[] a, StackTraceElement[] b) { |
|
|
|
|
|
|
|
|
|
if (a.length != b.length) { |
|
|
|
|
/** |
|
|
|
|
* @param a can not be null |
|
|
|
|
* @param b can not be null |
|
|
|
|
* @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) { |
|
|
|
@ -118,24 +121,29 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* / 用于堆栈的比较 |
|
|
|
|
* 用于判断是不是特定的堆栈 |
|
|
|
|
*/ |
|
|
|
|
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 stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true) && stackTraceElementIs(currentStack[1], "java.lang.Object", "wait", false) && stackTraceElementIs(currentStack[2], "java.awt.EventQueue", "getNextEvent", false); |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* /event事件的包装类 |
|
|
|
|
* event事件的包装类 |
|
|
|
|
*/ |
|
|
|
|
public static class DispatchInfo { |
|
|
|
|
// 上一次被打印的堆栈
|
|
|
|
|
// 上一次被打印的堆栈ou
|
|
|
|
|
private StackTraceElement[] lastReportedStack; |
|
|
|
|
//获取执行该事件的线程
|
|
|
|
|
private final Thread eventDispatchThread = Thread.currentThread(); |
|
|
|
@ -173,7 +181,7 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); |
|
|
|
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar()); |
|
|
|
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); |
|
|
|
|
outPutJournalLog(jsonObject.toString(), TIMER_CHECK_FLAG); |
|
|
|
|
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); |
|
|
|
|
checkForDeadlock(); |
|
|
|
|
} |
|
|
|
|
//记录连续运行了多长时间
|
|
|
|
@ -192,14 +200,15 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); |
|
|
|
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormat.format(startDispatchTimeMillis)); |
|
|
|
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms"); |
|
|
|
|
outPutJournalLog(jsonObject.toString(), EASY_CHECK_FLAG); |
|
|
|
|
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static void outPutJournalLog(String message, int flag) { |
|
|
|
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
|
|
|
|
String date = simpleDateFormat.format(System.currentTimeMillis()); |
|
|
|
|
String filename = flag == EASY_CHECK_FLAG ? "easy_check_log.csv": "timer_check_log.csv"; |
|
|
|
|
String filename = flag == SwitchForSwingChecker.EASY_CHECK_FLAG ? SwitchForSwingChecker.EASY_CHECKER_FILE_NAME: SwitchForSwingChecker.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); |
|
|
|
@ -262,7 +271,6 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Sets up hang detection for the event dispatch thread. |
|
|
|
|
* 将swing中默认的EventQueue换成自己的 |
|
|
|
|
*/ |
|
|
|
|
public static void initMonitoring() { |
|
|
|
@ -292,6 +300,7 @@ public final class EventDispatchThreadHangMonitor extends EventQueue {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Starts tracking a dispatch. |
|
|
|
|
*/ |
|
|
|
|