Initial Commit

This commit is contained in:
2025-09-07 01:46:37 -04:00
commit 66986cca51
272 changed files with 15331 additions and 0 deletions

View 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>);
}
}

View 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;
}
}

View 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;
}

View 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;
}
}

View 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;
}
}

View 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();

View 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 */

View 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);
}
}
}

View 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;
}
}