import { LoggerDelegate } from 'polar-shared/src/logger/LoggerDelegate' ; import { FilteredLogger } from './FilteredLogger' ; import { ConsoleLogger } from 'polar-shared/src/logger/ConsoleLogger' ; import { LevelAnnotatingLogger } from './annotating/LevelAnnotatingLogger' ; import { VersionAnnotatingLogger } from './annotating/VersionAnnotatingLogger' ; import { ILogger } from 'polar-shared/src/logger/ILogger' ; import { LogLevel } from './LogLevel' ; import { LogLevels } from './LogLevels' ; import { Optional } from 'polar-shared/src/util/ts/Optional' ; import { MultiLogger } from './MultiLogger' ; import { ElectronContextType } from '../electron/context/ElectronContextType' ; import { ElectronContextTypes } from '../electron/context/ElectronContextTypes' ; import process from 'process' ; import { MemoryLogger } from './MemoryLogger' ; import { ISODateTimeString } from 'polar-shared/src/metadata/ISODateTimeStrings' ; import { GALogger } from './GALogger' ; import { AppRuntime } from 'polar-shared/src/util/AppRuntime' ; /**
 * Maintains our general logging infrastructure.  Differentiated from Logger
 * which performs the actual logging of message. This maintains Loggers.
 */ export class Logging { /**
     * Initialize the logger to write to a specific directory.
     */ public static async init ( ) { const level = this . configuredLevel ( ) ; const target = await this . createTarget ( level ) ; await this . initWithTarget ( target ) ; } /**
     * Initialize a logger suitable for testing.
     */ public static initForTesting ( ) { const level = this . configuredLevel ( ) ; const target = new ConsoleLogger ( ) ; const delegate = new FilteredLogger ( new VersionAnnotatingLogger ( new LevelAnnotatingLogger ( target ) ) , level ) ; LoggerDelegate . set ( delegate ) ; } public static async initWithTarget ( target ) { const lc = await this . loggingConfig ( ) ; const delegate = new FilteredLogger ( new VersionAnnotatingLogger ( new LevelAnnotatingLogger ( target ) ) , lc . level ) ; LoggerDelegate . set ( delegate ) ; const logger = LoggerDelegate . get ( ) ; logger . info ( `Using logger: ${ logger . name }: target=${ lc . target }, level=${ LogLevel [ lc . level ] }` ) ; } public static async createTarget ( level ) { const loggers = [ ] ; const electronContext = ElectronContextTypes . create ( ) ; if ( AppRuntime . isBrowser ( ) ) { } if ( [ 'electron-renderer' , 'browser' ] . includes ( AppRuntime . get ( ) ) ) { loggers . push ( new GALogger ( ) ) ; } if ( electronContext === ElectronContextType . RENDERER ) { loggers . push ( new MemoryLogger ( ) ) ; } loggers . push ( await this . createPrimaryTarget ( ) ) ; return new MultiLogger ( ... loggers ) ; } public static async createPrimaryTarget ( ) { const loggingConfig = await this . loggingConfig ( ) ; if ( loggingConfig . target === LoggerTarget . CONSOLE ) { return new ConsoleLogger ( ) ; } else { throw new Error ( "Invalid target: " + loggingConfig . target ) ; } } private static async loggingConfig ( ) { return { target : LoggerTarget . CONSOLE , level : this . configuredLevel ( ) } ; } private static configuredLevel ( ) { const isRendererContext = typeof window !== 'undefined' ; const fromENV = ( ) : Optional < string > => { return Optional . of ( process . env . POLAR_LOG_LEVEL ) ; } ; const fromStorage = ( storage ) : Optional < string > => { return Optional . of ( storage . getItem ( "POLAR_LOG_LEVEL" ) ) ; } ; const fromLocalStorage = ( ) : Optional < string > => { if ( isRendererContext ) { return fromStorage ( window . localStorage ) ; } return Optional . empty ( ) ; } ; const fromSessionStorage = ( ) : Optional < string > => { if ( isRendererContext ) { return fromStorage ( window . sessionStorage ) ; } return Optional . empty ( ) ; } ; const level = Optional . first < string > ( fromENV ( ) , fromLocalStorage ( ) , fromSessionStorage ( ) ) . map ( level => LogLevels . fromName ( level ) ) . getOrElse ( LogLevel . WARN ) ; return level ; } } enum LoggerTarget { CONSOLE = 'CONSOLE' } /**
 * Basic disk config for our log information.
 */ export interface LoggingConfig { readonly target : LoggerTarget ; readonly level : LogLevel ; } export interface LogMessage { /**
     * A unique number for this LogMessage, just needs to be unique to the
     * process and we should be able to use a simple nonce.
     */ readonly idx : number ; readonly timestamp : ISODateTimeString ; readonly level : LogLevelName ; readonly msg : string ; readonly args : ReadonlyArray < any > ; } export type LogLevelName = 'notice' | 'info' | 'warn' | 'error' | 'verbose' | 'debug' ;