Destiny.Lin
3 months ago
17 changed files with 412 additions and 44 deletions
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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()); |
||||||
|
} |
||||||
|
} |
@ -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…
Reference in new issue