Browse Source
Merge in DESIGN/design from ~JOHN.YING/design:feature/x to feature/x * commit 'c71043cab6e885c5bd574a57f554b069adccc5a2': REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期 REPORT-72634 设计器卡顿优化一期feature/x
John.Ying
2 years ago
12 changed files with 1577 additions and 198 deletions
@ -0,0 +1,28 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
public class CartonFiles { |
||||||
|
private File easyCheckerFile; |
||||||
|
private File timerCheckerFile; |
||||||
|
|
||||||
|
public CartonFiles() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public File getEasyCheckerFile() { |
||||||
|
return easyCheckerFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEasyCheckerFile(File easyCheckerFile) { |
||||||
|
this.easyCheckerFile = easyCheckerFile; |
||||||
|
} |
||||||
|
|
||||||
|
public File getTimerCheckerFile() { |
||||||
|
return timerCheckerFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTimerCheckerFile(File timerCheckerFile) { |
||||||
|
this.timerCheckerFile = timerCheckerFile; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,161 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import com.fr.base.SimpleDateFormatThreadSafe; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import java.util.concurrent.*; |
||||||
|
import java.util.concurrent.atomic.AtomicLong; |
||||||
|
|
||||||
|
|
||||||
|
public class CartonThreadExecutorPool extends ThreadPoolExecutor { |
||||||
|
private static final int MAX_LIVE_TIME = 3000; |
||||||
|
private static final int MAX_WORKER_THREADS = 10; |
||||||
|
/** |
||||||
|
* 开启间隔检测后两次检测的相隔时间ms |
||||||
|
*/ |
||||||
|
private static final long CHECK_INTERVAL_MS = 100; |
||||||
|
private final ThreadLocal<StackTraceElement[]> startReportedStack = new ThreadLocal<>(); |
||||||
|
private volatile static CartonThreadExecutorPool cartonThreadExecutorPool; |
||||||
|
private static final ConcurrentHashMap<Long, ThreadInfo> concurrentHashMap = new ConcurrentHashMap<>(); |
||||||
|
private static final SimpleDateFormatThreadSafe simpleDateFormatThreadSafe = new SimpleDateFormatThreadSafe(); |
||||||
|
private final static AtomicLong hangCount = new AtomicLong(0); |
||||||
|
private Timer timer; |
||||||
|
/** |
||||||
|
* 一个变量,用于控制easy监测模式的开关 |
||||||
|
*/ |
||||||
|
private boolean easyWitch = false; |
||||||
|
|
||||||
|
public boolean isEasyWitch() { |
||||||
|
return easyWitch; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEasyWitch(boolean easyWitch) { |
||||||
|
this.easyWitch = easyWitch; |
||||||
|
} |
||||||
|
|
||||||
|
private static class ThreadInfo { |
||||||
|
private ThreadInfo () { |
||||||
|
hangNumber = hangCount.getAndAdd(1); |
||||||
|
} |
||||||
|
private long hangNumber; |
||||||
|
private final Thread eventThread = Thread.currentThread(); |
||||||
|
private StackTraceElement[] lastReportedStack; |
||||||
|
private final long startTime = System.currentTimeMillis(); |
||||||
|
public void checkForHang() { |
||||||
|
if (timeSoFar() > MAX_LIVE_TIME) { |
||||||
|
examineHang(); |
||||||
|
} |
||||||
|
} |
||||||
|
private long timeSoFar() { |
||||||
|
return (System.currentTimeMillis() - startTime); |
||||||
|
} |
||||||
|
|
||||||
|
private void examineHang() { |
||||||
|
StackTraceElement[] currentStack = eventThread.getStackTrace(); |
||||||
|
|
||||||
|
if (lastReportedStack!=null && EventDispatchThreadHangMonitor.stacksEqual(currentStack, lastReportedStack)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
lastReportedStack = currentStack; |
||||||
|
String stackTrace = EventDispatchThreadHangMonitor.stackTraceToString(currentStack); |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + hangNumber); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar()); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); |
||||||
|
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); |
||||||
|
EventDispatchThreadHangMonitor.checkForDeadlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 来自SwingWorker类 |
||||||
|
*/ |
||||||
|
public static ThreadFactory threadFactory = |
||||||
|
new ThreadFactory() { |
||||||
|
final ThreadFactory defaultFactory = |
||||||
|
Executors.defaultThreadFactory(); |
||||||
|
@Override |
||||||
|
public Thread newThread(final Runnable r) { |
||||||
|
Thread thread = |
||||||
|
defaultFactory.newThread(r); |
||||||
|
thread.setName("SwingWorker-" |
||||||
|
+ thread.getName()); |
||||||
|
thread.setDaemon(true); |
||||||
|
return thread; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static CartonThreadExecutorPool getTimerThreadExecutorPool () { |
||||||
|
if (cartonThreadExecutorPool == null) { |
||||||
|
synchronized (CartonThreadExecutorPool.class) { |
||||||
|
if (cartonThreadExecutorPool == null) { |
||||||
|
cartonThreadExecutorPool = |
||||||
|
new CartonThreadExecutorPool(MAX_WORKER_THREADS, MAX_WORKER_THREADS, |
||||||
|
10L, TimeUnit.MINUTES, |
||||||
|
new LinkedBlockingQueue<Runnable>(),threadFactory |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return cartonThreadExecutorPool; |
||||||
|
} |
||||||
|
|
||||||
|
private CartonThreadExecutorPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { |
||||||
|
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); |
||||||
|
simpleDateFormatThreadSafe.applyPattern("yyyy-MM-dd HH:mm:ss"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void beforeExecute(Thread t, Runnable r) { |
||||||
|
super.beforeExecute(t, r); |
||||||
|
startReportedStack.set(t.getStackTrace()); |
||||||
|
concurrentHashMap.put(t.getId(), new ThreadInfo()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void afterExecute(Runnable r, Throwable t) { |
||||||
|
super.afterExecute(r, t); |
||||||
|
long currentThreadId = Thread.currentThread().getId(); |
||||||
|
long runTime = (System.currentTimeMillis() - concurrentHashMap.get(currentThreadId).startTime); |
||||||
|
//加~是为了之后输出的时候换行。
|
||||||
|
if (isEasyWitch() && runTime > MAX_LIVE_TIME) { |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + concurrentHashMap.get(currentThreadId).hangNumber); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormatThreadSafe.format(concurrentHashMap.get(currentThreadId).startTime)); |
||||||
|
jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), runTime); |
||||||
|
EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); |
||||||
|
|
||||||
|
} |
||||||
|
concurrentHashMap.remove(currentThreadId); |
||||||
|
} |
||||||
|
|
||||||
|
public class Checker extends TimerTask { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (cartonThreadExecutorPool == null || concurrentHashMap.isEmpty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
for (Map.Entry<Long, ThreadInfo> map : concurrentHashMap.entrySet()) { |
||||||
|
map.getValue().checkForHang(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void initTimer() { |
||||||
|
timer = new Timer("CheckerSwingWorker",true); |
||||||
|
timer.schedule(new Checker(), 0, CHECK_INTERVAL_MS); |
||||||
|
} |
||||||
|
|
||||||
|
public void stopTimer() { |
||||||
|
if (timer != null) { |
||||||
|
timer.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
|
||||||
|
public class CartonUploadMessage { |
||||||
|
private String hangCount; |
||||||
|
private String slowTime; |
||||||
|
private String threadTime; |
||||||
|
private String info; |
||||||
|
|
||||||
|
public CartonUploadMessage() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public String getHangCount() { |
||||||
|
return hangCount; |
||||||
|
} |
||||||
|
|
||||||
|
public void setHangCount(String hangCount) { |
||||||
|
this.hangCount = hangCount; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSlowTime() { |
||||||
|
return slowTime; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSlowTime(String slowTime) { |
||||||
|
this.slowTime = slowTime; |
||||||
|
} |
||||||
|
|
||||||
|
public String getThreadTime() { |
||||||
|
return threadTime; |
||||||
|
} |
||||||
|
|
||||||
|
public void setThreadTime(String threadTime) { |
||||||
|
this.threadTime = threadTime; |
||||||
|
} |
||||||
|
|
||||||
|
public String getInfo() { |
||||||
|
return info; |
||||||
|
} |
||||||
|
|
||||||
|
public void setInfo(String info) { |
||||||
|
this.info = info; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,366 @@ |
|||||||
|
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.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
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; |
||||||
|
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.util.LinkedList; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
|
||||||
|
/** |
||||||
|
* 参考自git swinghelper |
||||||
|
* 用于卡顿检测 |
||||||
|
* 主要是两块内容 |
||||||
|
* 1.获取eventQueue中每个事件的执行时间 |
||||||
|
* 2.用一个Timer定时去检测当前执行任务的线程 |
||||||
|
*/ |
||||||
|
|
||||||
|
public final class EventDispatchThreadHangMonitor extends EventQueue { |
||||||
|
/** |
||||||
|
* 日期事件格式 |
||||||
|
*/ |
||||||
|
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
||||||
|
public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor(); |
||||||
|
/** |
||||||
|
* 一个timer |
||||||
|
*/ |
||||||
|
private Timer timer; |
||||||
|
/** |
||||||
|
* 开启间隔检测后两次检测的相隔时间ms |
||||||
|
*/ |
||||||
|
private static final long CHECK_INTERVAL_MS = 100; |
||||||
|
|
||||||
|
/** |
||||||
|
* 最大的事件允许执行时间,超过该时间则打印堆栈等相关信息 |
||||||
|
*/ |
||||||
|
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启动时,定时任务在窗口开启前都不会对执行的事件进行检查 |
||||||
|
*/ |
||||||
|
private boolean haveShownSomeComponent = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove |
||||||
|
*/ |
||||||
|
private final LinkedList<DispatchInfo> dispatches = new LinkedList<DispatchInfo>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 一个变量,用于控制easy监测模式的开关 |
||||||
|
*/ |
||||||
|
private boolean easyWitch = false; |
||||||
|
|
||||||
|
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++; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @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) { |
||||||
|
if (!a[i].equals(b[i])) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于判断是不是特定的堆栈 |
||||||
|
*/ |
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 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 (isWaitingForNextEvent(currentStack)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
//某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次
|
||||||
|
if (lastReportedStack !=null && stacksEqual(lastReportedStack, currentStack)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String stackTrace = stackTraceToString(currentStack); |
||||||
|
lastReportedStack = currentStack; |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.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()); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); |
||||||
|
outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); |
||||||
|
checkForDeadlock(); |
||||||
|
} |
||||||
|
//记录连续运行了多长时间
|
||||||
|
private long timeSoFar() { |
||||||
|
return (System.currentTimeMillis() - lastDispatchTimeMillis); |
||||||
|
} |
||||||
|
//记录一个事件从被分发到结束的总运行时间
|
||||||
|
private long totalTime() { |
||||||
|
return (System.currentTimeMillis() - startDispatchTimeMillis); |
||||||
|
} |
||||||
|
//事件处理完后的时间判断
|
||||||
|
public void dispose() { |
||||||
|
if (totalTime() > UNREASONABLE_DISPATCH_DURATION_MS) { |
||||||
|
JSONObject jsonObject = new JSONObject(); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.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"), simpleDateFormat.format(startDispatchTimeMillis)); |
||||||
|
jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), totalTime() + "ms"); |
||||||
|
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 == 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); |
||||||
|
File dirFile = new File(dirPath); |
||||||
|
File file = new File( StableUtils.pathJoin(dirPath, filename)); |
||||||
|
try { |
||||||
|
if (!file.exists()) { |
||||||
|
if (!dirFile.exists()) { |
||||||
|
dirFile.mkdirs(); |
||||||
|
} |
||||||
|
file.createNewFile(); |
||||||
|
} |
||||||
|
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true)); |
||||||
|
String outputMessage = new StringBuilder(message.replaceAll("~", "\r\n")).append(",").append("\r\n").toString(); |
||||||
|
bufferedWriter.write(outputMessage); |
||||||
|
bufferedWriter.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error("output fail", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private EventDispatchThreadHangMonitor() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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() { |
||||||
|
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 (!isEasyWitch() && !isTimerWitch()) { |
||||||
|
super.dispatchEvent(event); |
||||||
|
} else { |
||||||
|
try { |
||||||
|
preDispatchEvent(); |
||||||
|
super.dispatchEvent(event); |
||||||
|
} finally { |
||||||
|
postDispatchEvent(); |
||||||
|
if (!haveShownSomeComponent && |
||||||
|
event instanceof WindowEvent && event.getID() == WindowEvent.WINDOW_OPENED) { |
||||||
|
haveShownSomeComponent = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 (isEasyWitch()) { |
||||||
|
justFinishedDispatch.dispose(); |
||||||
|
} |
||||||
|
//嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis
|
||||||
|
Thread currentEventDispatchThread = Thread.currentThread(); |
||||||
|
for (DispatchInfo dispatchInfo : dispatches) { |
||||||
|
if (dispatchInfo.eventDispatchThread == currentEventDispatchThread) { |
||||||
|
dispatchInfo.lastDispatchTimeMillis = System.currentTimeMillis(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查死锁 |
||||||
|
*/ |
||||||
|
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(), stackTraceToStringForConsole(info.getStackTrace())); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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(); |
||||||
|
} |
||||||
|
|
||||||
|
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(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,330 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import com.fr.decision.webservice.v10.log.download.utils.LogZipUtils; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.dialog.FineJOptionPane; |
||||||
|
import com.fr.design.env.DesignerWorkspaceInfo; |
||||||
|
import com.fr.design.gui.date.UIDatePicker; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.icheckbox.UICheckBox; |
||||||
|
import com.fr.design.gui.ilable.UILabel; |
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.DesignerContext; |
||||||
|
import com.fr.design.utils.gui.GUICoreUtils; |
||||||
|
import com.fr.env.detect.ui.EnvDetectorDialog; |
||||||
|
import com.fr.file.FILE; |
||||||
|
import com.fr.file.FILEChooserPane; |
||||||
|
import com.fr.file.filter.ChooseFileFilter; |
||||||
|
import com.fr.general.GeneralUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.awt.event.WindowEvent; |
||||||
|
import java.io.File; |
||||||
|
import java.text.ParseException; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class FeedbackToolboxDialog extends JDialog { |
||||||
|
private UIDatePicker uiDatePicker; |
||||||
|
private JPanel generalSettingPanel = null; |
||||||
|
private UICheckBox easyCheckerButton = null; |
||||||
|
private UICheckBox timerCheckerButton = null; |
||||||
|
private UIButton uploadButton = null; |
||||||
|
private UILabel exportLogLabel = null; |
||||||
|
private final Color backgroundColor = new Color(240, 240, 243, 1); |
||||||
|
private final Color lineColor = new Color(192, 192, 192, 120); |
||||||
|
private JPanel body = null; |
||||||
|
private static final String WORK_SPACE_PATH = "reportlets"; |
||||||
|
|
||||||
|
public FeedbackToolboxDialog(Frame owner) { |
||||||
|
super(owner, Toolkit.i18nText("Fine-Design_Basic_Carton_Feedback_ToolBox")); |
||||||
|
setResizable(false); |
||||||
|
this.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
createBodyPanel(); |
||||||
|
add(body); |
||||||
|
setSize(body.getPreferredSize()); |
||||||
|
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||||
|
repaint(); |
||||||
|
GUICoreUtils.centerWindow(this); |
||||||
|
} |
||||||
|
|
||||||
|
public void createBodyPanel() { |
||||||
|
JPanel body = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
body.setBackground(backgroundColor); |
||||||
|
JPanel titlePane = createTitlePane(); |
||||||
|
JPanel tailPane = createTailPane(); |
||||||
|
JPanel midPane = createMidPane(); |
||||||
|
JPanel infoPane = createInfoPane(); |
||||||
|
body.add(titlePane, BorderLayout.NORTH); |
||||||
|
body.add(tailPane, BorderLayout.SOUTH); |
||||||
|
body.add(midPane, BorderLayout.CENTER); |
||||||
|
midPane.add(infoPane, BorderLayout.NORTH); |
||||||
|
Dimension dimension = new Dimension(662, 556); |
||||||
|
body.setPreferredSize(dimension); |
||||||
|
this.body = body; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createInfoPane() { |
||||||
|
JPanel northPane = FRGUIPaneFactory.createNColumnGridInnerContainer_Pane(2, 10, 10); |
||||||
|
UILabel title = new UILabel(); |
||||||
|
//空格布局会好看一点
|
||||||
|
title.setText(" " + Toolkit.i18nText("Fine-Design_Basic_Carton_Record_Lag_Time") + ": "); |
||||||
|
//判断一下当天是否有卡顿日志记录,如果有将日期设置为当天,如果没有设置为空
|
||||||
|
boolean cartonExists = SwitchForSwingChecker.isCartonExists(); |
||||||
|
if (cartonExists) { |
||||||
|
this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, this); |
||||||
|
} else { |
||||||
|
this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, null, this); |
||||||
|
} |
||||||
|
Dimension dimension = new Dimension(160, 100); |
||||||
|
uiDatePicker.setPreferredSize(dimension); |
||||||
|
northPane.add(GUICoreUtils.createFlowPane(new Component[]{title, uiDatePicker}, FlowLayout.LEFT)); |
||||||
|
exportLogLabel = new UILabel(); |
||||||
|
exportLogLabel.setText(Toolkit.i18nText("Fine-Design_Basic_Carton_Export_Carton_Log")); |
||||||
|
exportLogLabel.setForeground(UIConstants.FLESH_BLUE); |
||||||
|
exportLogLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
if (exportLogLabel.isEnabled()) { |
||||||
|
exportLogFile(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent evt) { |
||||||
|
Object source = evt.getSource(); |
||||||
|
if (source instanceof UILabel) { |
||||||
|
((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
northPane.add(GUICoreUtils.createFlowPane(exportLogLabel, FlowLayout.RIGHT)); |
||||||
|
return northPane; |
||||||
|
} |
||||||
|
|
||||||
|
private void exportLogFile() { |
||||||
|
String selectDate = GeneralUtils.objectToString(uiDatePicker.getSelectedItem()); |
||||||
|
FILEChooserPane fileChooserPane = FILEChooserPane.getInstance(); |
||||||
|
StringBuilder fileName = new StringBuilder(); |
||||||
|
fileName.append(selectDate).append(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Log")); |
||||||
|
fileChooserPane.setFileNameTextField(fileName.toString(), " "); |
||||||
|
fileChooserPane.removeAllFilter(); |
||||||
|
fileChooserPane.addChooseFILEFilter(new ChooseFileFilter("zip", Toolkit.i18nText("Fine-Design_Basic_Carton_Compile_File"))); |
||||||
|
int chooseResult = fileChooserPane.showSaveDialog(DesignerContext.getDesignerFrame()); |
||||||
|
if (chooseResult == 0) { |
||||||
|
FILE selectedFile = fileChooserPane.getSelectedFILE(); |
||||||
|
String path = selectedFile.getPath(); |
||||||
|
//selectDate 2002-03-09例子
|
||||||
|
String[] split = selectDate.split("-"); |
||||||
|
int month = Integer.parseInt(split[1]); |
||||||
|
File sourceFile = new File(StableUtils.pathJoin(SwitchForSwingChecker.JOURNAL_FILE_PATH, split[0], "month-" + month, selectDate)); |
||||||
|
if (sourceFile.exists()) { |
||||||
|
File[] files = sourceFile.listFiles(); |
||||||
|
if (files != null) { |
||||||
|
try { |
||||||
|
if (path.startsWith(WORK_SPACE_PATH)) { |
||||||
|
String curEnvName = DesignerEnvManager.getEnvManager().getCurEnvName(); |
||||||
|
DesignerWorkspaceInfo workspaceInfo = DesignerEnvManager.getEnvManager().getWorkspaceInfo(curEnvName); |
||||||
|
String workspaceInfoPath = workspaceInfo.getPath(); |
||||||
|
path = new StringBuilder(workspaceInfoPath).append(path.substring(10)).toString(); |
||||||
|
} |
||||||
|
LogZipUtils.compress(files, path, false); |
||||||
|
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Report_Exported_Successfully")); |
||||||
|
} catch (Exception exception) { |
||||||
|
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Report_Export_Failed"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); |
||||||
|
FineLoggerFactory.getLogger().error("export file fail", exception); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
fileChooserPane.removeAllFilter(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTailPane() { |
||||||
|
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); |
||||||
|
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||||
|
{ |
||||||
|
uploadButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_Upload_Carton_Log")); |
||||||
|
uploadButton.addActionListener((e) -> { |
||||||
|
try { |
||||||
|
List<CartonUploadMessage> list = SwitchForSwingChecker.uploadJournalLog(uiDatePicker.getSelectedDate()); |
||||||
|
if (list.isEmpty()) { |
||||||
|
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine_Design_Basic_Upload_Fail"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); |
||||||
|
} else { |
||||||
|
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Basic_Upload_Success")); |
||||||
|
} |
||||||
|
} catch (ParseException parseException) { |
||||||
|
FineLoggerFactory.getLogger().error("parse error", parseException); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
actionsPanel.add(uploadButton, BorderLayout.WEST); |
||||||
|
|
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||||
|
cancelButton.addActionListener((e) -> { |
||||||
|
setVisible(false); |
||||||
|
EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); |
||||||
|
envDetectorDialog.setVisible(true); |
||||||
|
dispose(); |
||||||
|
}); |
||||||
|
actionsPanel.add(cancelButton, BorderLayout.EAST); |
||||||
|
} |
||||||
|
UIButton currencySetButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); |
||||||
|
currencySetButton.addActionListener((e -> { |
||||||
|
createGeneralSettingPanel(); |
||||||
|
this.remove(body); |
||||||
|
this.add(generalSettingPanel); |
||||||
|
setSize(generalSettingPanel.getPreferredSize()); |
||||||
|
repaint(); |
||||||
|
setVisible(true); |
||||||
|
})); |
||||||
|
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||||
|
tailPanel.add(currencySetButton, BorderLayout.WEST); |
||||||
|
return tailPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTitlePane() { |
||||||
|
JPanel titlePane = FRGUIPaneFactory.createBorderLayout_M_Pane(); |
||||||
|
titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); |
||||||
|
UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record")); |
||||||
|
uiLabel.setForeground(UIConstants.FLESH_BLUE); |
||||||
|
titlePane.add(uiLabel, BorderLayout.WEST); |
||||||
|
return titlePane; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createMidPane() { |
||||||
|
JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
return midPanel; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 下面是通用设置的面板 |
||||||
|
*/ |
||||||
|
private void createGeneralSettingPanel() { |
||||||
|
JPanel generalSettingPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
JPanel tailPaneInGeneralSettings = createTailPaneInGeneralSettings(); |
||||||
|
generalSettingPanel.add(tailPaneInGeneralSettings, BorderLayout.SOUTH); |
||||||
|
|
||||||
|
JPanel titlePaneInGeneralSettings = createTitlePaneInGeneralSettings(); |
||||||
|
generalSettingPanel.add(titlePaneInGeneralSettings, BorderLayout.NORTH); |
||||||
|
|
||||||
|
JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
generalSettingPanel.add(midPanel, BorderLayout.CENTER); |
||||||
|
|
||||||
|
JPanel infoPane = createInfoPanelInGeneralSettings(); |
||||||
|
midPanel.add(infoPane, BorderLayout.NORTH); |
||||||
|
|
||||||
|
Dimension dimension = new Dimension(662, 556); |
||||||
|
generalSettingPanel.setPreferredSize(dimension); |
||||||
|
generalSettingPanel.setBackground(backgroundColor); |
||||||
|
this.generalSettingPanel = generalSettingPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTitlePaneInGeneralSettings() { |
||||||
|
JPanel titlePane = FRGUIPaneFactory.createBorderLayout_L_Pane(); |
||||||
|
titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); |
||||||
|
UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record") + "/"); |
||||||
|
uiLabel.addMouseListener(new MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseClicked(MouseEvent e) { |
||||||
|
createBodyPanel(); |
||||||
|
remove(generalSettingPanel); |
||||||
|
add(body); |
||||||
|
setPreferredSize(body.getPreferredSize()); |
||||||
|
setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); |
||||||
|
repaint(); |
||||||
|
setVisible(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mouseEntered(MouseEvent evt) { |
||||||
|
Object source = evt.getSource(); |
||||||
|
if (source instanceof UILabel) { |
||||||
|
((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
UILabel uiCurrentLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); |
||||||
|
uiCurrentLabel.setForeground(UIConstants.FLESH_BLUE); |
||||||
|
titlePane.add(GUICoreUtils.createFlowPane(new Component[]{uiLabel, uiCurrentLabel}, FlowLayout.LEFT)); |
||||||
|
return titlePane; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createTailPaneInGeneralSettings() { |
||||||
|
JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); |
||||||
|
JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); |
||||||
|
{ |
||||||
|
UIButton confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Save")); |
||||||
|
confirmButton.addActionListener((e) -> { |
||||||
|
if (easyCheckerButton.isSelected()) { |
||||||
|
SwitchForSwingChecker.startEasyChecker(); |
||||||
|
} else { |
||||||
|
SwitchForSwingChecker.stopEasyChecker(); |
||||||
|
} |
||||||
|
if (timerCheckerButton.isSelected()) { |
||||||
|
SwitchForSwingChecker.startTimerChecker(); |
||||||
|
} else { |
||||||
|
SwitchForSwingChecker.stopTimerChecker(); |
||||||
|
} |
||||||
|
FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Basic_Common_Save_Successfully")); |
||||||
|
}); |
||||||
|
actionsPanel.add(confirmButton, BorderLayout.WEST); |
||||||
|
|
||||||
|
UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); |
||||||
|
cancelButton.addActionListener((e) -> { |
||||||
|
createBodyPanel(); |
||||||
|
remove(generalSettingPanel); |
||||||
|
add(body); |
||||||
|
setPreferredSize(body.getPreferredSize()); |
||||||
|
repaint(); |
||||||
|
setVisible(true); |
||||||
|
|
||||||
|
}); |
||||||
|
actionsPanel.add(cancelButton, BorderLayout.EAST); |
||||||
|
} |
||||||
|
tailPanel.add(actionsPanel, BorderLayout.EAST); |
||||||
|
return tailPanel; |
||||||
|
} |
||||||
|
|
||||||
|
private JPanel createInfoPanelInGeneralSettings() { |
||||||
|
JPanel infoPane = FRGUIPaneFactory.createNColumnGridInnerContainer_S_Pane(1); |
||||||
|
easyCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Operation_Time_Consuming_Detection")); |
||||||
|
timerCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Operation_Class_Capture")); |
||||||
|
easyCheckerButton.setSelected(SwitchForSwingChecker.isEasyChecker()); |
||||||
|
timerCheckerButton.setSelected(SwitchForSwingChecker.isCheckerTimerSwitch()); |
||||||
|
infoPane.add(GUICoreUtils.createFlowPane(easyCheckerButton, FlowLayout.LEFT)); |
||||||
|
infoPane.add(GUICoreUtils.createFlowPane(timerCheckerButton, FlowLayout.LEFT)); |
||||||
|
return infoPane; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void processWindowEvent(WindowEvent e) { |
||||||
|
super.processWindowEvent(e); |
||||||
|
if (e.getID() == WindowEvent.WINDOW_CLOSING) { |
||||||
|
EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); |
||||||
|
envDetectorDialog.setVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 上传和导出卡顿日志的可用化处理,如果没有选择日期就不可用 |
||||||
|
*/ |
||||||
|
public void setSwitches(boolean flag) { |
||||||
|
uploadButton.setEnabled(flag); |
||||||
|
exportLogLabel.setEnabled(flag); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
public class MonthlyCartonFile { |
||||||
|
private File currentMonthFile; |
||||||
|
private File lastMonthFile; |
||||||
|
private File nextMonthFile; |
||||||
|
public MonthlyCartonFile() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public File getCurrentMonthFile() { |
||||||
|
return currentMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCurrentMonthFile(File currentMonthFile) { |
||||||
|
this.currentMonthFile = currentMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public File getLastMonthFile() { |
||||||
|
return lastMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLastMonthFile(File lastMonthFile) { |
||||||
|
this.lastMonthFile = lastMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public File getNextMonthFile() { |
||||||
|
return nextMonthFile; |
||||||
|
} |
||||||
|
|
||||||
|
public void setNextMonthFile(File nextMonthFile) { |
||||||
|
this.nextMonthFile = nextMonthFile; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,240 @@ |
|||||||
|
package com.fr.design.carton; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.general.GeneralUtils; |
||||||
|
import com.fr.json.JSON; |
||||||
|
import com.fr.json.JSONArray; |
||||||
|
import com.fr.json.JSONObject; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.ProductConstantsBase; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import sun.awt.AppContext; |
||||||
|
|
||||||
|
import javax.swing.SwingWorker; |
||||||
|
import java.io.File; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.FileReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.text.SimpleDateFormat; |
||||||
|
import java.util.List; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.Calendar; |
||||||
|
|
||||||
|
public class SwitchForSwingChecker { |
||||||
|
/** |
||||||
|
* /定时任务的开关 |
||||||
|
*/ |
||||||
|
private static boolean checkerTimerSwitch = false; |
||||||
|
/** |
||||||
|
* /简单记录事件执行时间的开关 |
||||||
|
*/ |
||||||
|
private static boolean easyChecker = false; |
||||||
|
/** |
||||||
|
* 一个标识位用于区分耗时任务时长检测(简单检测)和timer检测 |
||||||
|
*/ |
||||||
|
public static final int TIMER_CHECK_FLAG = 0; |
||||||
|
public static final int EASY_CHECK_FLAG = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* /日志存储地址 |
||||||
|
*/ |
||||||
|
public static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); |
||||||
|
public static final String EASY_CHECKER_FILE_NAME = "easy_check_log.csv"; |
||||||
|
public static final String TIMER_CHECKER_FILE_NAME = "timer_check_log.csv"; |
||||||
|
public static boolean isCheckerTimerSwitch() { |
||||||
|
return checkerTimerSwitch; |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean isEasyChecker() { |
||||||
|
return easyChecker; |
||||||
|
} |
||||||
|
|
||||||
|
public static void startTimerChecker() { |
||||||
|
if (!checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.initTimer(); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer(); |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true); |
||||||
|
checkerTimerSwitch = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void stopTimerChecker() { |
||||||
|
if (checkerTimerSwitch) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.stopTimer(); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().stopTimer(); |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(false); |
||||||
|
checkerTimerSwitch = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void startEasyChecker() { |
||||||
|
if (!easyChecker) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(true); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true); |
||||||
|
easyChecker = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void stopEasyChecker() { |
||||||
|
if (easyChecker) { |
||||||
|
EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(false); |
||||||
|
CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(false); |
||||||
|
easyChecker = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取文件名字以及判断文件是否存在 |
||||||
|
*/ |
||||||
|
private static CartonFiles getFiles(String date) { |
||||||
|
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 file1 = new File(StableUtils.pathJoin(dirPath, EASY_CHECKER_FILE_NAME)); |
||||||
|
File file2 = new File(StableUtils.pathJoin(dirPath, TIMER_CHECKER_FILE_NAME)); |
||||||
|
File[] files = new File[2]; |
||||||
|
files[0] = file1; |
||||||
|
files[1] = file2; |
||||||
|
CartonFiles cartonFiles = new CartonFiles(); |
||||||
|
cartonFiles.setEasyCheckerFile(file1); |
||||||
|
cartonFiles.setTimerCheckerFile(file2); |
||||||
|
return cartonFiles; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
*处理文件 |
||||||
|
* 一共四种情况, |
||||||
|
* 两个文件都不存在 |
||||||
|
* 文件一存在,文件二不存在 |
||||||
|
* 文件二存在,文件一不存在 |
||||||
|
* 两个文件都存在 |
||||||
|
*/ |
||||||
|
private static List<CartonUploadMessage> getCartonLog(File easyFile, File timerFile) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
List<CartonUploadMessage> easyFileCartonLog = getEasyFileCartonLog(easyFile); |
||||||
|
List<CartonUploadMessage> timerFileCartonLog = getTimerFileCartonLog(timerFile); |
||||||
|
Map<String, CartonUploadMessage> easyFileMap = new HashMap<>(); |
||||||
|
for (CartonUploadMessage cartonUploadMessage : easyFileCartonLog) { |
||||||
|
easyFileMap.put(cartonUploadMessage.getHangCount(), cartonUploadMessage); |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
for (CartonUploadMessage cartonUploadMessage : timerFileCartonLog) { |
||||||
|
String hangCount = cartonUploadMessage.getHangCount(); |
||||||
|
if (easyFileMap.containsKey(hangCount)) { |
||||||
|
cartonUploadMessage.setThreadTime(easyFileMap.get(hangCount).getThreadTime()); |
||||||
|
} |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
private static List<CartonUploadMessage> getTimerFileCartonLog(File file) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
try { |
||||||
|
StringBuilder stringBuilder = new StringBuilder(); |
||||||
|
stringBuilder.append("["); |
||||||
|
BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); |
||||||
|
String line1; |
||||||
|
while ((line1 = bufferedReader1.readLine()) != null) { |
||||||
|
stringBuilder.append(line1); |
||||||
|
} |
||||||
|
bufferedReader1.close(); |
||||||
|
stringBuilder.append("]"); |
||||||
|
JSONArray easyCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); |
||||||
|
for (Object jsonObject : easyCheckerJSON) { |
||||||
|
CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); |
||||||
|
JSONObject x = (JSONObject) jsonObject; |
||||||
|
cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); |
||||||
|
cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time")) + "ms"); |
||||||
|
cartonUploadMessage.setThreadTime("undefined"); |
||||||
|
//这个跟输出到文件中的格式匹配,参考EventDis里的stackTraceToString方法
|
||||||
|
String indentation = " "; |
||||||
|
String logMessage = x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info")).replaceAll(indentation, "\r\n "); |
||||||
|
cartonUploadMessage.setInfo(logMessage); |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error("upload fail", e); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
private static List<CartonUploadMessage> getEasyFileCartonLog(File file) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
try { |
||||||
|
StringBuilder stringBuilder = new StringBuilder(); |
||||||
|
stringBuilder.append("["); |
||||||
|
BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); |
||||||
|
String line1; |
||||||
|
while ((line1 = bufferedReader1.readLine()) != null) { |
||||||
|
stringBuilder.append(line1); |
||||||
|
} |
||||||
|
bufferedReader1.close(); |
||||||
|
stringBuilder.append("]"); |
||||||
|
JSONArray timerCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); |
||||||
|
for (Object jsonObject : timerCheckerJSON) { |
||||||
|
JSONObject x = (JSONObject) jsonObject; |
||||||
|
CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); |
||||||
|
cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); |
||||||
|
cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"))); |
||||||
|
cartonUploadMessage.setThreadTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"))); |
||||||
|
cartonUploadMessage.setInfo("undefined"); |
||||||
|
res.add(cartonUploadMessage); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
FineLoggerFactory.getLogger().error("upload fail", e); |
||||||
|
} |
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* /埋点方法上传卡顿信息入口 |
||||||
|
date为 2022-09-08的格式 |
||||||
|
*/ |
||||||
|
public static List<CartonUploadMessage> uploadJournalLog(Date dateTime) { |
||||||
|
List<CartonUploadMessage> res = new ArrayList<>(); |
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||||
|
CartonFiles files = getFiles(simpleDateFormat.format(dateTime)); |
||||||
|
File easyCheckerFile = files.getEasyCheckerFile(); |
||||||
|
File timerCheckerFile = files.getTimerCheckerFile(); |
||||||
|
if (easyCheckerFile.exists() && timerCheckerFile.exists()) { |
||||||
|
return getCartonLog(easyCheckerFile, timerCheckerFile); |
||||||
|
} else if (easyCheckerFile.exists()) { |
||||||
|
return getEasyFileCartonLog(easyCheckerFile); |
||||||
|
} else if (timerCheckerFile.exists()) { |
||||||
|
return getTimerFileCartonLog(timerCheckerFile); |
||||||
|
} else { |
||||||
|
return res; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化监控任务,主要是替换EventQueue以及SwingWorker执行任务的线程池 |
||||||
|
*/ |
||||||
|
public static void initThreadMonitoring () { |
||||||
|
EventDispatchThreadHangMonitor.initMonitoring(); |
||||||
|
AppContext.getAppContext().put(SwingWorker.class, CartonThreadExecutorPool.getTimerThreadExecutorPool()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断是否有指定日期的卡顿日志,没有就返回false |
||||||
|
*/ |
||||||
|
public static boolean isCartonExists(Date date) { |
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
||||||
|
String format = simpleDateFormat.format(date); |
||||||
|
Calendar calendar = Calendar.getInstance(); |
||||||
|
int month = calendar.get(Calendar.MONTH) + 1; |
||||||
|
int year = calendar.get(Calendar.YEAR); |
||||||
|
File file = new File(StableUtils.pathJoin(JOURNAL_FILE_PATH, String.valueOf(year), "month-" + month, format)); |
||||||
|
return file.exists(); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean isCartonExists() { |
||||||
|
return isCartonExists(new Date()); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue