import {LoggingLevel, LogMessage} from './types.ts' import Logger from './Logger.ts' import {Service} from '../../../../di/src/decorator/Service.ts' import {make} from '../../../../di/src/global.ts' import {isInstantiable} from '../../../../di/src/type/Instantiable.ts' /** * Service for managing application logging. */ @Service() class Logging { /** * The current logging level. * @type LoggingLevel */ private _level = LoggingLevel.Warning /** * Loggers registered with this service. * @type Array */ private _loggers: Logger[] = [] /** * Get the current logging level. * @type LoggingLevel */ public get level() { return this._level } /** * Set the new logging level. * @param {LoggingLevel} level */ public set level(level) { this._level = level } /** * Write an output with the success level. * @param output * @param {boolean} [force = false] - if true, the output will be written, regardless of the output level */ public success(output: any, force = false) { this.write_log(LoggingLevel.Success, output, force) } /** * Write an output with the error level. * @param output * @param {boolean} [force = false] - if true, the output will be written, regardless of the output level */ public error(output: any, force = false) { this.write_log(LoggingLevel.Error, output, force) } /** * Write an output with the warning level. * @param output * @param {boolean} [force = false] - if true, the output will be written, regardless of the output level */ public warn(output: any, force = false) { this.write_log(LoggingLevel.Warning, output, force) } /** * Write an output with the info level. * @param output * @param {boolean} [force = false] - if true, the output will be written, regardless of the output level */ public info(output: any, force = false) { this.write_log(LoggingLevel.Info, output, force) } /** * Write an output with the debug level. * @param output * @param {boolean} [force = false] - if true, the output will be written, regardless of the output level */ public debug(output: any, force = false) { this.write_log(LoggingLevel.Debug, output, force) } /** * Write an output with the verbose level. * @param output * @param {boolean} [force = false] - if true, the output will be written, regardless of the output level */ public verbose(output: any, force = false) { this.write_log(LoggingLevel.Verbose, output, force) } /** * Writes the output at the given logging level. * @param {LoggingLevel} level * @param output * @param {boolean} [force = false] */ protected write_log(level: LoggingLevel, output: any, force = false) { const message = this.build_message(level, output) if ( this._level >= level || force ) { for ( const logger of this._loggers ) { try { logger.write(message) } catch (e) { console.error('logging error', e) } } } } /** * Given an output and level, build a log message object. * @param {LoggingLevel} level * @param output * @return LogMessage */ protected build_message(level: LoggingLevel, output: any): LogMessage { return { level, output, date: new Date, caller_name: this.get_caller_info(), } } /** * Register a logger with this class. * @param {typeof Logger} logger_class */ public register_logger(logger_class: typeof Logger) { if ( isInstantiable(logger_class) ) { const logger = make(logger_class) if ( !this._loggers.includes(logger) ) this._loggers.push(logger) } } /** * Remove a logger from this class. * @param {typeof Logger} logger_class */ public remove_logger(logger_class: typeof Logger) { this._loggers = this._loggers.filter(x => !(x instanceof logger_class)) } /** * Get the information about the caller of a given context. * @param {number} [level = 5] - how far up in the stacktrace to go */ protected get_caller_info(level = 5): string { let e = new Error if ( !e.stack ) return 'Unknown' return e.stack.split(' at ') .slice(level) .map((x: string): string => x.trim().split(' (')[0].split('.')[0].split(':')[0])[0] } } export { Logging }