packageDocumentation module import { BentleyError , IModelStatus , LoggingMetaData } from "./BentleyError" ; import { BentleyLoggerCategory } from "./BentleyLoggerCategory" ; import { IDisposable } from "./Disposable" ; public export type LogFunction = ( category : string , message : string , metaData : LoggingMetaData ) => void ; public export enum LogLevel { /** Tracing and debugging - low level */ Trace , /** Information - mid level */ Info , /** Warnings - high level */ Warning , /** Errors - highest level */ Error , /** Higher than any real logging level. This is used to turn a category off. */ None } public export interface LoggerCategoryAndLevel { category : string ; logLevel : string ; } public export interface LoggerLevelsConfig { defaultLevel ? : string ; categoryLevels ? : LoggerCategoryAndLevel [ ] ; } public export class Logger { protected static _logError : LogFunction | undefined ; protected static _logWarning : LogFunction | undefined ; protected static _logInfo : LogFunction | undefined ; protected static _logTrace : LogFunction | undefined ; private static _categoryFilter : Map < string , LogLevel > = new Map < string , LogLevel > ( ) ; private static _minLevel : LogLevel | undefined = undefined ; /** Should the call stack be included when an exception is logged?  */ public static logExceptionCallstacks = false ; internal public static staticMetaData = new Map < string , LoggingMetaData > ( ) ; /** Initialize the logger streams. Should be called at application initialization time. */ public static initialize ( logError ? , logWarning ? , logInfo ? , logTrace ? ) { Logger . _logError = logError ; Logger . _logWarning = logWarning ; Logger . _logInfo = logInfo ; Logger . _logTrace = logTrace ; Logger . turnOffLevelDefault ( ) ; Logger . turnOffCategories ( ) ; } /** Initialize the logger to output to the console. */ public static initializeToConsole ( ) { const logConsole = ( level ) => ( category , message , metaData ) => console . log ( `${ level } | ${ category } | ${ message } ${ Logger . stringifyMetaData ( metaData ) }` ) ; Logger . initialize ( logConsole ( "Error" ) , logConsole ( "Warning" ) , logConsole ( "Info" ) , logConsole ( "Trace" ) ) ; } /** merge the supplied metadata with all static metadata into one object */ public static getMetaData ( metaData ? ) { const metaObj = { } ; for ( const meta of Logger . staticMetaData ) { const val = BentleyError . getMetaData ( meta [ 1 ] ) ; if ( val ) Object . assign ( metaObj , val ) ; } Object . assign ( metaObj , BentleyError . getMetaData ( metaData ) ) ; return metaObj ; } /** stringify the metadata for a log message by merging the supplied metadata with all static metadata into one object that is then `JSON.stringify`ed. */ public static stringifyMetaData ( metaData ? ) { const metaObj = this . getMetaData ( metaData ) ; return Object . keys ( metaObj ) . length > 0 ? JSON . stringify ( metaObj ) : "" ; } /** Set the least severe level at which messages should be displayed by default. Call setLevel to override this default setting for specific categories. */ public static setLevelDefault ( minLevel ) { Logger . _minLevel = minLevel ; } /** Set the minimum logging level for the specified category. The minimum level is least severe level at which messages in the
     * specified category should be displayed.
     */ public static setLevel ( category , minLevel ) { Logger . _categoryFilter . set ( category , minLevel ) ; } /** Interpret a string as the name of a LogLevel */ public static parseLogLevel ( str ) { switch ( str . toUpperCase ( ) ) { case "EXCEPTION" : return LogLevel . Error ; case "FATAL" : return LogLevel . Error ; case "ERROR" : return LogLevel . Error ; case "WARNING" : return LogLevel . Warning ; case "INFO" : return LogLevel . Info ; case "TRACE" : return LogLevel . Trace ; case "DEBUG" : return LogLevel . Trace ; } return LogLevel . None ; } /** Set the log level for multiple categories at once. Also see [[validateProps]] */ public static configureLevels ( cfg ) { Logger . validateProps ( cfg ) ; if ( cfg . defaultLevel !== undefined ) { this . setLevelDefault ( Logger . parseLogLevel ( cfg . defaultLevel ) ) ; } if ( cfg . categoryLevels !== undefined ) { for ( const cl of cfg . categoryLevels ) { this . setLevel ( cl . category , Logger . parseLogLevel ( cl . logLevel ) ) ; } } } private static isLogLevel ( v ) { return LogLevel . hasOwnProperty ( v ) ; } /** Check that the specified object is a valid LoggerLevelsConfig. This is useful when reading a config from a .json file. */ public static validateProps ( config ) { const validProps = [ "defaultLevel" , "categoryLevels" ] ; for ( const prop of Object . keys ( config ) ) { if ( ! validProps . includes ( prop ) ) throw new BentleyError ( IModelStatus . BadArg , `LoggerLevelsConfig - unrecognized property: ${ prop }` ) ; if ( prop === "defaultLevel" ) { if ( ! Logger . isLogLevel ( config . defaultLevel ) ) throw new BentleyError ( IModelStatus . BadArg , `LoggerLevelsConfig.defaultLevel must be a LogLevel. Invalid value: ${ JSON . stringify ( config . defaultLevel ) }` ) ; } else if ( prop === "categoryLevels" ) { const value = config [ prop ] ; if ( ! Array . isArray ( value ) ) throw new BentleyError ( IModelStatus . BadArg , `LoggerLevelsConfig.categoryLevels must be an array. Invalid value: ${ JSON . stringify ( value ) }` ) ; for ( const item of config [ prop ] ) { if ( ! item . hasOwnProperty ( "category" ) || ! item . hasOwnProperty ( "logLevel" ) ) throw new BentleyError ( IModelStatus . BadArg , `LoggerLevelsConfig.categoryLevels - each item must be a LoggerCategoryAndLevel {category: logLevel:}. Invalid value: ${ JSON . stringify ( item ) }` ) ; if ( ! Logger . isLogLevel ( item . logLevel ) ) throw new BentleyError ( IModelStatus . BadArg , `LoggerLevelsConfig.categoryLevels - each item's logLevel property must be a LogLevel. Invalid value: ${ JSON . stringify ( item . logLevel ) }` ) ; } } } } /** Get the minimum logging level for the specified category. */ public static getLevel ( category ) { const minLevelForThisCategory = Logger . _categoryFilter . get ( category ) ; if ( minLevelForThisCategory !== undefined ) return minLevelForThisCategory ; const parent = category . lastIndexOf ( "." ) ; if ( parent !== - 1 ) return Logger . getLevel ( category . slice ( 0 , parent ) ) ; return Logger . _minLevel ; } /** Turns off the least severe level at which messages should be displayed by default.
     * This turns off logging for all messages for which no category minimum level is defined.
     */ public static turnOffLevelDefault ( ) { Logger . _minLevel = undefined ; } /** Turns off all category level filters previously defined with [[Logger.setLevel]].
     */ public static turnOffCategories ( ) { Logger . _categoryFilter . clear ( ) ; } /** Check if messages in the specified category should be displayed at this level of severity. */ public static isEnabled ( category , level ) { const minLevel = Logger . getLevel ( category ) ; return ( minLevel !== undefined ) && ( level >= minLevel ) ; } param category param message param metaData public static logError ( category , message , metaData ? ) { if ( Logger . _logError && Logger . isEnabled ( category , LogLevel . Error ) ) Logger . _logError ( category , message , metaData ) ; } private static getExceptionMessage ( err ) { const stack = Logger . logExceptionCallstacks ? `\n${ BentleyError . getErrorStack ( err ) }` : "" ; return BentleyError . getErrorMessage ( err ) + stack ; } param category param err param log param metaData public static logException ( category , err , log = Logger . logError ) { log ( category , Logger . getExceptionMessage ( err ) , ( ) => { return { ... BentleyError . getErrorMetadata ( err ) , exceptionType : err . constructor . name } ; } ) ; } param category param message param metaData public static logWarning ( category , message , metaData ? ) { if ( Logger . _logWarning && Logger . isEnabled ( category , LogLevel . Warning ) ) Logger . _logWarning ( category , message , metaData ) ; } param category param message param metaData public static logInfo ( category , message , metaData ? ) { if ( Logger . _logInfo && Logger . isEnabled ( category , LogLevel . Info ) ) Logger . _logInfo ( category , message , metaData ) ; } param category param message param metaData public static logTrace ( category , message , metaData ? ) { if ( Logger . _logTrace && Logger . isEnabled ( category , LogLevel . Trace ) ) Logger . _logTrace ( category , message , metaData ) ; } } public export class PerfLogger implements IDisposable { private static _severity : LogLevel = LogLevel . Info ; private _operation : string ; private _metaData ? : LoggingMetaData ; private _startTimeStamp : number ; public constructor ( operation , metaData ? ) { this . _operation = operation ; this . _metaData = metaData ; if ( ! Logger . isEnabled ( BentleyLoggerCategory . Performance , PerfLogger . _severity ) ) { this . _startTimeStamp = 0 ; return ; } Logger . logInfo ( BentleyLoggerCategory . Performance , `${ this . _operation },START` , this . _metaData ) ; this . _startTimeStamp = new Date ( ) . getTime ( ) ; } private logMessage ( ) { const endTimeStamp = new Date ( ) . getTime ( ) ; if ( ! Logger . isEnabled ( BentleyLoggerCategory . Performance , PerfLogger . _severity ) ) return ; Logger . logInfo ( BentleyLoggerCategory . Performance , `${ this . _operation },END` , ( ) => { const mdata = this . _metaData ? BentleyError . getMetaData ( this . _metaData ) : { } ; return { ... mdata , TimeElapsed : endTimeStamp - this . _startTimeStamp } ; } ) ; } public dispose ( ) { this . logMessage ( ) ; } }