feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type TracePointName = 'success' | 'fail' | string;
export interface TraceDuration {
points: TracePointName[];
interval: {
[key: TracePointName]: number;
};
}
export function genDurationTracer() {
const duration: TraceDuration = {
points: [],
interval: {},
};
const tracer = (pointName: TracePointName) => {
if (!pointName) {
return duration;
}
if (duration.points.indexOf(pointName) === -1) {
duration.points.push(pointName);
}
performance.mark(pointName);
if (duration.points.length > 1) {
const curIdx = duration.points.length - 1;
const measure = performance.measure(
'measure',
duration.points[curIdx - 1],
duration.points[curIdx],
);
duration.interval[pointName] = measure?.duration ?? 0;
}
return duration;
};
return {
tracer,
};
}

View File

@@ -0,0 +1,320 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type CommonLogOptions, LogAction, LogLevel } from '../types';
import { SlardarReportClient, type SlardarInstance } from '../slardar';
import { Logger } from '../logger';
import { genDurationTracer, type TracePointName } from './duration-tracer';
export interface LoggerCommonProperties {
namespace?: string;
scope?: string;
}
export interface SlardarMeta {
meta?: Record<string, unknown>; // Combination of `categories` and `metrics`, check more:
}
export interface CustomLog extends SlardarMeta, LoggerCommonProperties {
message: string;
}
export type CustomErrorLog = CustomLog & { error: Error };
export interface CustomEvent<EventEnum extends string>
extends SlardarMeta,
LoggerCommonProperties {
eventName: EventEnum;
}
export interface ErrorEvent<EventEnum extends string>
extends CustomEvent<EventEnum> {
error: Error;
level?: 'error' | 'fatal';
}
export interface TraceEvent<EventEnum extends string>
extends LoggerCommonProperties {
eventName: EventEnum;
}
export interface TraceOptions extends SlardarMeta {
error?: Error;
}
type ReporterConfig = LoggerCommonProperties & SlardarMeta;
type LogType = 'info' | 'success' | 'warning' | 'error';
export class Reporter {
private initialized = false;
private logger: Logger;
private pendingQueue: CommonLogOptions[] = [];
private pendingInstance: Reporter[] = [];
public slardarInstance: SlardarInstance | null = null;
private log(type: LogType, payload: CommonLogOptions) {
if (!this.check(payload)) {
return;
}
this.logger.disableConsole = true;
this.logger[type](payload as CommonLogOptions & { error: Error });
this.logger.persist.disableConsole = true;
this.logger.persist[type](payload as CommonLogOptions & { error: Error });
}
constructor(config?: ReporterConfig) {
this.logger = new Logger({
clients: [],
ctx: {
...config,
},
});
}
/**
* 创建一个带有preset的reporter一般可以配置专属的`namespace`和`scope`
* @param preset
* @returns
*/
createReporterWithPreset(preset: ReporterConfig) {
const r = new Reporter(preset);
if (this.initialized) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
r.init(this.slardarInstance!);
} else {
this.pendingInstance.push(r);
}
return r;
}
/**
* 初始化reporter
* @param slardarInstance 需要上报的slardar实例
* @returns
*/
init(slardarInstance: SlardarInstance) {
if (!slardarInstance) {
console.warn('You should use reporter with a valid slardar instance');
return;
}
const slardarReportClient = new SlardarReportClient(slardarInstance);
this.slardarInstance = slardarReportClient.slardarInstance;
this.logger.persist.addClient(slardarReportClient);
this.initialized = true;
// Execute all pending items which are collected before initialization
this.pendingQueue.forEach(item => {
const levelFuncName: Omit<LogLevel, LogLevel.ERROR> =
item.level || LogLevel.INFO;
this.log(levelFuncName.toString() as LogType, item);
});
this.pendingQueue = [];
// Run init for all pending reporter instances
this.pendingInstance.forEach(instance => {
instance.init(slardarInstance);
});
this.pendingInstance = [];
}
getLogger() {
return this.logger;
}
/// Custom Log
/**
* 上报一个info日志
* @param event
* @returns
*/
info(log: CustomLog) {
this.log('info', log);
}
/**
* 上报一个success日志
* @param event
* @returns
*/
success(log: CustomLog) {
const info = this.formatCustomLog(log, LogLevel.SUCCESS);
this.log('success', info);
}
/**
* 上报一个warning日志
* @param event
* @returns
*/
warning(log: CustomLog) {
const info = this.formatCustomLog(log, LogLevel.WARNING);
this.log('warning', info);
}
/**
* 上报一个error日志
* @param event
* @returns
*/
error(log: CustomErrorLog) {
const info = this.formatCustomLog(
log,
LogLevel.ERROR,
) as CommonLogOptions & { error: Error };
this.log('error', info);
}
/// Custom Event
/**
* 上报一个自定义event事件
* @param event
* @returns
*/
event<EventEnum extends string>(event: CustomEvent<EventEnum>) {
const e = this.formatCustomEvent(event);
this.log('info', e);
}
/**
* 上报一个错误event事件LogLevel = 'error'
* @param event
* @returns
*/
errorEvent<EventEnum extends string>(event: ErrorEvent<EventEnum>) {
const e = this.formatErrorEvent(event) as CommonLogOptions & {
error: Error;
};
this.log('error', e);
}
/**
* 上报一个成功event事件LogLevel = 'success'
* @param event
* @returns
*/
successEvent<EventEnum extends string>(event: CustomEvent<EventEnum>) {
const e = this.formatCustomEvent(event) as CommonLogOptions;
this.log('success', e);
}
/// Trace Event
/**
* 性能追踪,可以记录一个流程中多个步骤间隔的耗时:
* @param event
* @returns
*/
tracer<EventEnum extends string>({ eventName }: TraceEvent<EventEnum>) {
const { tracer: durationTracer } = genDurationTracer();
const trace = (pointName: TracePointName, options: TraceOptions = {}) => {
const { meta, error } = options;
const e = this.formatCustomEvent({
eventName,
meta: {
...meta,
error,
duration: durationTracer(pointName),
},
});
if (!this.check(e)) {
return;
}
this.log('info', e);
};
return {
trace,
};
}
private check(info: CommonLogOptions) {
if (!this.initialized) {
// Initialization has not been called, collect the item into queue and consume it when called.
this.pendingQueue.push(info);
return false;
}
return true;
}
private formatCustomLog(
log: CustomLog | CustomErrorLog,
level: LogLevel,
): CommonLogOptions {
const {
namespace: ctxNamespace,
scope: ctxScope,
meta: ctxMeta = {},
} = this.logger.ctx?.options ?? {};
const { namespace, scope, meta = {}, message } = log;
return {
action: [LogAction.CONSOLE, LogAction.PERSIST],
namespace: namespace || ctxNamespace,
scope: scope || ctxScope,
level,
error: (log as CustomErrorLog).error,
message,
meta: {
...ctxMeta,
...meta,
},
};
}
private formatCustomEvent<EventEnum extends string>(
event: CustomEvent<EventEnum>,
): CommonLogOptions {
const {
namespace: ctxNamespace,
scope: ctxScope,
meta: ctxMeta = {},
} = this.logger.ctx?.options ?? {};
const { eventName, namespace, scope, meta = {} } = event;
return {
action: [LogAction.CONSOLE, LogAction.PERSIST],
namespace: namespace || ctxNamespace,
scope: scope || ctxScope,
eventName,
meta: {
...ctxMeta,
...meta,
},
};
}
private formatErrorEvent<EventEnum extends string>(
event: ErrorEvent<EventEnum>,
): CommonLogOptions {
const e = this.formatCustomEvent(event);
return {
...e,
meta: {
...e.meta,
// !NOTE: Slardar不支持`a.b`的字段的正则搜索(会报错),需要把`error.message`和`error.name`铺平放到第一层
errorMessage: event.error.message,
errorName: event.error.name,
level: event.level ?? 'error',
},
error: event.error,
};
}
}
const reporter = new Reporter();
export { reporter };