import { Awaitable, Getter } from '@blackglory/prelude' import { ITransport, Level } from '@src/types.js' import { getValue } from '@utils/get-value.js' import { measureElapsedTime, measureElapsedTimeAsync } from '@utils/measure-elapsed-time.js' export interface ILoggerOptions { level: Level transport: ITransport namespace?: string } export class Logger { constructor(private options: ILoggerOptions) {} trace(message: string | Getter, elapsedTime?: number): void { return this.logByLevel(Level.Trace, message, elapsedTime) } traceAsync( message: Awaitable | Getter> , elapsedTime?: number ): Promise { return this.logByLevelAsync(Level.Trace, message, elapsedTime) } info(message: string | Getter, elapsedTime?: number): void { return this.logByLevel(Level.Info, message, elapsedTime) } infoAsync( message: Awaitable | Getter> , elapsedTime?: number ): Promise { return this.logByLevelAsync(Level.Info, message, elapsedTime) } debug(message: string | Getter, elapsedTime?: number): void { return this.logByLevel(Level.Debug, message, elapsedTime) } debugAsync( message: Awaitable | Getter> , elapsedTime?: number ): Promise { return this.logByLevelAsync(Level.Debug, message, elapsedTime) } warn(message: string | Getter, elapsedTime?: number): void { return this.logByLevel(Level.Warn, message, elapsedTime) } warnAsync( message: Awaitable | Getter> , elapsedTime?: number ): Promise { return this.logByLevelAsync(Level.Warn, message, elapsedTime) } error(message: string | Getter, elapsedTime?: number): void { this.logByLevel(Level.Error, message, elapsedTime) } errorAsync( message: Awaitable | Getter> , elapsedTime?: number ): Promise { return this.logByLevelAsync(Level.Error, message, elapsedTime) } fatal(message: string | Getter, elapsedTime?: number): void { this.logByLevel(Level.Fatal, message, elapsedTime) } fatalAsync( message: Awaitable | Getter> , elapsedTime?: number ): Promise { return this.logByLevelAsync(Level.Fatal, message, elapsedTime) } traceTime( message: string | Getter , expression: () => T ): T { return this.logTimeByLevel(Level.Trace, message, expression) } traceTimeAsync( message: Awaitable | Getter> , expression: () => Awaitable ): Promise { return this.logTimeByLevelAsync(Level.Trace, message, expression) } infoTime( message: string | Getter , expression: () => T ): T { return this.logTimeByLevel(Level.Info, message, expression) } infoTimeAsync( message: Awaitable | Getter> , expression: () => Awaitable ): Promise { return this.logTimeByLevelAsync(Level.Info, message, expression) } debugTime( message: string | Getter , expression: () => T ): T { return this.logTimeByLevel(Level.Debug, message, expression) } debugTimeAsync( message: Awaitable | Getter> , expression: () => Awaitable ): Promise { return this.logTimeByLevelAsync(Level.Debug, message, expression) } warnTime( message: string | Getter , expression: () => T ): T { return this.logTimeByLevel(Level.Warn, message, expression) } warnTimeAsync( message: Awaitable | Getter> , expression: () => Awaitable ): Promise { return this.logTimeByLevelAsync(Level.Warn, message, expression) } errorTime( message: string | Getter , expression: () => T ): T { return this.logTimeByLevel(Level.Error, message, expression) } errorTimeAsync( message: Awaitable | Getter> , expression: () => Awaitable ): Promise { return this.logTimeByLevelAsync(Level.Error, message, expression) } fatalTime( message: string | Getter , expression: () => T ): T { return this.logTimeByLevel(Level.Fatal, message, expression) } fatalTimeAsync( message: Awaitable | Getter> , expression: () => Awaitable ): Promise { return this.logTimeByLevelAsync(Level.Fatal, message, expression) } traceTimeFunction( message: string | Getter , fn: (...args: Args) => Result ): (...args: Args) => Result { return this.logTimeFunctionByLevel(Level.Trace, message, fn) } traceTimeAsyncFunction( message: Awaitable | Getter> , fn: (...args: Args) => Awaitable ): (...args: Args) => Promise { return this.logTimeAsyncFunctionByLevel(Level.Trace, message, fn) } infoTimeFunction( message: string | Getter , fn: (...args: Args) => Result ): (...args: Args) => Result { return this.logTimeFunctionByLevel(Level.Info, message, fn) } infoTimeAsyncFunction( message: Awaitable | Getter> , fn: (...args: Args) => Awaitable ): (...args: Args) => Promise { return this.logTimeAsyncFunctionByLevel(Level.Info, message, fn) } debugTimeFunction( message: string | Getter , fn: (...args: Args) => Result ): (...args: Args) => Result { return this.logTimeFunctionByLevel(Level.Debug, message, fn) } debugTimeAsyncFunction( message: Awaitable | Getter> , fn: (...args: Args) => Awaitable ): (...args: Args) => Promise { return this.logTimeAsyncFunctionByLevel(Level.Debug, message, fn) } warnTimeFunction( message: string | Getter , fn: (...args: Args) => Result ): (...args: Args) => Result { return this.logTimeFunctionByLevel(Level.Warn, message, fn) } warnTimeAsyncFunction( message: Awaitable | Getter> , fn: (...args: Args) => Awaitable ): (...args: Args) => Promise { return this.logTimeAsyncFunctionByLevel(Level.Warn, message, fn) } errorTimeFunction( message: string | Getter , fn: (...args: Args) => Result ): (...args: Args) => Result { return this.logTimeFunctionByLevel(Level.Error, message, fn) } errorTimeAsyncFunction( message: Awaitable | Getter> , fn: (...args: Args) => Awaitable ): (...args: Args) => Promise { return this.logTimeAsyncFunctionByLevel(Level.Error, message, fn) } fatalTimeFunction( message: string | Getter , fn: (...args: Args) => Result ): (...args: Args) => Result { return this.logTimeFunctionByLevel(Level.Fatal, message, fn) } fatalTimeAsyncFunction( message: Awaitable | Getter> , fn: (...args: Args) => Awaitable ): (...args: Args) => Promise { return this.logTimeAsyncFunctionByLevel(Level.Fatal, message, fn) } private logByLevel( level: Level , message: string | Getter , elapsedTime?: number ): void { if (this.options.level <= level) { this.send(level, message, elapsedTime) } } private async logByLevelAsync( level: Level , message: Awaitable | Getter> , elapsedTime?: number ): Promise { if (this.options.level <= level) { await this.sendAsync(level, message, elapsedTime) } } private logTimeByLevel( level: Level , message: string | Getter , expression: () => T ): T { if (this.options.level <= level) { return measureElapsedTime( expression , elapsedTime => this.send(level, message, elapsedTime) ) } else { return expression() } } private async logTimeByLevelAsync( level: Level , message: Awaitable | Getter> , expression: () => Awaitable ): Promise { if (this.options.level <= level) { return measureElapsedTimeAsync( expression , elapsedTime => this.sendAsync(level, message, elapsedTime) ) } else { return await expression() } } private logTimeFunctionByLevel( level: Level , message: string | Getter , fn: (...args: Args) => Result ): (...args: Args) => Result { if (this.options.level <= level) { return (...args: Args) => { return measureElapsedTime( () => fn(...args) , elapsedTime => this.send(level, message, elapsedTime) ) } } else { return fn } } private logTimeAsyncFunctionByLevel( level: Level , message: Awaitable | Getter> , fn: (...args: Args) => Awaitable ): (...args: Args) => Promise { if (this.options.level <= level) { return (...args: Args) => { return measureElapsedTimeAsync( () => fn(...args) , elapsedTime => this.sendAsync(level, message, elapsedTime) ) } } else { return async (...args: Args) => await fn(...args) } } private send( level: Level , message: string | Getter , elapsedTime?: number ): void { this.options.transport.send({ level , message: getValue(message) , namespace: this.options.namespace , timestamp: Date.now() , elapsedTime }) } private async sendAsync( level: Level , message: Awaitable | Getter> , elapsedTime?: number ): Promise { return this.send(level, await getValue(message), elapsedTime) } }