export enum Level { debug = 10 , info = 20 , warning = 30 , error = 40 , test = 1000 } export interface LoggerAdapter { info ( ... message ) : void ; warn ( ... message ) : void ; error ( ... message ) : void ; debug ( ... message ) : void ; } export type Formatter = ( name : string , level : Level , ... message : any [ ] ) => string ; export class ContextLogger { public destroy = ( ) : void => void 0 ; private effects : Map < keyof LoggerAdapter , Function [ ] > = new Map ( ) ; constructor ( private name , private level , private formatter ? , private adapter = console ) { } private invoke ( method , message ) { let output = '' ; if ( this . formatter ) { const params = [ this . name , this . level ] . concat ( message ) ; output = this . formatter . apply ( this , params ) ; } this . adapter [ method ] . call ( this . adapter , output ) ; const fns = ( this . effects . get ( method as ( keyof LoggerAdapter ) ) || [ ] ) ; fns . forEach ( ( fn ) => fn ( ... message ) ) ; } info ( ... message ) { if ( Level . info >= this . level ) { this . invoke ( 'info' , message ) ; } } warn ( ... message ) { if ( Level . warning >= this . level ) { this . invoke ( 'warn' , message ) ; } } error ( ... message ) { if ( Level . error >= this . level ) { this . invoke ( 'error' , message ) ; } } debug ( ... message ) { if ( Level . debug >= this . level ) { this . invoke ( 'debug' , message ) ; } } setLevel ( level ) { this . level = level ; } replaceAdapter ( adapter ) { if ( adapter !== this . adapter ) { this . adapter = adapter ; } } replaceFormatter ( formatter ) { if ( formatter !== this . formatter ) { this . formatter = formatter ; } } effect ( method , callback ) { if ( this . effects . has ( method ) ) { const fns = this . effects . get ( method ) ! ; if ( fns . every ( ( fn ) => fn !== callback ) ) { fns . push ( callback ) ; } } else { this . effects . set ( method , [ callback ] ) ; } } clearEffects ( ) { this . effects . clear ( ) ; } } export class Logger { private static contextMap = new Map < string , ContextLogger > ( ) ; private static defaultLevel = Level . debug ; private static outputLogger = new ContextLogger ( '[ReactiveDB]' , Logger . defaultLevel , ( name , _ , message ) => { const output = Array . isArray ( message ) ? message . join ( '' ) : message ; const current = new Date ( ) ; const prefix = name ? `[${ name }] ` : '' ; return `${ prefix }at ${ current . toLocaleString ( ) }: \r\n    ` + output ; } ) ; static get ( name , formatter ? , level ? , adapter = console ) { const logger = Logger . contextMap . get ( name ) ; if ( ! logger ) { const ctxLogger = new ContextLogger ( name , level || Logger . defaultLevel , formatter , adapter ) ; Logger . contextMap . set ( name , ctxLogger ) ; ctxLogger . destroy = ( ) => { Logger . contextMap . delete ( name ) ; ctxLogger . clearEffects ( ) ; } ; return ctxLogger ; } return logger ; } static setLevel ( level ) { Logger . outputLogger . setLevel ( level ) ; } static warn ( ... message ) { Logger . outputLogger . warn ( ... message ) ; } static info ( ... message ) { Logger . outputLogger . info ( ... message ) ; } static debug ( ... message ) { Logger . outputLogger . debug ( ... message ) ; } static error ( ... message ) { Logger . outputLogger . error ( ... message ) ; } } const envifyLevel = ( ) => { const env = ( process && process . env && process . env . NODE_ENV ) || 'production' ; switch ( env ) { case 'production' : return Level . error ; case 'test' : return Level . test ; default : return Level . debug ; } } ; Logger . setLevel ( envifyLevel ( ) ) ;