import * as $ from 'jquery'; import { ERROR_CODE } from './ErrorCode'; import { LOG_LEVEL } from './LogLevel'; import { LogObj } from './LogObj'; import { LOG_SOURCE } from './LogSource'; import { ILoggerConfiguration, defaultLoggerConfiguration } from '../models/LoggerConfiguration'; /** * Abstract Class for AMC Logger objects. Implementations of this interface report * DaVinci App logs through various methods and mediums. * * @export * @abstract * @class AbstractLogger */ export abstract class AbstractLogger { // ================================================================================= // || Common Fields (and Constructor) || // ================================================================================= public static tabId = '00000000-0000-0000-0000-000000000000'; public config: ILoggerConfiguration; protected timerId: number = null; protected static ApiServiceUrl: string = null; protected _isDev = false; protected _logLevel: LOG_LEVEL = LOG_LEVEL.Information; protected _logSource: string; get logLevel(): LOG_LEVEL { return this._logLevel; } set logLevel(logLevel: LOG_LEVEL) { this._logLevel = logLevel; } get logSource() { return this._logSource; } set logSource(logSourceIn: LOG_SOURCE | string) { this._logSource = logSourceIn; } get apiUrl() { return AbstractLogger.ApiServiceUrl; } set apiUrl(apiUrl: string) { AbstractLogger.ApiServiceUrl = apiUrl; this.syncLogLevel(); } get isDev(): boolean { return this._isDev; } set isDev(isDev: boolean) { this._isDev = isDev; } /** * Creates an instance of AbstractLogger. * @param {(LOG_SOURCE | string)} logSource Name of DaVinci App which owns this Logger instance * @param {boolean} [isDev=false] True if developer mode should be enabled (behavior dependent on implementation) * @param {string} [apiUrl=null] URL of DaVinci API. Required to retrieve configured log level * @memberof AbstractLogger */ constructor(logSource: LOG_SOURCE | string, isDev: boolean = false, apiUrl: string = null) { this._logSource = logSource; this.getTabIdFromSession(); if (this.timerId == null) { this.timerId = window.setInterval(this.pushLogs.bind(this), 60000); this.setConfiguration(defaultLoggerConfiguration); } if (apiUrl != null && /\S/.test(apiUrl) && apiUrl !== undefined) { this.apiUrl = apiUrl; } this._isDev = isDev; } // ================================================================================= // || Abstract Methods || // ================================================================================= abstract setConfiguration(config: ILoggerConfiguration): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create a LOOP log * * @abstract * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ abstract logLoop(message: string, errorCode?: ERROR_CODE, localTime?: Date): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create a TRACE log * * @abstract * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ abstract logTrace(message: string, errorCode?: ERROR_CODE, localTime?: Date): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create a DEBUG log * * @abstract * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ abstract logDebug(message: string, errorCode?: ERROR_CODE, localTime?: Date): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create an INFORMATION log * * @abstract * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ abstract logInformation(message: string, errorCode?: ERROR_CODE, localTime?: Date): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create a WARNING log * * @abstract * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ abstract logWarning(message: string, errorCode?: ERROR_CODE, localTime?: Date): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create an ERROR log * * @abstract * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ abstract logError(message: string, errorCode?: ERROR_CODE, localTime?: Date): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create a CRITICAL log. Deprecated, behaves the same as logCritical() * * @deprecated * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ logCriticial(message: string, errorCode: ERROR_CODE = ERROR_CODE.Other, localTime: Date = new Date()): void { this.logCritical(message, errorCode, localTime); } // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Create a CRITICAL log * * @abstract * @param {string} message Content of log record * @param {ERROR_CODE} errorCode Error classification * @param {Date} localTime Local time when log record was generated * @memberof AbstractLogger */ abstract logCritical(message: string, errorCode?: ERROR_CODE, localTime?: Date): void; // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| /** * Push logs if any log records have been buffered. Logs are not guaranteed * to be committed if this object is deallocated before pushLogs() is called. * * @abstract * @memberof AbstractLogger */ abstract pushLogs(): void; /** * Push logs asynchronously if any log records have been buffered. Logs are not * guaranteed to be committed if this object is deallocated before pushLogsAsync() * is called and resolved successfully. * * @abstract * @return {*} {Promise} * @memberof AbstractLogger */ abstract pushLogsAsync(): Promise; // ================================================================================= // || Common Implementations || // ================================================================================= // Potentially a bug since we are clearing the timeout using an id of an interval (Came from current logger code) clearTimeOutForLogger(): void { clearTimeout(this.timerId); this.timerId = null; } /** * Retrieves the session ID from browser session storage, or generates a new one * if not already present. The session ID helps to differentiate app logs if duplicate * instances of the parent DaVinci App are loaded in multiple tabs. * * @protected * @memberof AbstractLogger */ protected getTabIdFromSession() { const sessionTabId = sessionStorage.getItem('LoggerTabId'); if (sessionTabId != null) { AbstractLogger.tabId = sessionTabId; } else { AbstractLogger.tabId = this.uuidv4(); sessionStorage.setItem('LoggerTabId', AbstractLogger.tabId); } } /** * Generates a random UUID. Method of generation is not secure. Only use this * method to generate UUIDs if the UUID cannot be used to compromise data and system * security. * * @protected * @return {*} {string} A randomly generated UUID * @memberof AbstractLogger */ protected uuidv4(): string { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { // eslint-disable-next-line no-bitwise const r = (Math.random() * 16) | 0, // eslint-disable-next-line no-bitwise, eqeqeq v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } /** * Captures all log record information as a LogObj object * * @protected * @param {LOG_LEVEL} logLevel Log Level of log record * @param {ERROR_CODE} errorCode Classification of error * @param {string} message Content of log record * @param {Date} [localTime=new Date()] Local time when log record was generated * @return {*} {LogObj} Wrapper object for log record * @memberof AbstractLogger */ protected generateLogObj(logLevel: LOG_LEVEL, errorCode: ERROR_CODE, message: string, localTime: Date = new Date()): LogObj { return new LogObj(errorCode, message, logLevel, this.logLevel, AbstractLogger.tabId, AbstractLogger.tabId, localTime); } /** * This method will sync the logLevel with account level log level of the logged in user. * User has to be logged in before calling this method. */ protected syncLogLevel(): void { if (AbstractLogger.ApiServiceUrl) { $.ajax({ headers: { 'Content-Type': 'application/json' }, url: `${AbstractLogger.ApiServiceUrl}/api/me/LogLevel`, xhrFields: { withCredentials: true }, type: 'GET', success: (logLevel: LOG_LEVEL) => { if (logLevel !== undefined && logLevel != null) { this._logLevel = logLevel; } } }); } } }