Browse Source

REPORT-130080 【FR-FBP】埋点方案梳理与调整

fbp/merge
Bruce.Deng 5 months ago
parent
commit
94006395f5
  1. 12
      designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java
  2. 214
      designer-base/src/main/java/com/fr/design/EnvSwitcherSubmitTask.java
  3. 39
      designer-base/src/main/java/com/fr/design/metric/AbstractDesignerMetric.java
  4. 19
      designer-realize/src/main/java/com/fanruan/boot/env/DesignEnvChooseComponent.java
  5. 3
      designer-realize/src/main/java/com/fr/start/CarinaDesigner.java
  6. 94
      designer-realize/src/main/java/com/fr/start/DesignerStartupMetric.java

12
designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java

@ -2,8 +2,6 @@ package com.fr.design;
import com.fr.common.report.ReportState;
import com.fr.design.backup.EnvBackupHelper;
import com.fr.design.mainframe.manager.clip.TemplateTreeClipboard;
import com.fr.design.plugin.remind.PluginErrorDesignReminder;
import com.fr.design.data.DesignTableDataManager;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.DialogActionAdapter;
@ -52,7 +50,6 @@ import com.fr.workspace.engine.base.FineObjectPool;
import com.fr.workspace.engine.channel.http.FunctionalHttpRequest;
import com.fr.workspace.engine.exception.WorkspaceConnectionException;
import com.fr.workspace.engine.rpc.WorkspaceProxyPool;
import static javax.swing.JOptionPane.QUESTION_MESSAGE;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
@ -68,9 +65,10 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import static javax.swing.JOptionPane.QUESTION_MESSAGE;
public class EnvChangeEntrance {
private static final String BRANCH_TAG = "#";
private static final String BRANCH_BEGIN = "-";
@ -158,11 +156,11 @@ public class EnvChangeEntrance {
}
/**
* 由云端运维触发切换埋点
* 内部空实现
* <a href="https://kms.fineres.com/pages/viewpage.action?pageId=945327503">实现</a>
* 异步提交埋点
*
*/
private void triggerSwitchMetric() {
EnvSwitcherSubmitTask.asyncSubmit();
}
/**

214
designer-base/src/main/java/com/fr/design/EnvSwitcherSubmitTask.java

@ -0,0 +1,214 @@
package com.fr.design;
import com.fr.concurrent.FineExecutors;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.design.mainframe.SiteCenterToken;
import com.fr.design.metric.AbstractDesignerMetric;
import com.fr.general.CloudCenter;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONArray;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.workspace.Workspace;
import com.fr.workspace.WorkspaceSwitchProcess;
import com.fr.workspace.WorkspaceSwitchStatics;
import com.fr.workspace.switcher.WorkspaceSwitchHistory;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
/**
* 设计器环境切换埋点提交
*
* @author Bruce.Deng
* @since 11.0
* Created on 2024/9/5
*/
public class EnvSwitcherSubmitTask extends AbstractDesignerMetric {
private static final String TAG_MODULE = "module";
private static final String TAG_SELF_USED = "selfUsed";
private static final String TAG_SUB = "sub";
private static final String WORK_DIRECTORY_TYPE_BEFORE_SWITCHING = "workDirectoryTypeBeforeSwitching";
private static final String WORK_DIRECTORY_TYPE_AFTER_SWITCHING = "workDirectoryTypeAfterSwitching";
private static final String SWITCH_COMPLETED = "switchCompleted";
private static final String EXCHANGE_TIME = "exchangeTime";
private static final String START_SLOW_MODULE_NAME = "startSlowModuleName";
private static final String STOP_SLOW_MODULE_NAME = "stopSlowModuleName";
private static final int MIN_LIMIT = 100;
private static final String SWITCH_TABLE_SUFFIX = "record_of_fbp_remoteSwitch/single";
private final ExecutorService service;
private EnvSwitcherSubmitTask() {
service = FineExecutors.newSingleThreadExecutor(new NamedThreadFactory("DesignEnvSwitchMetricSubmit"));
}
/**
* 异步提交环境切换的埋点
*/
public static void asyncSubmit() {
new EnvSwitcherSubmitTask().run();
}
/**
* 执行任务
*/
private void run() {
WorkspaceSwitchHistory.consume(workspaceSwitchProcesses -> {
Iterator<WorkspaceSwitchProcess> workspaceSwitchProcessIterator = workspaceSwitchProcesses.descendingIterator();
while (workspaceSwitchProcessIterator.hasNext()) {
WorkspaceSwitchProcess next = workspaceSwitchProcessIterator.next();
// 过滤掉 source 为空的启动过程
if (next != null && next.getSource() != null) {
submitProcess(next);
}
// 提交之后要移除掉
workspaceSwitchProcessIterator.remove();
}
});
}
private void submitProcess(WorkspaceSwitchProcess process) {
service.submit(new Runnable() {
@Override
public void run() {
collectAndSubmit(process);
}
});
}
private void collectAndSubmit(WorkspaceSwitchProcess process) {
Map<String, Object> para = new HashMap<>();
para.put("token", SiteCenterToken.generateToken());
para.put("content", collect(process));
try {
HttpToolbox.post(getUrl(), para);
FineLoggerFactory.getLogger().debug("[EnvSwitcher] submit env switcher metric to cloud.");
} catch (Throwable t) {
FineLoggerFactory.getLogger().debug(t,"[EnvSwitcher] failed to submit env switcher metric to cloud.");
}
}
private JSONObject collect(WorkspaceSwitchProcess process) {
JSONObject info = new JSONObject();
addDefaultMetric(info);
addMeta(info, process);
addStatics(info, process.getStatics());
return info;
}
private static void addMeta(JSONObject info, WorkspaceSwitchProcess process) {
Workspace source = process.getSource();
info.put(WORK_DIRECTORY_TYPE_BEFORE_SWITCHING, workspaceType(source));
Workspace target = process.getTarget();
info.put(WORK_DIRECTORY_TYPE_AFTER_SWITCHING, workspaceType(target));
info.put(SWITCH_COMPLETED, switchCompleted(process));
}
@NotNull
private static String switchCompleted(WorkspaceSwitchProcess process) {
return process.isSwitchSuccess() ? "yes" : "no";
}
@NotNull
private static String workspaceType(Workspace workspace) {
return workspace.isLocal() ? "local" : "remote";
}
private void addStatics(JSONObject info, WorkspaceSwitchStatics statics) {
info.put(EXCHANGE_TIME, statics.getElapsed());
JSONObject startSlowModuleName = convert2CloudModel(statics.getStartModuleUsed());
info.put(START_SLOW_MODULE_NAME, startSlowModuleName);
JSONObject stopSlowModuleName = convert2CloudModel(statics.getStopModuleUsed());
info.put(STOP_SLOW_MODULE_NAME, stopSlowModuleName);
}
/* convert */
@NotNull
private JSONObject convert2CloudModel(JSONObject moduleUsedJO) {
TreeSet<Entry> entries = convert2SortSet(moduleUsedJO);
return convert2JO(entries);
}
@NotNull
private static JSONObject convert2JO(TreeSet<Entry> startEntries) {
JSONObject moduleNames = new JSONObject();
startEntries.stream()
.filter((e) -> e.getSelfUsed() > MIN_LIMIT)
.forEach((e) -> moduleNames.put(e.getModuleName(), e.getSelfUsed()));
return moduleNames;
}
@NotNull
private TreeSet<Entry> convert2SortSet(JSONObject moduleUsedJO) {
TreeSet<Entry> entries = new TreeSet<>(Comparator.comparingInt(Entry::getSelfUsed));
sortBySelfUsed(moduleUsedJO, entries);
return entries;
}
/**
* 循环处理 JSON, 并降序排序
*
* @param moduleUsed 模块用时 {@link com.fr.module.engine.FineModule}
* @param entries 降序排序的树集合
*/
private void sortBySelfUsed(JSONObject moduleUsed, TreeSet<Entry> entries) {
if (moduleUsed == null || moduleUsed.isEmpty()) {
return;
}
String moduleName = moduleUsed.optString(TAG_MODULE);
int selfUsed = moduleUsed.optInt(TAG_SELF_USED);
entries.add(new Entry(moduleName, selfUsed));
JSONArray subModules = moduleUsed.optJSONArray(TAG_SUB);
if (subModules != null) {
int length = subModules.length();
for (int i = 0; i < length; i++) {
JSONObject subModuleUsed = subModules.optJSONObject(i);
sortBySelfUsed(subModuleUsed, entries);
}
}
}
private String getUrl() {
String monitorEntry = CloudCenter.getInstance().acquireUrlByKind("cloud.monitor.api.entrypoint");
String url = (StringUtils.isNotEmpty(monitorEntry) ? monitorEntry : DEFAULT_MONITOR_URL)
+ SWITCH_TABLE_SUFFIX;
return url;
}
private static class Entry {
private final String moduleName;
private final Integer selfUsed;
public Entry(String moduleName, Integer selfUsed) {
this.moduleName = moduleName;
this.selfUsed = selfUsed;
}
public String getModuleName() {
return moduleName;
}
public Integer getSelfUsed() {
return selfUsed;
}
}
}

39
designer-base/src/main/java/com/fr/design/metric/AbstractDesignerMetric.java

@ -0,0 +1,39 @@
package com.fr.design.metric;
import com.fanruan.carina.Carina;
import com.fanruan.config.bbs.FineBBSConfigProvider;
import com.fr.config.MarketConfig;
import com.fr.design.DesignerEnvManager;
import com.fr.general.GeneralUtils;
import com.fr.json.JSONObject;
import org.jetbrains.annotations.NotNull;
/**
* 设计器埋点抽象
*
* @author Bruce.Deng
* @since 11.0
* Created on 2024/9/10
*/
public abstract class AbstractDesignerMetric {
public static final String TIME = "time";
public static final String DESIGNER_ID = "designerId";
public static final String DESIGNER_VERSION = "designerVersion";
public static final String USERID = "userId";
public static final String APPID = "appId";
public static final String DEFAULT_MONITOR_URL = "https://cloud.fanruan.com/api/monitor/";
protected void addDefaultMetric(@NotNull JSONObject info) {
info.put(TIME, System.currentTimeMillis());
info.put(DESIGNER_ID, DesignerEnvManager.getEnvManager().getUUID());
info.put(DESIGNER_VERSION, GeneralUtils.getVersion());
info.put(USERID, Carina.config(FineBBSConfigProvider.class).getBbsUid());
info.put(APPID, MarketConfig.getInstance().getCloudOperationMaintenanceId());
}
}

19
designer-realize/src/main/java/com/fanruan/boot/env/DesignEnvChooseComponent.java vendored

@ -1,8 +1,6 @@
package com.fanruan.boot.env;
import com.fr.design.ConfigHelper;
import com.fanruan.boot.key.StartupArgsShell;
import com.fr.design.mem.MemConfigRepositoryBuilder;
import com.fanruan.carina.Carina;
import com.fanruan.carina.annotions.FineComponent;
import com.fanruan.carina.annotions.Start;
@ -15,15 +13,18 @@ import com.fanruan.config.realm.local.LocalConfigRepositoryBuilder;
import com.fr.base.operator.org.OrganizationOperator;
import com.fr.base.rpc.encrypt.EncryptOperator;
import com.fr.decision.service.context.ServiceContext;
import com.fr.design.ConfigHelper;
import com.fr.design.DesignerEnvManager;
import com.fr.design.EnvChangeEntrance;
import com.fr.design.PluginClassRefreshManager;
import com.fr.design.backup.DesignContext;
import com.fr.design.constants.DesignerLaunchStatus;
import com.fr.design.editlock.ConnectionLockChangeChecker;
import com.fr.design.editlock.ServerTableDataLockChangeChecker;
import com.fr.design.env.DesignerWorkspaceGenerator;
import com.fr.design.env.DesignerWorkspaceInfo;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.mem.MemConfigRepositoryBuilder;
import com.fr.design.plugin.remind.PluginErrorDesignReminder;
import com.fr.env.utils.WorkspaceUtils;
import com.fr.event.Event;
@ -36,12 +37,12 @@ import com.fr.report.lock.DefaultLockInfoOperator;
import com.fr.report.lock.LocalLockInfoOperator;
import com.fr.report.lock.LockInfoOperator;
import com.fr.report.lock.ServerLockInfoOperator;
import com.fr.design.backup.DesignContext;
import com.fr.start.module.StartupArgs;
import com.fr.value.NotNullLazyValue;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.WorkspaceEvent;
import com.fr.workspace.WorkspaceSwitchProcess;
import com.fr.workspace.base.WorkspaceKey;
import com.fr.workspace.pool.WorkRPCRegister;
import com.fr.workspace.pool.WorkRPCType;
@ -101,6 +102,8 @@ import com.fr.workspace.server.vcs.v2.scheduler.VcsAutoCleanOperator;
import com.fr.workspace.server.vcs.v2.scheduler.VcsAutoCleanService;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
/**
* 环境选择模块
*
@ -118,10 +121,20 @@ public class DesignEnvChooseComponent extends ResourceAffiliate {
BootstrapFactory.get().reboot("design_env_prepare");
// 环境切换后,等到模块重启更新一下当前的系统信息
WorkplaceConstants.updateBean();
recordModuleStartStop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void recordModuleStartStop() {
WorkspaceSwitchProcess process = WorkContext.getSwitcher().getProcess();
Optional.ofNullable(process)
.ifPresent((e) -> e.recordModuleStartUsed(() -> BootstrapFactory.get().profileStart("design_env_prepare")));
Optional.ofNullable(process)
.ifPresent((e) -> e.recordModuleStopUsed(() -> BootstrapFactory.get().profileStop("design_env_prepare")));
}
};
private Listener<Workspace> beforeSwitch4Max = new Listener<Workspace>(Integer.MAX_VALUE) {

3
designer-realize/src/main/java/com/fr/start/CarinaDesigner.java

@ -10,11 +10,9 @@ import com.fanruan.gui.UiInspector;
import com.fr.base.StateHubContext;
import com.fr.design.backup.DesignContext;
import com.fr.design.carton.SwitchForSwingChecker;
import com.fr.design.carton.latency.DesignerLatencyMetric;
import com.fr.design.mainframe.DesignerUIModeConfig;
import com.fr.log.FineLoggerFactory;
import com.fr.runtime.FineRuntime;
import com.fr.start.common.DesignerStartupContext;
import com.fr.start.module.StartupArgs;
@ -65,6 +63,7 @@ public class CarinaDesigner extends MainDesigner{
FineLoggerFactory.getLogger().info("Designer started.Time used {} ms", DesignerStartupContext.getRecorder().getTime(TimeUnit.MILLISECONDS));
DesignerStartupContext.getRecorder().stop();
DesignerStartupMetric.getInstance().asyncSubmit();
SwitchForSwingChecker.initThreadMonitoring();
DesignerLatencyMetric.getInstance().start();
}

94
designer-realize/src/main/java/com/fr/start/DesignerStartupMetric.java

@ -0,0 +1,94 @@
package com.fr.start;
import com.fr.concurrent.FineExecutors;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.design.mainframe.SiteCenterToken;
import com.fr.design.metric.AbstractDesignerMetric;
import com.fr.general.CloudCenter;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONObject;
import com.fr.json.revise.EmbedJson;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.start.common.DesignerStartupContext;
import com.fr.startup.metric.DesignerMetrics;
import com.fr.startup.metric.DesignerStartupModel;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* fbp设计器启动埋点提交
*
* @author Bruce.Deng
* @since 11.0
* Created on 2024/9/5
*/
public class DesignerStartupMetric extends AbstractDesignerMetric {
private static final String LANDING_TIME = "landingTime";
private static final String STARTING_TIME = "startingTime";
private static final String MODE = "mode";
private static final String INFO = "info";
private static volatile DesignerStartupMetric instance = new DesignerStartupMetric();
private static final String DESIGNER_START_TABLE_SUFFIX = "record_of_fbp_designerStartTime/single";
private DesignerStartupMetric() {
}
/**
* 获取单例
*/
public static DesignerStartupMetric getInstance() {
return instance;
}
/**
* 延迟5分钟提交埋点数据
*
*/
public void asyncSubmit() {
ScheduledExecutorService scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor(new NamedThreadFactory("DesignerStartupMetricSubmit"));
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
collectAndSubmit();
}
}, 5, TimeUnit.MINUTES);
scheduledExecutorService.shutdown();
}
private void collectAndSubmit() {
Map<String, Object> para = new HashMap<>();
para.put("token", SiteCenterToken.generateToken());
para.put("content", collect());
try {
HttpToolbox.post(getUrl(), para);
FineLoggerFactory.getLogger().debug("[DesignerStartup] submit designer startup metric to cloud.");
} catch (Throwable t) {
FineLoggerFactory.getLogger().debug(t,"[DesignerStartup] failed to submit designer startup metric to cloud.");
}
}
private String getUrl() {
String monitorEntry = CloudCenter.getInstance().acquireUrlByKind("cloud.monitor.api.entrypoint");
String url = (StringUtils.isNotEmpty(monitorEntry) ? monitorEntry : DEFAULT_MONITOR_URL)
+ DESIGNER_START_TABLE_SUFFIX;
return url;
}
private JSONObject collect() {
JSONObject info = new JSONObject();
DesignerMetrics designerMetrics = DesignerStartupContext.getInstance().getDesignerMetrics();
DesignerStartupModel model = designerMetrics.getModel();
info.put(LANDING_TIME, model.getLandingTime());
info.put(STARTING_TIME, model.getStartingTime());
info.put(MODE, model.getMode());
info.put(INFO, EmbedJson.encode(model.getInfo()));
addDefaultMetric(info);
return info;
}
}
Loading…
Cancel
Save