Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions packages/connection/src/common/rpc-service/center.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Deferred, DisposableStore, IDisposable, randomString } from '@opensumi/ide-core-common';
import {
Deferred,
DisposableStore,
IDisposable,
IReporterService,
IReporterTimer,
REPORT_NAME,
randomString,
} from '@opensumi/ide-core-common';
import { addElement } from '@opensumi/ide-utils/lib/arrays';

import { METHOD_NOT_REGISTERED } from '../constants';
Expand All @@ -12,6 +20,8 @@ import { ProtocolRegistry, ServiceRegistry } from './registry';

import type { MessageConnection } from '@opensumi/vscode-jsonrpc';

const kDefaultMinimumReportThresholdTime = 200;

export class RPCServiceCenter implements IDisposable {
private _disposables = new DisposableStore();

Expand All @@ -30,6 +40,16 @@ export class RPCServiceCenter implements IDisposable {
this.logger = logger || console;
}

private _reporterService: IReporterService | undefined;
private _reportThreshold: number = kDefaultMinimumReportThresholdTime;
setReporter(
reporterService: IReporterService,
minimumReportThresholdTime: number = kDefaultMinimumReportThresholdTime,
) {
this._reporterService = reporterService;
this._reportThreshold = minimumReportThresholdTime;
}

registerService(serviceName: string, type: ServiceType): void {
if (type === ServiceType.Service) {
if (this.bench) {
Expand Down Expand Up @@ -98,8 +118,13 @@ export class RPCServiceCenter implements IDisposable {

async broadcast(serviceName: string, _name: string, ...args: any[]): Promise<any> {
await this.ready();

const name = getMethodName(serviceName, _name);

let timer: IReporterTimer | undefined;
if (this._reporterService) {
timer = this._reporterService.time(REPORT_NAME.RPC_TIMMING_MEASURE);
}

const broadcastResult = await Promise.all(this.proxies.map((proxy) => proxy.invoke(name, ...args)));

const doubtfulResult = [] as any[];
Expand All @@ -117,9 +142,33 @@ export class RPCServiceCenter implements IDisposable {
}

if (result.length === 0) {
if (timer) {
timer.timeEnd(
name,
{
success: false,
},
{
minimumReportThresholdTime: this._reportThreshold,
},
);
}

throw new Error(`broadcast rpc \`${name}\` error: no remote service can handle this call`);
}

if (timer) {
timer.timeEnd(
name,
{
success: true,
},
{
minimumReportThresholdTime: this._reportThreshold,
},
);
}

// FIXME: this is an unreasonable design, if remote service only returned doubtful result, we will return an empty array.
// but actually we should throw an error to tell user that no remote service can handle this call.
// or just return `undefined`.
Expand Down
7 changes: 7 additions & 0 deletions packages/core-browser/src/bootstrap/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { BackService } from '@opensumi/ide-core-common/lib/module';

import { ClientAppStateService } from '../application';
import { AppConfig } from '../react-providers/config-provider';

import { ModuleConstructor } from './app.interface';

Expand All @@ -25,6 +26,7 @@ export async function createConnectionService(
channelHandler: WSChannelHandler,
options: ISumiConnectionOptions = {},
) {
const appConfig = injector.get(AppConfig) as AppConfig;
const reporterService: IReporterService = injector.get(IReporterService);
channelHandler.setReporter(reporterService);

Expand Down Expand Up @@ -69,6 +71,11 @@ export async function createConnectionService(

const clientCenter = new RPCServiceCenter();
clientCenter.setSumiConnection(channel.createSumiConnection(options));

if (appConfig?.measure?.connection) {
clientCenter.setReporter(reporterService, appConfig.measure.connection.minimumReportThresholdTime);
}

initConnectionService(injector, modules, clientCenter);

return channel;
Expand Down
20 changes: 19 additions & 1 deletion packages/core-browser/src/react-providers/config-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const AppConfig = Symbol('AppConfig');
export interface AppConfig {
/**
* APP的名称
* 默认值为 `ClientApp.DEFAULT_APPLICATION_NAME` 即 `OPENSUMI`
* 默认值为 `ClientApp.DEFAULT_APPLICATION_NAME` 即 `OpenSumi`
*/
appName?: string;
/**
Expand Down Expand Up @@ -284,6 +284,10 @@ export interface AppConfig {
* 支持的通信协议类型
*/
connectionProtocols?: string[];
/**
* 埋点上报的配置
*/
measure?: IMeasureConfig;
/**
* 是否启用 Diff 协议文件自动恢复
*/
Expand All @@ -294,6 +298,20 @@ export interface ICollaborationClientOpts {
port?: number;
}

export interface IMeasureConfig {
/**
* 是否开启连接性能监控
*/
connection?: IConnectionMeasureConfig;
}

export interface IConnectionMeasureConfig {
/**
* 最低上报阈值时间,单位 ms
*/
minimumReportThresholdTime?: number;
}

export const ConfigContext = React.createContext<AppConfig>({
workspaceDir: '',
injector: null as any,
Expand Down
15 changes: 13 additions & 2 deletions packages/core-common/src/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IReporter,
IReporterService,
IReporterTimer,
IReporterTimerEndOptions,
PerformanceData,
PointData,
REPORT_NAME,
Expand All @@ -18,8 +19,18 @@ class ReporterTimer implements IReporterTimer {
this.now = Date.now();
}

timeEnd(msg?: string, extra?: any) {
const duration = Date.now() - this.now;
getElapsedTime() {
return Date.now() - this.now;
}

timeEnd(msg?: string, extra?: any, options?: IReporterTimerEndOptions) {
const duration = this.getElapsedTime();

if (options?.minimumReportThresholdTime && duration < options.minimumReportThresholdTime) {
// 不满足最小时间要求,不上报
return duration;
}

this.reporter.performance(this.name, {
duration,
metadata: this.metadata,
Expand Down
11 changes: 10 additions & 1 deletion packages/core-common/src/types/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export enum REPORT_NAME {
TERMINAL_MEASURE = 'terminalMeasure',
SEARCH_MEASURE = 'searchMeasure',
QUICK_OPEN_MEASURE = 'quickOpenMeasure',
RPC_TIMMING_MEASURE = 'rpcTimingMeasure',
}

export enum REPORT_HOST {
Expand Down Expand Up @@ -71,8 +72,16 @@ export interface PerformanceData extends PointData {

export const IReporterService = Symbol('IReporterService');

export interface IReporterTimerEndOptions {
/**
* 上报的最小时间阈值,单位毫秒
* 经过的时间要大于这个值才会上报
*/
minimumReportThresholdTime?: number;
}

export interface IReporterTimer {
timeEnd(msg?: string, extra?: any): number;
timeEnd(msg?: string, extra?: any, options?: IReporterTimerEndOptions): number;
}

export interface IReporterService {
Expand Down
5 changes: 5 additions & 0 deletions packages/startup/entry/web/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ renderApp(
modules: [DESIGN_MENUBAR_CONTAINER_VIEW_ID],
},
},
measure: {
connection: {
minimumReportThresholdTime: 400,
},
},
},
}),
);
2 changes: 1 addition & 1 deletion packages/startup/entry/web/render-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export async function renderApp(opts: IClientAppOpts) {
// eslint-disable-next-line no-console
console.timeEnd('Render');
};
registerLocalStorageProvider(GeneralSettingsId.Theme, opts.workspaceDir || '', 'prefix1');
registerLocalStorageProvider(GeneralSettingsId.Theme, opts.workspaceDir || '', 'sumi-dev');

const app = new ClientApp(opts);

Expand Down
4 changes: 2 additions & 2 deletions packages/utils/src/functional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ export function diffSets<T>(before: Set<T>, after: Set<T>): { removed: T[]; adde
}

export function findFirstTruthy<T>(...sources: Array<T | (() => T)>): T | undefined {
for (let i = 0; i < sources.length - 1; i++) {
for (let i = 0; i <= sources.length - 1; i++) {
const result = check(sources[i]);
if (result) {
return result;
}
}

return check(sources[sources.length - 1]);
return undefined;

function check(value: T | (() => T)): T | undefined {
if (typeof value === 'function') {
Expand Down