帆软报表设计器源代码。
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.
 
 
 
 

181 lines
6.5 KiB

package com.fr.design.carton.latency;
import com.fr.concurrent.NamedThreadFactory;
import com.fr.config.MarketConfig;
import com.fr.design.DesignerEnvManager;
import com.fr.design.carton.SwitchForSwingChecker;
import com.fr.design.mainframe.SiteCenterToken;
import com.fr.event.Event;
import com.fr.event.EventDispatcher;
import com.fr.event.Listener;
import com.fr.general.CloudCenter;
import com.fr.general.GeneralContext;
import com.fr.general.GeneralUtils;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.Workspace;
import com.fr.workspace.WorkspaceEvent;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static com.fr.design.carton.CartonConstants.APPID;
import static com.fr.design.carton.CartonConstants.DESIGNER_ID;
import static com.fr.design.carton.CartonConstants.DESIGNER_VERSION;
import static com.fr.design.carton.CartonConstants.DESIGN_METHOD;
import static com.fr.design.carton.CartonConstants.LOCAL;
import static com.fr.design.carton.CartonConstants.OPERANDS_NUM;
import static com.fr.design.carton.CartonConstants.REMOTE;
import static com.fr.design.carton.CartonConstants.TIME;
import static com.fr.design.carton.CartonConstants.USERID;
/**
* 设计器延迟时间记录Metric
*
* @author Levy.Xie
* @since 11.0
* Created on 2024/07/01
*/
public class DesignerLatencyMetric {
private String latencyUrl;
private ExecutorService executorService;
private ScheduledExecutorService scheduler;
private static final Map<LatencyLevel, AtomicInteger> LATENCY_CONTAINER = new ConcurrentHashMap<>();
private static final String DEFAULT_MONITOR_URL = "https://cloud.fanruan.com/api/monitor/";
private static final String LATENCY_TABLE_SUFFIX = "record_of_designer_latency/single";
private final static class InstanceHolder {
static final DesignerLatencyMetric INSTANCE = new DesignerLatencyMetric();
}
/**
* 获取单例
*/
public static DesignerLatencyMetric getInstance() {
return DesignerLatencyMetric.InstanceHolder.INSTANCE;
}
private DesignerLatencyMetric() {
}
/**
* 启动
*/
public void start() {
if (needMonitor()) {
// 初始化容器
initializeContainer();
// 启动异步性能记录线程池
executorService = Executors.newFixedThreadPool(8);
// 启动定时埋点
this.scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("LatencyMetricWorker"));
this.scheduler.scheduleWithFixedDelay(this::collectAndSubmit, 60, 60, TimeUnit.MINUTES);
// 注册设计器工作目录切换事件监听
EventDispatcher.listen(WorkspaceEvent.BeforeSwitch, new Listener<Workspace>() {
@Override
public void on(Event event, Workspace param) {
collectAndSubmit();
}
});
FineLoggerFactory.getLogger().info("[Latency] designer latency metric started.");
}
}
/**
* 关闭
*/
public void stop() {
if (needMonitor()) {
if (this.executorService != null) {
this.executorService.shutdown();
}
if (this.scheduler != null) {
this.scheduler.shutdown();
}
collectAndSubmit();
FineLoggerFactory.getLogger().info("[Latency] designer latency metric stopped.");
}
}
private static boolean needMonitor() {
// 海外版本不回传云中心
return SwitchForSwingChecker.isLatencyMonitoring() && Locale.CHINA.equals(GeneralContext.getLocale());
}
private String getLatencyUrl() {
if (StringUtils.isEmpty(latencyUrl)) {
String monitorEntry = CloudCenter.getInstance().acquireUrlByKind("cloud.monitor.api.entrypoint");
latencyUrl = (StringUtils.isNotEmpty(monitorEntry) ? monitorEntry : DEFAULT_MONITOR_URL)
+ LATENCY_TABLE_SUFFIX;
}
return latencyUrl;
}
private void initializeContainer() {
for (LatencyLevel level : LatencyLevel.values()) {
LATENCY_CONTAINER.put(level, new AtomicInteger());
}
}
private void resetContainer() {
LATENCY_CONTAINER.values().forEach(count -> count.set(0));
}
/**
* 记录性能信息
*/
public void record(long cost) {
executorService.submit(() -> {
try {
LatencyLevel level = LatencyLevel.measure(cost);
LATENCY_CONTAINER.computeIfAbsent(level, k -> new AtomicInteger()).incrementAndGet();
} catch (Throwable ignore) {
// 记录失败不影响业务
}
});
}
/**
* 汇总并提交性能监控埋点
*/
public void collectAndSubmit() {
Map<String, Object> para = new HashMap<>();
para.put("token", SiteCenterToken.generateToken());
para.put("content", collect());
try {
HttpToolbox.post(getLatencyUrl(), para);
FineLoggerFactory.getLogger().debug("[Latency] submit latency log to cloud.");
} catch (Throwable t) {
FineLoggerFactory.getLogger().debug(t,"[Latency] failed to submit latency log to cloud.");
}
resetContainer();
}
private JSONObject collect() {
JSONObject info = new JSONObject();
info.put(TIME, System.currentTimeMillis());
info.put(APPID, MarketConfig.getInstance().getCloudOperationMaintenanceId());
info.put(USERID, MarketConfig.getInstance().getBbsUid());
info.put(DESIGNER_ID, DesignerEnvManager.getEnvManager().getUUID());
info.put(DESIGNER_VERSION, GeneralUtils.getVersion());
info.put(DESIGN_METHOD, WorkContext.getCurrent().isLocal() ? LOCAL : REMOTE);
info.put(OPERANDS_NUM, LATENCY_CONTAINER.values().stream().mapToInt(AtomicInteger::get).sum());
for (Map.Entry<LatencyLevel, AtomicInteger> entry : LATENCY_CONTAINER.entrySet()) {
info.put(entry.getKey().getMark(), entry.getValue().get());
}
return info;
}
}