帆软报表设计器源代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

306 lines
11 KiB

package com.fr.design.carton;
import com.fr.concurrent.FineExecutors;
import com.fr.design.carton.latency.DesignerLatencyMetric;
import com.fr.design.ui.util.UIUtil;
import com.fr.json.JSONObject;
import javax.swing.SwingUtilities;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.AWTEvent;
import java.awt.event.WindowEvent;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.fr.design.carton.CartonConstants.CHECK_INTERVAL_MS;
import static com.fr.design.carton.CartonConstants.DATE_FORMAT;
import static com.fr.design.carton.CartonConstants.LATENCY_SAMPLING_FREQUENCY;
import static com.fr.design.carton.CartonConstants.UNREASONABLE_DISPATCH_DURATION_MS;
/**
* 参考自git swinghelper
* 用于卡顿检测
* 主要是两块内容
* 1.获取eventQueue中每个事件的执行时间
* 2.用一个Timer定时去检测当前执行任务的线程
*/
public final class EventDispatchThreadHangMonitor extends EventQueue {
public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor();
/**
* 一个timer
*/
private Timer timer;
/**
* 事件唯一编码,用于方便日志的查看
*/
private static long hangCount = 0;
/**
* 类似于一个开关,当该值为默认的false启动时,定时任务在窗口开启前都不会对执行的事件进行检查
*/
private boolean haveShownSomeComponent = false;
/**
* 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove
*/
private final LinkedList<DispatchInfo> dispatches = new LinkedList<DispatchInfo>();
/**
* 一个变量,用于控制easy监测模式的开关
*/
private boolean easyWitch = false;
private static ScheduledExecutorService scheduledExecutorService;
public boolean isEasyWitch() {
return easyWitch;
}
public void setEasyWitch(boolean easyWitch) {
this.easyWitch = easyWitch;
}
/**
* 一个变量,用于记录Timer的开关。
*/
public boolean isTimerWitch() {
return timerWitch;
}
public void setTimerWitch(boolean timerWitch) {
this.timerWitch = timerWitch;
}
private boolean timerWitch = false;
private synchronized static long getHangCount() {
return hangCount++;
}
/**
* event事件的包装类
*/
public static class DispatchInfo {
// 上一次被打印的堆栈ou
private StackTraceElement[] lastReportedStack;
//获取执行该事件的线程
private final Thread eventDispatchThread = Thread.currentThread();
//在队列中等待执行的事件最后未执行的时间,当有一个事件执行完后就遍历dispatches给该值赋当前时间
private long lastDispatchTimeMillis = System.currentTimeMillis();
//事件开始的时间
private final long startDispatchTimeMillis = System.currentTimeMillis();
//事件编号
private final long hangNumber;
//构造函数,给当前对象赋一个递增的唯一编号
public DispatchInfo() {
hangNumber = getHangCount();
}
//定时调度任务检测的入口,如果执行时间大于设定的值就进入examineHang()方法
public void checkForHang() {
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) {
examineHang();
}
}
//超时堆栈的具体处理
private void examineHang() {
//获取执行线程的当前堆栈
StackTraceElement[] currentStack = eventDispatchThread.getStackTrace();
if (CartonUtils.isWaitingForNextEvent(currentStack)) {
return;
}
//某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次
if (lastReportedStack != null && CartonUtils.stacksEqual(lastReportedStack, currentStack)) {
return;
}
String stackTrace = CartonUtils.stackTraceToString(currentStack);
lastReportedStack = currentStack;
JSONObject jsonObject = new JSONObject();
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), DATE_FORMAT.format(System.currentTimeMillis()));
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() + "ms");
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace);
CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG);
CartonUtils.checkForDeadlock();
}
//记录连续运行了多长时间
public long timeSoFar() {
return (System.currentTimeMillis() - lastDispatchTimeMillis);
}
//记录一个事件从被分发到结束的总运行时间
public long totalTime() {
return (System.currentTimeMillis() - startDispatchTimeMillis);
}
//事件处理完后的时间判断
public void dispose() {
if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) {
exportCartonLog(true);
} else if (lastReportedStack != null) {
exportCartonLog(false);
}
}
/**
*
* @param flag 判断一下输出日志时要输出哪个时间
*/
private void exportCartonLog(boolean flag) {
JSONObject jsonObject = new JSONObject();
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), DATE_FORMAT.format(System.currentTimeMillis()));
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"), DATE_FORMAT.format(startDispatchTimeMillis));
if (flag) {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), timeSoFar() + "ms");
} else {
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms");
}
CartonUtils.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG);
}
}
private EventDispatchThreadHangMonitor() {
}
/**
* 参考SwingExplorer,在处理模态框时没有做特殊处理,也不会输出卡顿堆栈
* 原因是SwingExplorer窗口一直有一个监听事件,不断的add,remove。
* 由于卡顿日志输出的是事件连续执行的时间,所以一个长时间存在的模态框被不断重复的监听事件刷新时间,就不会输出了。
* 当检测开关打开后,在这里模拟一下监听事件,给个不耗时的任务就可以。
*/
public void startFilterModalWindow() {
scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//不用干事,切个片就可以
}
});
}
}, 0, 500, TimeUnit.MILLISECONDS);
}
public void stopFilterModalWindow() {
if (scheduledExecutorService != null) {
scheduledExecutorService.shutdown();
}
}
/**
* Sets up a timer to check for hangs frequently.
* 初始化一个Timer
*/
public void initTimer() {
final long initialDelayMs = 0;
final boolean daemon = true;
timer = new Timer("EventDispatchThreadHangMonitor", daemon);
timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS);
}
/**
* /消除Timer
*/
public void stopTimer() {
if (timer != null) {
timer.cancel();
}
}
/**
* /定时执行的任务
*/
public class HangChecker extends TimerTask {
@Override
public void run() {
synchronized (dispatches) {
//如果链表为空或者窗口还没启开,定时检测就不进行
if (dispatches.isEmpty() || !haveShownSomeComponent) {
return;
}
dispatches.getLast().checkForHang();
}
}
}
/**
* 将swing中默认的EventQueue换成自己的
*/
public static void initMonitoring() {
UIUtil.invokeLaterIfNeeded(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE));
}
/**
* Overrides EventQueue.dispatchEvent to call our pre and post hooks either
* side of the system's event dispatch code.
* 重写
*/
@Override
protected void dispatchEvent(AWTEvent event) {
if (!useCustomEventQueue()) {
super.dispatchEvent(event);
} else {
try {
preDispatchEvent();
super.dispatchEvent(event);
} finally {
postDispatchEvent();
if (!haveShownSomeComponent &&
event instanceof WindowEvent && event.getID() == WindowEvent.WINDOW_OPENED) {
haveShownSomeComponent = true;
}
}
}
}
private boolean useCustomEventQueue() {
// 开启性能监控或开启卡顿工具箱,则走自定义的EventQueue
return SwitchForSwingChecker.isLatencyMonitoring() ||
isEasyWitch() || isTimerWitch();
}
private boolean needSampling() {
// UI性能采样逻辑:开启采样并且符合采样频次
return SwitchForSwingChecker.isLatencyMonitoring()
&& (hangCount % LATENCY_SAMPLING_FREQUENCY == 0);
}
/**
* Starts tracking a dispatch.
*/
private synchronized void preDispatchEvent() {
synchronized (dispatches) {
dispatches.addLast(new DispatchInfo());
}
}
/**
* Stops tracking a dispatch.
*/
private synchronized void postDispatchEvent() {
synchronized (dispatches) {
DispatchInfo justFinishedDispatch = dispatches.removeLast();
if (needSampling()) {
DesignerLatencyMetric.getInstance().record(justFinishedDispatch.timeSoFar());
}
if (isEasyWitch()) {
justFinishedDispatch.dispose();
}
//嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis
Thread currentEventDispatchThread = Thread.currentThread();
for (DispatchInfo dispatchInfo : dispatches) {
if (dispatchInfo.eventDispatchThread == currentEventDispatchThread) {
dispatchInfo.lastDispatchTimeMillis = System.currentTimeMillis();
}
}
}
}
}