openvidu/openvidu-browser/src/OpenViduInternal/Logger/OpenViduLogger.ts

287 lines
8.5 KiB
TypeScript
Raw Normal View History

import { JL } from 'jsnlog'
import { OpenVidu } from "../../OpenVidu/OpenVidu";
import { ConsoleLogger } from './ConsoleLogger';
import { OpenViduLoggerConfiguration } from "./OpenViduLoggerConfiguration";
export class OpenViduLogger {
private static instance: OpenViduLogger;
private JSNLOG_URL: string = "/openvidu/elk/openvidu-browser-logs";
private MAX_JSNLOG_BATCH_LOG_MESSAGES: number = 100;
private MAX_MSECONDS_BATCH_MESSAGES: number = 5000;
private MAX_LENGTH_STRING_JSON: number = 1000;
private defaultConsoleLogger: ConsoleLogger = new ConsoleLogger(window.console);
private currentAppender: any;
private isProdMode = false;
private isJSNLogSetup = false;
// This two variables are used to restart JSNLog
// on different sessions and different userIds
private loggingSessionId: string | undefined;
/**
* @hidden
*/
static configureJSNLog(openVidu: OpenVidu, token: string) {
try {
// If dev mode
if ((window['LOG_JSNLOG_RESULTS']) ||
// If instance is created and it is OpenVidu Pro
(this.instance && openVidu.webrtcStatsInterval > -1
// If logs are enabled
&& this.instance.isOpenViduBrowserLogsDebugActive(openVidu)
// Only reconfigure it if session or finalUserId has changed
&& this.instance.canConfigureJSNLog(openVidu, this.instance))) {
// Check if app logs can be sent
// and replace console.log function to send
// logs of the application
if (openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debugApp) {
this.instance.replaceWindowConsole();
}
// isJSNLogSetup will not be true until completed setup
this.instance.isJSNLogSetup = false;
this.instance.info("Configuring JSNLogs.");
const finalUserId = openVidu.finalUserId;
const sessionId = openVidu.session.sessionId;
const beforeSendCallback = (xhr) => {
// If 401 or 403 or 404 modify ready and status so JSNLog don't retry to send logs
// https://github.com/mperdeck/jsnlog.js/blob/v2.30.0/jsnlog.ts#L805-L818
const parentReadyStateFunction = xhr.onreadystatechange;
xhr.onreadystatechange = () => {
console.log(xhr.status);
if (this.isInvalidResponse(xhr)) {
Object.defineProperty(xhr, "readyState", { value: 4 });
Object.defineProperty(xhr, "status", { value: 200 });
// Disable JSNLog too to not send periodically errors
this.instance.disableLogger();
}
parentReadyStateFunction();
}
// Headers to identify and authenticate logs
xhr.setRequestHeader('Authorization', "Basic " + btoa(`${finalUserId}%/%${sessionId}` + ":" + token));
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Additional headers for OpenVidu
xhr.setRequestHeader('OV-Final-User-Id', finalUserId);
2021-04-29 13:26:54 +02:00
xhr.setRequestHeader('OV-Session-Id', sessionId);
xhr.setRequestHeader('OV-Token', token);
}
// Creation of the appender.
this.instance.currentAppender = JL.createAjaxAppender(`appender-${finalUserId}-${sessionId}`);
this.instance.currentAppender.setOptions({
beforeSend: beforeSendCallback,
maxBatchSize: 1000,
batchSize: this.instance.MAX_JSNLOG_BATCH_LOG_MESSAGES,
batchTimeout: this.instance.MAX_MSECONDS_BATCH_MESSAGES
});
// Avoid circular dependencies
const logSerializer = (obj): string => {
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value != null) {
if (seen.has(value) || value instanceof HTMLElement) {
return;
}
seen.add(value);
}
return value;
};
};
// Cut long messages
let stringifyJson = JSON.stringify(obj, getCircularReplacer());
if (stringifyJson.length > this.instance.MAX_LENGTH_STRING_JSON) {
stringifyJson = `${stringifyJson.substring(0, this.instance.MAX_LENGTH_STRING_JSON)}...`;
}
if (window['LOG_JSNLOG_RESULTS']) {
console.log(stringifyJson);
}
return stringifyJson;
};
// Initialize JL to send logs
JL.setOptions({
defaultAjaxUrl: openVidu.httpUri + this.instance.JSNLOG_URL,
serialize: logSerializer,
enabled: true
});
JL().setOptions({
appenders: [this.instance.currentAppender]
});
this.instance.isJSNLogSetup = true;
this.instance.loggingSessionId = sessionId;
this.instance.info("JSNLog configured.");
}
} catch (e) {
// Print error
console.error("Error configuring JSNLog: ");
console.error(e);
// Restore defaults values just in case any exception happen-
this.instance.disableLogger();
}
}
/**
* @hidden
*/
static getInstance(): OpenViduLogger {
if (!OpenViduLogger.instance) {
OpenViduLogger.instance = new OpenViduLogger();
}
return OpenViduLogger.instance;
}
private static isInvalidResponse(xhr: XMLHttpRequest) {
return xhr.status == 401 || xhr.status == 403 || xhr.status == 404 || xhr.status == 0;
}
private canConfigureJSNLog(openVidu: OpenVidu, logger: OpenViduLogger): boolean {
return openVidu.session.sessionId != logger.loggingSessionId
}
private isOpenViduBrowserLogsDebugActive(openVidu: OpenVidu) {
return openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debug ||
openVidu.sendBrowserLogs === OpenViduLoggerConfiguration.debugApp;
}
// Return console functions with jsnlog integration
private getConsoleWithJSNLog() {
return function(openViduLogger: OpenViduLogger){
return {
log: function(...args){
openViduLogger.defaultConsoleLogger.log.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().info(arguments);
}
},
info: function (...args) {
openViduLogger.defaultConsoleLogger.info.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().info(arguments);
}
},
debug: function(...args) {
openViduLogger.defaultConsoleLogger.debug.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
},
warn: function (...args) {
openViduLogger.defaultConsoleLogger.warn.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().warn(arguments);
}
},
error: function (...args) {
openViduLogger.defaultConsoleLogger.error.apply(openViduLogger.defaultConsoleLogger.logger, arguments);
if (openViduLogger.isJSNLogSetup) {
JL().error(arguments);
}
}
};
}(this);
}
private replaceWindowConsole() {
window.console = this.defaultConsoleLogger.logger;
window.console.log = this.getConsoleWithJSNLog().log;
window.console.info = this.getConsoleWithJSNLog().info;
window.console.debug = this.getConsoleWithJSNLog().debug;
window.console.warn = this.getConsoleWithJSNLog().warn;
window.console.error = this.getConsoleWithJSNLog().error;
}
private disableLogger() {
JL.setOptions({enabled: false});
this.isJSNLogSetup = false;
this.loggingSessionId = undefined;
this.currentAppender = undefined;
window.console = this.defaultConsoleLogger.logger;
window.console.log = this.defaultConsoleLogger.log;
window.console.info = this.defaultConsoleLogger.info;
window.console.debug = this.defaultConsoleLogger.debug;
window.console.warn = this.defaultConsoleLogger.warn;
window.console.error = this.defaultConsoleLogger.error;
}
/**
* @hidden
*/
log(...args: any[]) {
if (!this.isProdMode) {
this.defaultConsoleLogger.log.apply(this.defaultConsoleLogger.logger, arguments);
}
if (this.isJSNLogSetup) {
JL().info(arguments);
}
}
/**
* @hidden
*/
debug(...args: any[]) {
if (!this.isProdMode) {
this.defaultConsoleLogger.debug.apply(this.defaultConsoleLogger.logger, arguments);
}
}
/**
* @hidden
*/
info(...args: any[]) {
if (!this.isProdMode) {
this.defaultConsoleLogger.info.apply(this.defaultConsoleLogger.logger, arguments);
}
if (this.isJSNLogSetup) {
JL().info(arguments);
}
}
/**
* @hidden
*/
warn(...args: any[]) {
if (!this.isProdMode) {
this.defaultConsoleLogger.warn.apply(this.defaultConsoleLogger.logger, arguments);
}
if (this.isJSNLogSetup) {
JL().warn(arguments);
}
}
/**
* @hidden
*/
error(...args: any[]) {
this.defaultConsoleLogger.error.apply(this.defaultConsoleLogger.logger, arguments);
if (this.isJSNLogSetup) {
JL().error(arguments);
}
}
/**
* @hidden
*/
flush() {
if (this.isJSNLogSetup && this.currentAppender != null) {
this.currentAppender.sendBatch();
}
}
enableProdMode() {
this.isProdMode = true;
}
}