UNPKG

@litert/logger

Version:

A logs-recorder for LiteRT framework.

183 lines (137 loc) 4.59 kB
/** * Copyright 2025 Angus ZENG <fenying@litert.org> * * 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 * * https://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 * as dL from './Decl'; /** * Create a logging method, works like a JIT compiler. */ function createLogMethod<T, TLv extends string>( subject: string, level: string, traceDepth: number, driver: dL.IDriver, formatter: dL.IFormatter<T, TLv> ): dL.ILoggerMethod<T, TLv> { const cs: string[] = []; cs.push('return function(log, dt = Date.now()) {'); subject = JSON.stringify(subject); level = JSON.stringify(level); if (traceDepth) { cs.push('let tmpObj = {};'); cs.push(`Error.captureStackTrace(tmpObj, this[${level}]);`); cs.push('let traces = tmpObj.stack.split('); cs.push(' /\\n\\s+at\\s+/'); cs.push(`).slice(1, ${traceDepth + 1});`); } cs.push('driver.write('); cs.push(' formatter('); cs.push(' log,'); cs.push(` ${subject},`); cs.push(` ${level},`); if (traceDepth) { cs.push(' dt,'); cs.push(' traces'); } else { cs.push(' dt'); } cs.push(' ),'); cs.push(` ${subject},`); cs.push(` ${level},`); cs.push(' dt'); cs.push(');'); cs.push('return this;'); cs.push('};'); return (new Function( 'formatter', 'driver', cs.join('\n') ))( formatter, driver ); } export class Logger<TLog, TLv extends string> implements dL.IBaseLogger<TLog, TLv> { private readonly _mutedLogger = (): this => this; protected readonly _options: Record<string, dL.ILevelOptions<TLog, TLv>> = {}; public constructor( public readonly subject: string, defaultOptions: Record<string, dL.ILevelOptions<TLog, TLv>>, public readonly levels: readonly TLv[] ) { for (const lv of levels) { this.setLevelOptions({ ...defaultOptions[lv], 'levels': lv }); } } public setLevelOptions(options: dL.ILevelUpdateOptions<TLog, TLv>): this { const levels = options.levels?.length ? Array.isArray(options.levels) ? options.levels : [options.levels] : this.levels; for (const lv of levels) { if (!this.levels.includes(lv)) { continue; } this._options[lv] = { 'traceDepth': Math.max(0, options.traceDepth ?? this._options[lv]?.traceDepth ?? 0), 'enabled': options.enabled ?? this._options[lv]?.enabled ?? true, 'driver': options.driver ?? this._options[lv].driver, 'formatter': options.formatter ?? this._options[lv].formatter, }; this._updateMethod(lv); } return this; } public getLevel(level: TLv): dL.ILevelOptions<TLog, TLv> { return this._options[level]; } public clone(): this { const logger = new Logger<TLog, TLv>( this.subject, { ...this._options }, this.levels ); for (const lv of this.levels) { logger.setLevelOptions({ levels: lv, ...this.getLevel(lv), }); } return logger as this; } public setSubject(subject: string): this { if (this.subject === subject) { return this; } Object.assign(this, { subject }); for (const lv of this.levels) { this._updateMethod(lv); } return this; } protected _updateMethod(lv: string): void { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error this[lv] = this._options[lv].enabled ? createLogMethod<TLog, TLv>( this.subject, lv, this._options[lv].traceDepth, this._options[lv].driver, this._options[lv].formatter ) : this._mutedLogger; } }