import type { PublicMethodsOf } from '@osd/utility-types' ; import { DisposableAppender , LogLevel , Logger , LoggerFactory } from '@osd/logging' ; import { Appenders } from './appenders/appenders' ; import { BufferAppender } from './appenders/buffer/buffer_appender' ; import { BaseLogger } from './logger' ; import { LoggerAdapter } from './logger_adapter' ; import { LoggingConfigType , LoggerConfigType , LoggingConfig , LoggerContextConfigType , LoggerContextConfigInput , loggerContextConfigSchema , } from './logging_config' ; export type ILoggingSystem = PublicMethodsOf < LoggingSystem > ; internal export class LoggingSystem implements LoggerFactory { /** The configuration set by the user. */ private baseConfig ? : LoggingConfig ; /** The fully computed configuration extended by context-specific configurations set programmatically */ private computedConfig ? : LoggingConfig ; private readonly appenders : Map < string , DisposableAppender > = new Map ( ) ; private readonly bufferAppender = new BufferAppender ( ) ; private readonly loggers : Map < string , LoggerAdapter > = new Map ( ) ; private readonly contextConfigs = new Map < string , LoggerContextConfigType > ( ) ; public get ( ... contextParts ) { const context = LoggingConfig . getLoggerContext ( contextParts ) ; if ( ! this . loggers . has ( context ) ) { this . loggers . set ( context , new LoggerAdapter ( this . createLogger ( context , this . computedConfig ) ) ) ; } return this . loggers . get ( context ) ! ; } /**
     * Safe wrapper that allows passing logging service as immutable LoggerFactory.
     */ public asLoggerFactory ( ) { return { get : ( ... contextParts ) => this . get ( ... contextParts ) } ; } param rawConfig public upgrade ( rawConfig ) { const config = new LoggingConfig ( rawConfig ) ! ; this . applyBaseConfig ( config ) ; } remarks example param baseContextParts param rawConfig public setContextConfig ( baseContextParts , rawConfig ) { const context = LoggingConfig . getLoggerContext ( baseContextParts ) ; const contextConfig = loggerContextConfigSchema . validate ( rawConfig ) ; this . contextConfigs . set ( context , { ... contextConfig , loggers : contextConfig . loggers . map ( ( l ) => ( { ... l , context : LoggingConfig . getLoggerContext ( l . context . length > 0 ? [ context , l . context ] : [ context ] ) , } ) ) , } ) ; if ( this . baseConfig ) { this . applyBaseConfig ( this . baseConfig ) ; } } returns public async stop ( ) { await Promise . all ( [ ... this . appenders . values ( ) ] . map ( ( a ) => a . dispose ( ) ) ) ; await this . bufferAppender . dispose ( ) ; this . appenders . clear ( ) ; this . loggers . clear ( ) ; } private createLogger ( context , config ) { if ( config === undefined ) { return new BaseLogger ( context , LogLevel . All , [ this . bufferAppender ] , this . asLoggerFactory ( ) ) ; } const { level , appenders } = this . getLoggerConfigByContext ( config , context ) ; const loggerLevel = LogLevel . fromId ( level ) ; const loggerAppenders = appenders . map ( ( appenderKey ) => this . appenders . get ( appenderKey ) ! ) ; return new BaseLogger ( context , loggerLevel , loggerAppenders , this . asLoggerFactory ( ) ) ; } private getLoggerConfigByContext ( config , context ) { const loggerConfig = config . loggers . get ( context ) ; if ( loggerConfig !== undefined ) { return loggerConfig ; } return this . getLoggerConfigByContext ( config , LoggingConfig . getParentLoggerContext ( context ) ) ; } private applyBaseConfig ( newBaseConfig ) { const computedConfig = [ ... this . contextConfigs . values ( ) ] . reduce ( ( baseConfig , contextConfig ) => baseConfig . extend ( contextConfig ) , newBaseConfig ) ; for ( const appender of this . appenders . values ( ) ) { appender . dispose ( ) ; } this . appenders . clear ( ) ; for ( const [ appenderKey , appenderConfig ] of computedConfig . appenders ) { this . appenders . set ( appenderKey , Appenders . create ( appenderConfig ) ) ; } for ( const [ loggerKey , loggerAdapter ] of this . loggers ) { loggerAdapter . updateLogger ( this . createLogger ( loggerKey , computedConfig ) ) ; } this . baseConfig = newBaseConfig ; this . computedConfig = computedConfig ; for ( const logRecord of this . bufferAppender . flush ( ) ) { this . get ( logRecord . context ) . log ( logRecord ) ; } } }