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.
274 lines
10 KiB
274 lines
10 KiB
package com.fr.design.debug.ui; |
|
|
|
import com.fine.swing.ui.layout.Row; |
|
import com.fine.theme.icon.LazyIcon; |
|
import com.fine.theme.utils.FineClientProperties; |
|
import com.fine.theme.utils.FineUIUtils; |
|
import com.formdev.flatlaf.util.ScaledEmptyBorder; |
|
import com.fr.base.extension.FileExtension; |
|
import com.fr.design.border.FineBorderFactory; |
|
import com.fr.design.carton.latency.LatencyLevel; |
|
import com.fr.design.dialog.BasicDialog; |
|
import com.fr.design.dialog.BasicPane; |
|
import com.fr.design.gui.ibutton.UIButton; |
|
import com.fr.design.gui.icheckbox.UICheckBox; |
|
import com.fr.design.gui.icombobox.UIComboBox; |
|
import com.fr.design.gui.icontainer.UIScrollPane; |
|
import com.fr.design.gui.icontainer.UITableScrollPane; |
|
import com.fr.design.gui.ilable.UILabel; |
|
import com.fr.design.gui.itable.FineUITable; |
|
import com.fr.design.gui.itextarea.UITextArea; |
|
import com.fr.design.gui.itoolbar.UIToolbar; |
|
import com.fr.design.mainframe.DesignerContext; |
|
import com.fr.event.Event; |
|
import com.fr.event.EventDispatcher; |
|
import com.fr.event.Listener; |
|
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 org.jetbrains.annotations.NotNull; |
|
|
|
import javax.swing.JPanel; |
|
import javax.swing.JTable; |
|
import javax.swing.JToolBar; |
|
import javax.swing.SwingUtilities; |
|
import javax.swing.table.DefaultTableModel; |
|
import javax.swing.table.TableColumn; |
|
import javax.swing.table.TableColumnModel; |
|
import java.awt.BorderLayout; |
|
import java.awt.Point; |
|
import java.awt.event.MouseAdapter; |
|
import java.awt.event.MouseEvent; |
|
import java.io.BufferedWriter; |
|
import java.io.FileWriter; |
|
import java.util.Arrays; |
|
import java.util.Date; |
|
|
|
import static com.fine.swing.ui.layout.Layouts.cell; |
|
import static com.fine.swing.ui.layout.Layouts.column; |
|
import static com.fine.swing.ui.layout.Layouts.row; |
|
|
|
/** |
|
* UI监控面板 |
|
* |
|
* @author Levy.Xie |
|
* @since 11.0 |
|
* Created on 2024/11/07 |
|
*/ |
|
public class UIMonitorPane extends JPanel { |
|
|
|
private DefaultTableModel model; |
|
private UICheckBox inspector; |
|
private UICheckBox monitor; |
|
|
|
public UIMonitorPane() { |
|
setLayout(new BorderLayout()); |
|
setBorder(new ScaledEmptyBorder(10, 10, 10, 10)); |
|
initComponent(); |
|
} |
|
|
|
private void initComponent() { |
|
UITableScrollPane tablePane = initLatencyTable(); |
|
Row topSettingRow = initTopSettingRow(); |
|
inspector = new UICheckBox("Open UI Inspector"); |
|
monitor = new UICheckBox("Open Latency Monitor"); |
|
|
|
JPanel monitorPane = column(10, |
|
cell(monitor), cell(topSettingRow), cell(tablePane).weight(1) |
|
).getComponent(); |
|
|
|
add(column(10, |
|
cell(FineUIUtils.wrapComponentWithTitle(inspector, "UI Inspector")), |
|
cell(FineUIUtils.wrapComponentWithTitle(monitorPane, "UI Latency Monitor")) |
|
).getComponent(), BorderLayout.CENTER); |
|
|
|
topSettingRow.setVisible(false); |
|
tablePane.setVisible(false); |
|
|
|
initMonitorStatus(topSettingRow, tablePane); |
|
} |
|
|
|
private void initMonitorStatus(Row topSettingRow, UITableScrollPane tablePane) { |
|
inspector.setSelected(UIInspectorHolder.getInstance().isInstalled()); |
|
monitor.setSelected(UILatencyWorker.getInstance().isMonitoring()); |
|
// 注册事件监听 |
|
inspector.addChangeListener(e -> { |
|
if (inspector.isSelected()) { |
|
UIInspectorHolder.getInstance().install(); |
|
} else { |
|
UIInspectorHolder.getInstance().uninstall(); |
|
} |
|
}); |
|
monitor.addChangeListener(e -> { |
|
topSettingRow.setVisible(monitor.isSelected()); |
|
tablePane.setVisible(monitor.isSelected()); |
|
if (monitor.isSelected()) { |
|
startMonitor(); |
|
} else { |
|
stopMonitor(); |
|
} |
|
}); |
|
// 初始化卡顿堆栈表 |
|
if (monitor.isSelected()) { |
|
SwingUtilities.invokeLater(() -> UILatencyWorker.getInstance().getAllLatencyInfo() |
|
.forEach(info -> model.addRow(parseInfo2Row(info)))); |
|
|
|
} |
|
} |
|
|
|
private Row initTopSettingRow() { |
|
UIComboBox comboBox = initThresholdComboBox(); |
|
UIButton export = new UIButton(new LazyIcon("export")); |
|
export.setToolTipText("Export latency log"); |
|
UIButton clear = new UIButton(new LazyIcon("remove")); |
|
clear.setToolTipText("Clear latency log"); |
|
|
|
JToolBar toolbar = new UIToolbar(); |
|
toolbar.add(comboBox); |
|
toolbar.add(clear); |
|
toolbar.add(export); |
|
|
|
export.addActionListener(e -> exportData()); |
|
clear.addActionListener(e -> { |
|
model.setRowCount(0); |
|
UILatencyWorker.getInstance().clearData(); |
|
}); |
|
|
|
return row(5, cell(new UILabel("Latency Threshold")), cell(toolbar)).getComponent(); |
|
} |
|
|
|
private static @NotNull UIComboBox initThresholdComboBox() { |
|
UIComboBox comboBox = new UIComboBox(Arrays.stream(LatencyLevel.values()) |
|
.filter(it -> it != LatencyLevel.FLASH).map(LatencyLevel::getStart).toArray()); |
|
comboBox.putClientProperty(FineClientProperties.COMBO_BOX_TYPE, FineClientProperties.ADAPTIVE_COMBO_BOX); |
|
comboBox.setSelectedItem(UILatencyInfoHandler.getInstance().getThreshold()); |
|
comboBox.addActionListener(e -> { |
|
if (comboBox.getSelectedItem() != null) { |
|
UILatencyWorker.getInstance().resetThreshold((Long) comboBox.getSelectedItem()); |
|
} |
|
}); |
|
// 阈值初始化 |
|
comboBox.setSelectedItem(comboBox.getSelectedItem()); |
|
return comboBox; |
|
} |
|
|
|
private UITableScrollPane initLatencyTable() { |
|
model = new DefaultTableModel(); |
|
model.addColumn("seq"); |
|
model.addColumn("cost(ms)"); |
|
model.addColumn("stack"); |
|
FineUITable table = new FineUITable(model) { |
|
public boolean isCellEditable(int row, int column) { |
|
return false; |
|
} |
|
}; |
|
UITableScrollPane tablePane = new UITableScrollPane(table); |
|
table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); |
|
table.addMouseListener(new MouseAdapter() { |
|
@Override |
|
public void mouseClicked(MouseEvent e) { |
|
int row = table.rowAtPoint(e.getPoint()); |
|
if (row >= 0) { |
|
String stack = (String) table.getValueAt(row, 2); |
|
StackPane stackPane = new StackPane(stack); |
|
BasicDialog dialog = stackPane.showLargeWindow(SwingUtilities.getWindowAncestor(e.getComponent()), null); |
|
dialog.setAlwaysOnTop(true); |
|
dialog.setVisible(true); |
|
} |
|
} |
|
}); |
|
TableColumnModel columnModel = table.getColumnModel(); |
|
adjustColumnWidth(columnModel.getColumn(0)); |
|
adjustColumnWidth(columnModel.getColumn(1)); |
|
return tablePane; |
|
} |
|
|
|
private void adjustColumnWidth(TableColumn column) { |
|
column.setPreferredWidth(100); |
|
column.setMinWidth(100); |
|
column.setMaxWidth(100); |
|
} |
|
|
|
/** |
|
* 开启性能监控 |
|
*/ |
|
public void startMonitor() { |
|
EventDispatcher.listen(LatencyMonitorEvent.OFF_THRESHOLD_EVENT, latencyInfoListener); |
|
UILatencyWorker.getInstance().start(); |
|
} |
|
|
|
/** |
|
* 关闭性能监控 |
|
*/ |
|
public void stopMonitor() { |
|
UILatencyWorker.getInstance().stop(); |
|
EventDispatcher.stopListen(latencyInfoListener); |
|
model.setRowCount(0); |
|
} |
|
|
|
private void exportData() { |
|
// 导出为txt文件 |
|
FILEChooserPane fileChooserPane = FILEChooserPane.getMultiEnvInstance(true, false); |
|
String fileName = "latency_log_" + GeneralUtils.objectToString(new Date()).replaceAll(":", "_"); |
|
fileChooserPane.setFileNameTextField(fileName, ".txt"); |
|
fileChooserPane.addChooseFILEFilter(new ChooseFileFilter(FileExtension.TXT)); |
|
int saveValue = fileChooserPane.showSaveDialog(DesignerContext.getDesignerFrame()); |
|
if (saveValue == FILEChooserPane.JOPTIONPANE_OK_OPTION || saveValue == FILEChooserPane.OK_OPTION) { |
|
FILE target = fileChooserPane.getSelectedFILE(); |
|
try { |
|
target.mkfile(); |
|
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(target.getPath(), true))) { |
|
bufferedWriter.write(UILatencyWorker.getInstance().getLatencyData()); |
|
} |
|
} catch (Exception exp) { |
|
FineLoggerFactory.getLogger().error("[Latency] Error export latency log.", exp); |
|
} |
|
} |
|
} |
|
|
|
private final Listener<LatencyInfo> latencyInfoListener = new Listener<LatencyInfo>() { |
|
@Override |
|
public void on(Event event, LatencyInfo latencyInfo) { |
|
SwingUtilities.invokeLater(() -> { |
|
// 存量卡顿堆栈信息更新 |
|
for (int i = 0; i < model.getRowCount(); i++) { |
|
if (latencyInfo.getSeq() == (Long) model.getValueAt(i, 0)) { |
|
model.removeRow(i); |
|
break; |
|
} |
|
} |
|
model.addRow(parseInfo2Row(latencyInfo)); |
|
}); |
|
} |
|
}; |
|
|
|
private Object[] parseInfo2Row(LatencyInfo latencyInfo) { |
|
return new Object[]{ |
|
latencyInfo.getSeq(), |
|
latencyInfo.getCost(), |
|
UIMonitorHelper.convertStack(latencyInfo.getDetailStack())}; |
|
} |
|
|
|
static class StackPane extends BasicPane { |
|
|
|
public StackPane(String stack) { |
|
setLayout(new BorderLayout()); |
|
UITextArea textArea = new UITextArea(); |
|
textArea.setBorder(null); |
|
textArea.setEditable(false); |
|
textArea.setText(stack); |
|
UIScrollPane scrollPane = new UIScrollPane(textArea); |
|
scrollPane.setBorder(FineBorderFactory.createWrappedRoundBorder()); |
|
add(scrollPane); |
|
SwingUtilities.invokeLater(() -> scrollPane.getViewport().setViewPosition(new Point(0, 0))); |
|
} |
|
|
|
@Override |
|
protected String title4PopupWindow() { |
|
return "Latency Stack"; |
|
} |
|
} |
|
|
|
}
|
|
|