Initial Commit
This commit is contained in:
26
src/services/logger/Appenders.ts
Normal file
26
src/services/logger/Appenders.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {IAppender} from './IAppender';
|
||||
|
||||
export default class Appenders {
|
||||
private _appenders: Array<IAppender> = [];
|
||||
|
||||
get value(): Array<IAppender> {
|
||||
return this._appenders;
|
||||
}
|
||||
|
||||
add(appender: IAppender): void {
|
||||
this._appenders.push(appender);
|
||||
}
|
||||
|
||||
remove(appender): void {
|
||||
this._appenders = this._appenders.reduce((items, item) => {
|
||||
if (!(item === appender)) {
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}, [] as Array<IAppender>);
|
||||
}
|
||||
}
|
||||
29
src/services/logger/ConsoleAppender.ts
Normal file
29
src/services/logger/ConsoleAppender.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {IAppender} from './IAppender';
|
||||
import {LoggingLevel} from './Logger';
|
||||
|
||||
export default class ConsoleAppender implements IAppender {
|
||||
private readonly _threshold: LoggingLevel;
|
||||
|
||||
log(logLevel: LoggingLevel, message: string, category: string, date: Date): void {
|
||||
if (logLevel < this._threshold) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fullMessage = `${date.toISOString()} [${category}] [${LoggingLevel[logLevel]}] ${message}`;
|
||||
|
||||
if (logLevel < LoggingLevel.Warn) {
|
||||
console.log(fullMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(fullMessage);
|
||||
}
|
||||
|
||||
constructor(threshold: LoggingLevel) {
|
||||
this._threshold = threshold;
|
||||
}
|
||||
}
|
||||
8
src/services/logger/IAppender.ts
Normal file
8
src/services/logger/IAppender.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {LoggingLevel} from './Logger';
|
||||
|
||||
export interface IAppender {
|
||||
log: (logLevel: LoggingLevel, message: string, category: string, date: Date) => void;
|
||||
}
|
||||
195
src/services/logger/Logger.ts
Normal file
195
src/services/logger/Logger.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import {IAppender} from './IAppender';
|
||||
import Appenders from './Appenders';
|
||||
import LoggingThreshold from './LoggingThreshold';
|
||||
|
||||
export enum LoggingLevel {
|
||||
All = -1,
|
||||
Trace = 10,
|
||||
Debug = 20,
|
||||
Info = 30,
|
||||
Warn = 40,
|
||||
Error = 50,
|
||||
Fatal = 60,
|
||||
Off = 100
|
||||
}
|
||||
|
||||
export type LoggingLevelType = 'Off' | 'Trace' | 'Debug' | 'Info' | 'Warn' | 'Error' | 'Fatal' | 'All';
|
||||
|
||||
export default class Logger {
|
||||
private readonly _category: string;
|
||||
private readonly _appenders: Appenders;
|
||||
private readonly _threshold: LoggingThreshold;
|
||||
|
||||
get category(): string {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
get appenders(): Appenders {
|
||||
return this._appenders;
|
||||
}
|
||||
|
||||
get threshold(): LoggingThreshold {
|
||||
return this._threshold;
|
||||
}
|
||||
|
||||
trace(...args: any): void {
|
||||
if (!this._threshold.value || this._threshold.value > LoggingLevel.Trace) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log(LoggingLevel.Trace, args);
|
||||
}
|
||||
|
||||
debug(...args: any): void {
|
||||
if (!this._threshold.value || this._threshold.value > LoggingLevel.Debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log(LoggingLevel.Debug, args);
|
||||
}
|
||||
|
||||
info(...args: any): void {
|
||||
if (!this._threshold.value || this._threshold.value > LoggingLevel.Info) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log(LoggingLevel.Info, args);
|
||||
}
|
||||
|
||||
warn(...args: any): void {
|
||||
if (!this._threshold.value || this._threshold.value > LoggingLevel.Warn) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log(LoggingLevel.Warn, args);
|
||||
}
|
||||
|
||||
error(...args: any): void {
|
||||
if (!this._threshold.value || this._threshold.value > LoggingLevel.Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log(LoggingLevel.Error, args);
|
||||
}
|
||||
|
||||
fatal(...args: any): void {
|
||||
if (!this._threshold.value || this._threshold.value > LoggingLevel.Fatal) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log(LoggingLevel.Fatal, args);
|
||||
}
|
||||
|
||||
private log(level: number, args: any): void {
|
||||
const date = new Date();
|
||||
const message = this.replacePlaceholders(args);
|
||||
|
||||
this._appenders.value.forEach((appender: IAppender) => {
|
||||
appender.log(level, message, this.category, date);
|
||||
});
|
||||
}
|
||||
|
||||
private replacePlaceholders(args: any): string {
|
||||
let replacePlaceholdersString = args[0];
|
||||
let index = 0;
|
||||
|
||||
while (replacePlaceholdersString.indexOf && args.length > 1 && index >= 0) {
|
||||
index = replacePlaceholdersString.indexOf('%', index);
|
||||
|
||||
if (index > 0) {
|
||||
const type = replacePlaceholdersString.substring(index + 1, index + 2);
|
||||
|
||||
switch (type) {
|
||||
case '%':
|
||||
// Escaped '%%' turns into '%'
|
||||
replacePlaceholdersString = replacePlaceholdersString.substring(0, index) + replacePlaceholdersString.substring(index + 1);
|
||||
index++;
|
||||
|
||||
break;
|
||||
case 's':
|
||||
case 'd':
|
||||
// Replace '%d' or '%s' with the argument
|
||||
args[0] = replacePlaceholdersString = this.replaceArgument(this.toString(args[1]), replacePlaceholdersString, index);
|
||||
args.splice(1, 1);
|
||||
|
||||
break;
|
||||
case 'j':
|
||||
// Replace %j' with the argument
|
||||
args[0] = replacePlaceholdersString = this.replaceArgument(this.stringify(args[1]), replacePlaceholdersString, index);
|
||||
|
||||
args.splice(1, 1);
|
||||
|
||||
break;
|
||||
default:
|
||||
return args.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.length > 1) {
|
||||
args = args.reduce((accumulator, currentValue, index, array) => {
|
||||
if (index + 1 === array.length && currentValue instanceof Error) {
|
||||
return accumulator + '\n' + this.toString(currentValue.stack);
|
||||
}
|
||||
|
||||
return accumulator + `[${this.toString(currentValue)}]`;
|
||||
});
|
||||
}
|
||||
|
||||
return args.toString();
|
||||
}
|
||||
|
||||
private stringify(arg: any): string {
|
||||
try {
|
||||
return JSON.stringify(arg instanceof Error ? this.toString(arg) : arg, null, 2);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
return '[object invalid JSON.stringify]';
|
||||
}
|
||||
}
|
||||
|
||||
private replaceArgument(argument: any, replacePlaceholdersString: string, index: number): string {
|
||||
return replacePlaceholdersString.substring(0, index) + this.toString(argument) + replacePlaceholdersString.substring(index + 2);
|
||||
}
|
||||
|
||||
private toString(data: any): string {
|
||||
if (typeof data === 'string') {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (typeof data === 'boolean') {
|
||||
return data ? 'true' : 'false';
|
||||
}
|
||||
|
||||
if (typeof data === 'number') {
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
let toStringStr = '';
|
||||
|
||||
if (data) {
|
||||
if (typeof data === 'function') {
|
||||
toStringStr = data.toString();
|
||||
} else if (data instanceof Object) {
|
||||
try {
|
||||
toStringStr = data.toString();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
toStringStr = '[object invalid toString()]';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return toStringStr;
|
||||
}
|
||||
|
||||
constructor(category: string, appenders: Appenders, threshold: LoggingThreshold) {
|
||||
this._category = category;
|
||||
this._appenders = appenders;
|
||||
this._threshold = threshold;
|
||||
}
|
||||
}
|
||||
51
src/services/logger/LoggerDefaults.ts
Normal file
51
src/services/logger/LoggerDefaults.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {LoggingLevel, LoggingLevelType} from '../logger/Logger';
|
||||
|
||||
declare const __FEATURES__: {
|
||||
sendLogs: LoggingLevelType;
|
||||
logToConsole: LoggingLevelType;
|
||||
};
|
||||
|
||||
export class BuildFeatures {
|
||||
private static _sendLogs: LoggingLevelType;
|
||||
private static _logToConsole: LoggingLevelType;
|
||||
|
||||
static get sendLogs(): LoggingLevelType {
|
||||
return this._sendLogs;
|
||||
}
|
||||
|
||||
static get logToConsole(): LoggingLevelType {
|
||||
return this._logToConsole;
|
||||
}
|
||||
|
||||
static applyFeatures(): void {
|
||||
try {
|
||||
const features = __FEATURES__;
|
||||
|
||||
this._sendLogs = 'sendLogs' in features ? features.sendLogs : 'All';
|
||||
this._logToConsole = 'logToConsole' in features ? features.logToConsole : 'All';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
this._sendLogs = 'All';
|
||||
this._logToConsole = 'All';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BuildFeatures.applyFeatures();
|
||||
|
||||
export default class LoggerDefaults {
|
||||
static get defaultLoggingLevel(): LoggingLevel {
|
||||
return LoggingLevel[BuildFeatures.sendLogs];
|
||||
}
|
||||
|
||||
static get defaultConsoleLoggingLevel(): LoggingLevel {
|
||||
return LoggingLevel[BuildFeatures.logToConsole];
|
||||
}
|
||||
|
||||
static get defaultTelemetryLoggingLevel(): LoggingLevel {
|
||||
return LoggingLevel.Info;
|
||||
}
|
||||
}
|
||||
78
src/services/logger/LoggerFactory.ts
Normal file
78
src/services/logger/LoggerFactory.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
// import TelemetryUrl from '/telemetry/TelemetryUrl';
|
||||
import PlatformDetectionService from '../PlatformDetection.service';
|
||||
import TelemetryConfiguration from '../telemetry/TelemetryConfiguration';
|
||||
import TelemetryAppender from '../telemetry/TelemetryApender';
|
||||
// import hostService from 'services/host-url.service';
|
||||
// import userStore from 'services/user-store';
|
||||
import ILogger from './LoggerInterface';
|
||||
import Logger, {LoggingLevel} from './Logger';
|
||||
import Appenders from './Appenders';
|
||||
import LoggingThreshold from './LoggingThreshold';
|
||||
import ConsoleAppender from './ConsoleAppender';
|
||||
import LoggerDefaults from './LoggerDefaults';
|
||||
|
||||
export default class LoggerFactory {
|
||||
private static _loggers: {[category: string]: ILogger} = {};
|
||||
private static _appenders: Appenders = new Appenders();
|
||||
private static _threshold: LoggingThreshold = new LoggingThreshold();
|
||||
private static _telemetryConfiguration: TelemetryConfiguration = new TelemetryConfiguration();
|
||||
|
||||
static get telemetryConfiguration(): TelemetryConfiguration {
|
||||
return this._telemetryConfiguration;
|
||||
}
|
||||
|
||||
static applyLoggerConfig(): void {
|
||||
LoggerFactory.applyConsoleLogger(LoggingLevel['All']);
|
||||
LoggerFactory.applyLoggingLevel();
|
||||
LoggerFactory.applyTelemetryLogger();
|
||||
}
|
||||
|
||||
static getLogger(category: string): ILogger {
|
||||
if (typeof category !== 'string') {
|
||||
category = 'portal';
|
||||
}
|
||||
|
||||
const logger = LoggerFactory._loggers[category];
|
||||
|
||||
if (logger) {
|
||||
return logger;
|
||||
}
|
||||
|
||||
return (LoggerFactory._loggers[category] = new Logger(category, this._appenders, this._threshold));
|
||||
}
|
||||
|
||||
static applyLoggingLevel(): void {
|
||||
this._threshold.setThreshold(LoggingLevel['All']);
|
||||
}
|
||||
|
||||
static applyConsoleLogger(level: LoggingLevel): void {
|
||||
this._appenders.add(new ConsoleAppender(level || LoggerDefaults.defaultConsoleLoggingLevel));
|
||||
}
|
||||
|
||||
static async applyTelemetryConfiguration(level: LoggingLevel): Promise<void> {
|
||||
const browser = PlatformDetectionService.browser;
|
||||
const applicationId = 'phenixrts.com-alex.zinn'; // TEMPORARY --> FOR DEVELOPMENT ONLY
|
||||
this._telemetryConfiguration.threshold = level || LoggerDefaults.defaultTelemetryLoggingLevel;
|
||||
this._telemetryConfiguration.url = 'https://pcast-stg.phenixrts.com/telemetry'; //TelemetryUrl.getTelemetryUrl();
|
||||
this._telemetryConfiguration.environment = 'https://pcast-stg.phenixrts.com'; // TODO(AZ): hostService.getHostUrl();
|
||||
this._telemetryConfiguration.tenancy = applicationId; // TODO(AZ): await userStore.get('applicationId');
|
||||
this._telemetryConfiguration.userId = applicationId;
|
||||
this._telemetryConfiguration.sessionId = 'some-session-id'; // TODOD(AZ): await userStore.get('sessionId');
|
||||
this._telemetryConfiguration.browser = browser ? `${browser}/${PlatformDetectionService.version}` : 'unknown';
|
||||
}
|
||||
|
||||
private static applyTelemetryLogger(): void {
|
||||
LoggerFactory.applyTelemetryConfiguration(LoggingLevel['Info']);
|
||||
|
||||
this._appenders.add(new TelemetryAppender(this._telemetryConfiguration));
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
throw new Error('LoggerFactory is a static class that may not be instantiated');
|
||||
}
|
||||
}
|
||||
|
||||
LoggerFactory.applyLoggerConfig();
|
||||
27
src/services/logger/LoggerInterface.ts
Normal file
27
src/services/logger/LoggerInterface.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import Appenders from './Appenders';
|
||||
import LoggingThreshold from './LoggingThreshold';
|
||||
|
||||
export default interface ILogger {
|
||||
readonly category: string;
|
||||
|
||||
readonly appenders: Appenders;
|
||||
|
||||
readonly threshold: LoggingThreshold;
|
||||
|
||||
trace: (...args: any) => void;
|
||||
|
||||
debug: (...args: any) => void;
|
||||
|
||||
info: (...args: any) => void;
|
||||
|
||||
warn: (...args: any) => void;
|
||||
|
||||
error: (...args: any) => void;
|
||||
|
||||
fatal: (...args: any) => void;
|
||||
}
|
||||
/* eslint-enable */
|
||||
56
src/services/logger/LoggingLevelMapping.ts
Normal file
56
src/services/logger/LoggingLevelMapping.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import {LoggingLevel, LoggingLevelType} from './Logger';
|
||||
|
||||
function assertUnreachable(x: never): never {
|
||||
throw new Error(`Unexpected value [${x}]. This should never be reached`);
|
||||
}
|
||||
|
||||
export default class LoggingLevelMapping {
|
||||
static convertLoggingLevelToLoggingLevelType(loggingLevel: LoggingLevel): LoggingLevelType {
|
||||
switch (loggingLevel) {
|
||||
case LoggingLevel.Off:
|
||||
return 'Off';
|
||||
case LoggingLevel.Trace:
|
||||
return 'Trace';
|
||||
case LoggingLevel.Debug:
|
||||
return 'Debug';
|
||||
case LoggingLevel.Info:
|
||||
return 'Trace';
|
||||
case LoggingLevel.Warn:
|
||||
return 'Warn';
|
||||
case LoggingLevel.Error:
|
||||
return 'Error';
|
||||
case LoggingLevel.Fatal:
|
||||
return 'Fatal';
|
||||
case LoggingLevel.All:
|
||||
return 'All';
|
||||
default:
|
||||
assertUnreachable(loggingLevel);
|
||||
}
|
||||
}
|
||||
|
||||
static convertLoggingLevelTypeToLoggingLevel(loggingLevelType: LoggingLevelType): LoggingLevel {
|
||||
switch (loggingLevelType) {
|
||||
case 'Off':
|
||||
return LoggingLevel.Off;
|
||||
case 'Trace':
|
||||
return LoggingLevel.Trace;
|
||||
case 'Debug':
|
||||
return LoggingLevel.Debug;
|
||||
case 'Info':
|
||||
return LoggingLevel.Info;
|
||||
case 'Warn':
|
||||
return LoggingLevel.Warn;
|
||||
case 'Error':
|
||||
return LoggingLevel.Error;
|
||||
case 'Fatal':
|
||||
return LoggingLevel.Fatal;
|
||||
case 'All':
|
||||
return LoggingLevel.All;
|
||||
default:
|
||||
assertUnreachable(loggingLevelType);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/services/logger/LoggingThreshold.ts
Normal file
17
src/services/logger/LoggingThreshold.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
|
||||
*/
|
||||
import LoggerDefaults from './LoggerDefaults';
|
||||
import {LoggingLevel} from './Logger';
|
||||
|
||||
export default class LoggingThreshold {
|
||||
private _threshold: LoggingLevel = LoggerDefaults.defaultLoggingLevel;
|
||||
|
||||
get value(): LoggingLevel {
|
||||
return this._threshold;
|
||||
}
|
||||
|
||||
setThreshold(threshold: LoggingLevel): void {
|
||||
this._threshold = threshold;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user