UNPKG

ngx-logger

Version:

[![npm version](https://badge.fury.io/js/ngx-logger.svg)](https://www.npmjs.com/package/ngx-logger)

1,060 lines (1,037 loc) 50.2 kB
import * as i1 from '@angular/common/http'; import { HttpRequest, HttpResponse, HttpHeaders, HttpParams } from '@angular/common/http'; import * as i0 from '@angular/core'; import { Injectable, Optional, PLATFORM_ID, Inject, NgModule } from '@angular/core'; import { of, BehaviorSubject, isObservable, throwError, timer } from 'rxjs'; import { filter, map, retry, shareReplay, catchError, concatMap, take } from 'rxjs/operators'; import * as vlq from 'vlq'; import * as i1$1 from '@angular/common'; import { isPlatformBrowser, CommonModule } from '@angular/common'; /** * Injection token of logger config */ const TOKEN_LOGGER_CONFIG = 'TOKEN_LOGGER_CONFIG'; class NGXLoggerConfigEngine { constructor(config) { this.config = this._clone(config); } /** Get a readonly access to the level configured for the NGXLogger */ get level() { return this.config.level; } /** Get a readonly access to the serverLogLevel configured for the NGXLogger */ get serverLogLevel() { return this.config.serverLogLevel; } updateConfig(config) { this.config = this._clone(config); } /** Update the config partially * This is useful if you want to update only one parameter of the config */ partialUpdateConfig(partialConfig) { // avoid any error if the config is incorrect if (!partialConfig) { return; } Object.keys(partialConfig).forEach(configParamKey => { this.config[configParamKey] = partialConfig[configParamKey]; }); } getConfig() { return this._clone(this.config); } // TODO: This is a shallow clone, If the config ever becomes hierarchical we must make this a deep clone _clone(object) { const cloneConfig = { level: null }; Object.keys(object).forEach((key) => { cloneConfig[key] = object[key]; }); return cloneConfig; } } /** * Injection token of logger config engine factory */ const TOKEN_LOGGER_CONFIG_ENGINE_FACTORY = 'TOKEN_LOGGER_CONFIG_ENGINE_FACTORY'; class NGXLoggerConfigEngineFactory { provideConfigEngine(config) { return new NGXLoggerConfigEngine(config); } } /** * Injection token of logger mapper service */ const TOKEN_LOGGER_MAPPER_SERVICE = 'TOKEN_LOGGER_MAPPER_SERVICE'; class NGXLoggerMapperService { constructor(httpBackend) { this.httpBackend = httpBackend; /** cache for source maps, key is source map location, ie. 'http://localhost:4200/main.js.map' */ this.sourceMapCache = new Map(); /** cache for specific log position, key is the dist position, ie 'main.js:339:21' */ this.logPositionCache = new Map(); } /** * Returns the log position of the caller * If sourceMaps are enabled, it attemps to get the source map from the server, and use that to parse the position * @param config * @param metadata * @returns */ getLogPosition(config, metadata) { const stackLine = this.getStackLine(config); // if we were not able to parse the stackLine, just return an empty Log Position if (!stackLine) { return of({ fileName: '', lineNumber: 0, columnNumber: 0 }); } const logPosition = this.getLocalPosition(stackLine); if (!config.enableSourceMaps) { return of(logPosition); } const sourceMapLocation = this.getSourceMapLocation(stackLine); return this.getSourceMap(sourceMapLocation, logPosition); } /** * Get the stackline of the original caller * @param config * @returns null if stackline was not found */ getStackLine(config) { const error = new Error(); try { // noinspection ExceptionCaughtLocallyJS throw error; } catch (e) { try { // Here are different examples of stacktrace // Firefox (last line is the user code, the 4 first are ours): // getStackLine@http://localhost:4200/main.js:358:23 // getCallerDetails@http://localhost:4200/main.js:557:44 // _log@http://localhost:4200/main.js:830:28 // debug@http://localhost:4200/main.js:652:14 // handleLog@http://localhost:4200/main.js:1158:29 // Chrome and Edge (last line is the user code): // Error // at Function.getStackLine (ngx-logger.js:329) // at NGXMapperService.getCallerDetails (ngx-logger.js:528) // at NGXLogger._log (ngx-logger.js:801) // at NGXLogger.info (ngx-logger.js:631) // at AppComponent.handleLog (app.component.ts:38) let defaultProxy = 4; // We make 4 functions call before getting here const firstStackLine = error.stack.split('\n')[0]; if (!firstStackLine.includes('.js:')) { // The stacktrace starts with no function call (example in Chrome or Edge) defaultProxy = defaultProxy + 1; } return error.stack.split('\n')[(defaultProxy + (config.proxiedSteps || 0))]; } catch (e) { return null; } } } /** * Get position of caller without using sourceMaps * @param stackLine * @returns */ getLocalPosition(stackLine) { // strip base path, then parse filename, line, and column, stackline looks like this : // Firefox // handleLog@http://localhost:4200/main.js:1158:29 // Chrome and Edge // at AppComponent.handleLog (app.component.ts:38) const positionStartIndex = stackLine.lastIndexOf('\/'); let positionEndIndex = stackLine.indexOf(')'); if (positionEndIndex < 0) { positionEndIndex = undefined; } const position = stackLine.substring(positionStartIndex + 1, positionEndIndex); const dataArray = position.split(':'); if (dataArray.length === 3) { return { fileName: dataArray[0], lineNumber: +dataArray[1], columnNumber: +dataArray[2] }; } return { fileName: 'unknown', lineNumber: 0, columnNumber: 0 }; } getTranspileLocation(stackLine) { // Example stackLine: // Firefox : getStackLine@http://localhost:4200/main.js:358:23 // Chrome and Edge : at Function.getStackLine (ngx-logger.js:329) let locationStartIndex = stackLine.indexOf('('); if (locationStartIndex < 0) { locationStartIndex = stackLine.lastIndexOf('@'); if (locationStartIndex < 0) { locationStartIndex = stackLine.lastIndexOf(' '); } } let locationEndIndex = stackLine.indexOf(')'); if (locationEndIndex < 0) { locationEndIndex = undefined; } return stackLine.substring(locationStartIndex + 1, locationEndIndex); } /** * Gets the URL of the sourcemap (the URL can be relative or absolute, it is browser dependant) * @param stackLine * @returns */ getSourceMapLocation(stackLine) { const file = this.getTranspileLocation(stackLine); const mapFullPath = file.substring(0, file.lastIndexOf(':')); return mapFullPath.substring(0, mapFullPath.lastIndexOf(':')) + '.map'; } getMapping(sourceMap, position) { // => ';' indicates end of a line // => ',' separates mappings in a line // decoded mapping => [ generatedCodeColumn, sourceFileIndex, sourceCodeLine, sourceCodeColumn, nameIndex ] let sourceFileIndex = 0, // second field sourceCodeLine = 0, // third field sourceCodeColumn = 0; // fourth field const lines = sourceMap.mappings.split(';'); for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { // reset column position to 0 after each line let generatedCodeColumn = 0; // decode sections in line const columns = lines[lineIndex].split(','); for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) { const decodedSection = vlq.decode(columns[columnIndex]); if (decodedSection.length >= 4) { // update relative positions generatedCodeColumn += decodedSection[0]; sourceFileIndex += decodedSection[1]; sourceCodeLine += decodedSection[2]; sourceCodeColumn += decodedSection[3]; } // check if matching map if (lineIndex === position.lineNumber) { if (generatedCodeColumn === position.columnNumber) { // matching column and line found return { fileName: sourceMap.sources[sourceFileIndex], lineNumber: sourceCodeLine, columnNumber: sourceCodeColumn }; } else if (columnIndex + 1 === columns.length) { // matching column not found, but line is correct return { fileName: sourceMap.sources[sourceFileIndex], lineNumber: sourceCodeLine, columnNumber: 0 }; } } } } // failed if reached return { fileName: 'unknown', lineNumber: 0, columnNumber: 0 }; } /** * does the http get request to get the source map * @param sourceMapLocation * @param distPosition */ getSourceMap(sourceMapLocation, distPosition) { const req = new HttpRequest('GET', sourceMapLocation); const distPositionKey = `${distPosition.fileName}:${distPosition.lineNumber}:${distPosition.columnNumber}`; // if the specific log position is already in cache return it if (this.logPositionCache.has(distPositionKey)) { return this.logPositionCache.get(distPositionKey); } // otherwise check if the source map is already cached for given source map location if (!this.sourceMapCache.has(sourceMapLocation)) { if (!this.httpBackend) { console.error('NGXLogger : Can\'t get sourcemap because HttpBackend is not provided. You need to import HttpClientModule'); this.sourceMapCache.set(sourceMapLocation, of(null)); } else { // obtain the source map if not cached this.sourceMapCache.set(sourceMapLocation, this.httpBackend.handle(req).pipe(filter((e) => e instanceof HttpResponse), map((httpResponse) => httpResponse.body), retry(3), shareReplay(1))); } } // at this point the source map is cached, use it to get specific log position mapping const logPosition$ = this.sourceMapCache.get(sourceMapLocation).pipe(map((sourceMap) => { // sourceMap can be null if HttpBackend is not provided for example if (!sourceMap) { return distPosition; } // map generated position to source position return this.getMapping(sourceMap, distPosition); }), catchError(() => of(distPosition)), shareReplay(1)); // store specific log position in cache for given dest position and return it this.logPositionCache.set(distPositionKey, logPosition$); return logPosition$; } } /** @nocollapse */ /** @nocollapse */ NGXLoggerMapperService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerMapperService, deps: [{ token: i1.HttpBackend, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ NGXLoggerMapperService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerMapperService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerMapperService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.HttpBackend, decorators: [{ type: Optional }] }]; } }); /** * Injection token of logger metadata service */ const TOKEN_LOGGER_METADATA_SERVICE = 'TOKEN_LOGGER_METADATA_SERVICE'; class NGXLoggerMetadataService { constructor(datePipe) { this.datePipe = datePipe; } computeTimestamp(config) { const defaultTimestamp = () => new Date().toISOString(); if (config.timestampFormat) { if (!this.datePipe) { console.error('NGXLogger : Can\'t use timeStampFormat because DatePipe is not provided. You need to provide DatePipe'); return defaultTimestamp(); } else { return this.datePipe.transform(new Date(), config.timestampFormat); } } return defaultTimestamp(); } getMetadata(level, config, message, additional) { const metadata = { level: level, additional: additional, }; // The user can send a function // This is useful in order to compute string concatenation only when the log will actually be written if (message && typeof message === 'function') { metadata.message = message(); } else { metadata.message = message; } metadata.timestamp = this.computeTimestamp(config); return metadata; } } /** @nocollapse */ /** @nocollapse */ NGXLoggerMetadataService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerMetadataService, deps: [{ token: i1$1.DatePipe, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ NGXLoggerMetadataService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerMetadataService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerMetadataService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1$1.DatePipe, decorators: [{ type: Optional }] }]; } }); // I kept this class alive only to avoid a breaking change with the old version // This class does not implement anything so it is useless and the interface is enough /** * @deprecated this class does not implement anything thus being useless, you should rather implements @see INGXLoggerMonitor */ class NGXLoggerMonitor { } /** * Injection token of logger metadata service */ const TOKEN_LOGGER_RULES_SERVICE = 'TOKEN_LOGGER_RULES_SERVICE'; class NGXLoggerRulesService { shouldCallWriter(level, config, message, additional) { return !config.disableConsoleLogging && level >= config.level; } shouldCallServer(level, config, message, additional) { return !!config.serverLoggingUrl && level >= config.serverLogLevel; } shouldCallMonitor(level, config, message, additional) { // The default behavior is to call the monitor only if the writer or the server is called return this.shouldCallWriter(level, config, message, additional) || this.shouldCallServer(level, config, message, additional); } } /** @nocollapse */ /** @nocollapse */ NGXLoggerRulesService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerRulesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ NGXLoggerRulesService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerRulesService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerRulesService, decorators: [{ type: Injectable }] }); /** * Injection token of logger server service */ const TOKEN_LOGGER_SERVER_SERVICE = 'TOKEN_LOGGER_SERVER_SERVICE'; class NGXLoggerServerService { constructor(httpBackend, ngZone) { this.httpBackend = httpBackend; this.ngZone = ngZone; this.serverCallsQueue = []; this.flushingQueue = new BehaviorSubject(false); } ngOnDestroy() { if (this.flushingQueue) { this.flushingQueue.complete(); this.flushingQueue = null; } if (this.addToQueueTimer) { this.addToQueueTimer.unsubscribe(); this.addToQueueTimer = null; } } /** * Transforms an error object into a readable string (taking only the stack) * This is needed because JSON.stringify would return "{}" * @param err the error object * @returns The stack of the error */ secureErrorObject(err) { return err?.stack; } /** * Transforms the additional parameters to avoid any json error when sending the data to the server * Basically it just replaces unstringifiable object to a string mentioning an error * @param additional The additional data to be sent * @returns The additional data secured */ secureAdditionalParameters(additional) { if (additional === null || additional === undefined) { return null; } return additional.map((next, idx) => { try { if (next instanceof Error) { return this.secureErrorObject(next); } // We just want to make sure the JSON can be parsed, we do not want to actually change the type if (typeof next === 'object') { JSON.stringify(next); } return next; } catch (e) { return `The additional[${idx}] value could not be parsed using JSON.stringify().`; } }); } /** * Transforms the message so that it can be sent to the server * @param message the message to be sent * @returns the message secured */ secureMessage(message) { try { if (message instanceof Error) { return this.secureErrorObject(message); } if (typeof message !== 'string') { message = JSON.stringify(message, null, 2); } } catch (e) { message = 'The provided "message" value could not be parsed with JSON.stringify().'; } return message; } /** * Edits HttpRequest object before sending request to server * @param httpRequest default request object * @returns altered httprequest */ alterHttpRequest(httpRequest) { return httpRequest; } /** * Sends request to server * @param url * @param logContent * @param options * @returns */ logOnServer(url, logContent, options) { if (!this.httpBackend) { console.error('NGXLogger : Can\'t log on server because HttpBackend is not provided. You need to import HttpClientModule'); return of(null); } // HttpBackend skips all HttpInterceptors // They may log errors using this service causing circular calls let defaultRequest = new HttpRequest('POST', url, logContent, options || {}); let finalRequest = of(defaultRequest); const alteredRequest = this.alterHttpRequest(defaultRequest); if (isObservable(alteredRequest)) { finalRequest = alteredRequest; } else if (alteredRequest) { finalRequest = of(alteredRequest); } else { console.warn('NGXLogger : alterHttpRequest returned an invalid request. Using default one instead'); } return finalRequest.pipe(concatMap(req => { if (!req) { console.warn('NGXLogger : alterHttpRequest returned an invalid request (observable). Using default one instead'); return this.httpBackend.handle(defaultRequest); } return this.httpBackend.handle(req); }), filter(e => e instanceof HttpResponse), map((httpResponse) => httpResponse.body)); } /** * Customise the data sent to the API * @param metadata the data provided by NGXLogger * @returns the data that will be sent to the API in the body */ customiseRequestBody(metadata) { // In our API the body is not customised return metadata; } /** * Flush the queue of the logger * @param config */ flushQueue(config) { this.flushingQueue.next(true); // If a timer was set, we cancel it because the queue is flushed if (this.addToQueueTimer) { this.addToQueueTimer.unsubscribe(); this.addToQueueTimer = null; } if (!!this.serverCallsQueue && this.serverCallsQueue.length > 0) { this.sendToServerAction(this.serverCallsQueue, config); } this.serverCallsQueue = []; this.flushingQueue.next(false); } sendToServerAction(metadata, config) { let requestBody; const secureMetadata = (pMetadata) => { // Copying metadata locally because we don't want to change the object for the caller const securedMetadata = { ...pMetadata }; securedMetadata.additional = this.secureAdditionalParameters(securedMetadata.additional); securedMetadata.message = this.secureMessage(securedMetadata.message); return securedMetadata; }; if (Array.isArray(metadata)) { requestBody = []; metadata.forEach(m => { requestBody.push(secureMetadata(m)); }); } else { requestBody = secureMetadata(metadata); } // Allow users to customise the data sent to the API requestBody = this.customiseRequestBody(requestBody); const headers = config.customHttpHeaders || new HttpHeaders(); if (!headers.has('Content-Type')) { headers.set('Content-Type', 'application/json'); } const logOnServerAction = () => { this.logOnServer(config.serverLoggingUrl, requestBody, { headers, params: config.customHttpParams || new HttpParams(), responseType: config.httpResponseType || 'json', withCredentials: config.withCredentials || false, }).pipe(catchError(err => { // Do not use NGXLogger here because this could cause an infinite loop console.error('NGXLogger: Failed to log on server', err); return throwError(err); })).subscribe(); }; if (config.serverCallsOutsideNgZone === true) { if (!this.ngZone) { console.error('NGXLogger: NgZone is not provided and serverCallsOutsideNgZone is set to true'); return; } this.ngZone.runOutsideAngular(logOnServerAction); } else { logOnServerAction(); } } /** * Sends the content to be logged to the server according to the config * @param metadata * @param config */ sendToServer(metadata, config) { // If there is no batch mode in the config, we send the log call straight to the server as usual if ((!config.serverCallsBatchSize || config.serverCallsBatchSize <= 0) && (!config.serverCallsTimer || config.serverCallsTimer <= 0)) { this.sendToServerAction(metadata, config); return; } const addLogToQueueAction = () => { this.serverCallsQueue.push({ ...metadata }); // Flush queue when size is reached if (!!config.serverCallsBatchSize && this.serverCallsQueue.length > config.serverCallsBatchSize) { this.flushQueue(config); } // Call timer only if it is in the config and timer is not already running if (config.serverCallsTimer > 0 && !this.addToQueueTimer) { this.addToQueueTimer = timer(config.serverCallsTimer).subscribe(_ => { this.flushQueue(config); }); } }; // If queue is being flushed, we need to wait for it to finish before adding other calls if (this.flushingQueue.value === true) { this.flushingQueue.pipe(filter(fq => fq === false), take(1)).subscribe(_ => { addLogToQueueAction(); }); } else { addLogToQueueAction(); } } } /** @nocollapse */ /** @nocollapse */ NGXLoggerServerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerServerService, deps: [{ token: i1.HttpBackend, optional: true }, { token: i0.NgZone, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ NGXLoggerServerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerServerService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerServerService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.HttpBackend, decorators: [{ type: Optional }] }, { type: i0.NgZone, decorators: [{ type: Optional }] }]; } }); /** * Injection token of logger writer service */ const TOKEN_LOGGER_WRITER_SERVICE = 'TOKEN_LOGGER_WRITER_SERVICE'; var NgxLoggerLevel; (function (NgxLoggerLevel) { NgxLoggerLevel[NgxLoggerLevel["TRACE"] = 0] = "TRACE"; NgxLoggerLevel[NgxLoggerLevel["DEBUG"] = 1] = "DEBUG"; NgxLoggerLevel[NgxLoggerLevel["INFO"] = 2] = "INFO"; NgxLoggerLevel[NgxLoggerLevel["LOG"] = 3] = "LOG"; NgxLoggerLevel[NgxLoggerLevel["WARN"] = 4] = "WARN"; NgxLoggerLevel[NgxLoggerLevel["ERROR"] = 5] = "ERROR"; NgxLoggerLevel[NgxLoggerLevel["FATAL"] = 6] = "FATAL"; NgxLoggerLevel[NgxLoggerLevel["OFF"] = 7] = "OFF"; })(NgxLoggerLevel || (NgxLoggerLevel = {})); const DEFAULT_COLOR_SCHEME = [ 'purple', 'teal', 'gray', 'gray', 'red', 'red', 'red' ]; class NGXLoggerWriterService { constructor(platformId) { this.platformId = platformId; /** List of functions called when preparing meta string */ this.prepareMetaStringFuncs = [ this.getTimestampToWrite, this.getLevelToWrite, this.getFileDetailsToWrite, this.getContextToWrite, ]; this.isIE = isPlatformBrowser(platformId) && navigator && navigator.userAgent && !!(navigator.userAgent.indexOf('MSIE') !== -1 || navigator.userAgent.match(/Trident\//) || navigator.userAgent.match(/Edge\//)); this.logFunc = this.isIE ? this.logIE.bind(this) : this.logModern.bind(this); } getTimestampToWrite(metadata, config) { return metadata.timestamp; } getLevelToWrite(metadata, config) { return NgxLoggerLevel[metadata.level]; } getFileDetailsToWrite(metadata, config) { return config.disableFileDetails === true ? '' : `[${metadata.fileName}:${metadata.lineNumber}:${metadata.columnNumber}]`; } getContextToWrite(metadata, config) { return config.context ? `{${config.context}}` : ''; } /** Generate a "meta" string that is displayed before the content sent to the log function */ prepareMetaString(metadata, config) { let metaString = ''; this.prepareMetaStringFuncs.forEach(prepareMetaStringFunc => { const metaItem = prepareMetaStringFunc(metadata, config); if (metaItem) { metaString = metaString + ' ' + metaItem; } }); return metaString.trim(); } /** Get the color to use when writing to console */ getColor(metadata, config) { const configColorScheme = config.colorScheme ?? DEFAULT_COLOR_SCHEME; // this is needed to avoid a build error if (metadata.level === NgxLoggerLevel.OFF) { return undefined; } return configColorScheme[metadata.level]; } /** Log to the console specifically for IE */ logIE(metadata, config, metaString) { // Coloring doesn't work in IE // make sure additional isn't null or undefined so that ...additional doesn't error const additional = metadata.additional || []; switch (metadata.level) { case NgxLoggerLevel.WARN: console.warn(`${metaString} `, metadata.message, ...additional); break; case NgxLoggerLevel.ERROR: case NgxLoggerLevel.FATAL: console.error(`${metaString} `, metadata.message, ...additional); break; case NgxLoggerLevel.INFO: console.info(`${metaString} `, metadata.message, ...additional); break; default: console.log(`${metaString} `, metadata.message, ...additional); } } /** Log to the console */ logModern(metadata, config, metaString) { const color = this.getColor(metadata, config); // make sure additional isn't null or undefined so that ...additional doesn't error const additional = metadata.additional || []; switch (metadata.level) { case NgxLoggerLevel.WARN: console.warn(`%c${metaString}`, `color:${color}`, metadata.message, ...additional); break; case NgxLoggerLevel.ERROR: case NgxLoggerLevel.FATAL: console.error(`%c${metaString}`, `color:${color}`, metadata.message, ...additional); break; case NgxLoggerLevel.INFO: console.info(`%c${metaString}`, `color:${color}`, metadata.message, ...additional); break; // Disabling console.trace since the stack trace is not helpful. it is showing the stack trace of // the console.trace statement // case NgxLoggerLevel.TRACE: // console.trace(`%c${metaString}`, `color:${color}`, message, ...additional); // break; case NgxLoggerLevel.DEBUG: console.debug(`%c${metaString}`, `color:${color}`, metadata.message, ...additional); break; default: console.log(`%c${metaString}`, `color:${color}`, metadata.message, ...additional); } } /** Write the content sent to the log function to the console */ writeMessage(metadata, config) { const metaString = this.prepareMetaString(metadata, config); this.logFunc(metadata, config, metaString); } } /** @nocollapse */ /** @nocollapse */ NGXLoggerWriterService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerWriterService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ NGXLoggerWriterService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerWriterService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLoggerWriterService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }]; } }); class NGXLogger { constructor(config, configEngineFactory, metadataService, ruleService, mapperService, writerService, serverService) { this.metadataService = metadataService; this.ruleService = ruleService; this.mapperService = mapperService; this.writerService = writerService; this.serverService = serverService; this.configEngine = configEngineFactory.provideConfigEngine(config); } /** Get a readonly access to the level configured for the NGXLogger */ get level() { return this.configEngine.level; } /** Get a readonly access to the serverLogLevel configured for the NGXLogger */ get serverLogLevel() { return this.configEngine.serverLogLevel; } trace(message, ...additional) { this._log(NgxLoggerLevel.TRACE, message, additional); } debug(message, ...additional) { this._log(NgxLoggerLevel.DEBUG, message, additional); } info(message, ...additional) { this._log(NgxLoggerLevel.INFO, message, additional); } log(message, ...additional) { this._log(NgxLoggerLevel.LOG, message, additional); } warn(message, ...additional) { this._log(NgxLoggerLevel.WARN, message, additional); } error(message, ...additional) { this._log(NgxLoggerLevel.ERROR, message, additional); } fatal(message, ...additional) { this._log(NgxLoggerLevel.FATAL, message, additional); } /** @deprecated customHttpHeaders is now part of the config, this should be updated via @see updateConfig */ setCustomHttpHeaders(headers) { const config = this.getConfigSnapshot(); config.customHttpHeaders = headers; this.updateConfig(config); } /** @deprecated customHttpParams is now part of the config, this should be updated via @see updateConfig */ setCustomParams(params) { const config = this.getConfigSnapshot(); config.customHttpParams = params; this.updateConfig(config); } /** @deprecated withCredentials is now part of the config, this should be updated via @see updateConfig */ setWithCredentialsOptionValue(withCredentials) { const config = this.getConfigSnapshot(); config.withCredentials = withCredentials; this.updateConfig(config); } /** * Register a INGXLoggerMonitor that will be trigger when a log is either written or sent to server * * There is only one monitor, registering one will overwrite the last one if there was one * @param monitor */ registerMonitor(monitor) { this._loggerMonitor = monitor; } /** Set config of logger * * Warning : This overwrites all the config, if you want to update only one property, you should use @see getConfigSnapshot before */ updateConfig(config) { this.configEngine.updateConfig(config); } partialUpdateConfig(partialConfig) { this.configEngine.partialUpdateConfig(partialConfig); } /** Get config of logger */ getConfigSnapshot() { return this.configEngine.getConfig(); } /** * Flush the serveur queue */ flushServerQueue() { this.serverService.flushQueue(this.getConfigSnapshot()); } _log(level, message, additional = []) { const config = this.configEngine.getConfig(); const shouldCallWriter = this.ruleService.shouldCallWriter(level, config, message, additional); const shouldCallServer = this.ruleService.shouldCallServer(level, config, message, additional); const shouldCallMonitor = this.ruleService.shouldCallMonitor(level, config, message, additional); if (!shouldCallWriter && !shouldCallServer && !shouldCallMonitor) { // If nothing is to be called we return return; } const metadata = this.metadataService.getMetadata(level, config, message, additional); this.mapperService.getLogPosition(config, metadata).pipe(take(1)).subscribe(logPosition => { if (logPosition) { metadata.fileName = logPosition.fileName; metadata.lineNumber = logPosition.lineNumber; metadata.columnNumber = logPosition.columnNumber; } if (shouldCallMonitor && this._loggerMonitor) { this._loggerMonitor.onLog(metadata, config); } if (shouldCallWriter) { this.writerService.writeMessage(metadata, config); } if (shouldCallServer) { this.serverService.sendToServer(metadata, config); } }); } } /** @nocollapse */ /** @nocollapse */ NGXLogger.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLogger, deps: [{ token: TOKEN_LOGGER_CONFIG }, { token: TOKEN_LOGGER_CONFIG_ENGINE_FACTORY }, { token: TOKEN_LOGGER_METADATA_SERVICE }, { token: TOKEN_LOGGER_RULES_SERVICE }, { token: TOKEN_LOGGER_MAPPER_SERVICE }, { token: TOKEN_LOGGER_WRITER_SERVICE }, { token: TOKEN_LOGGER_SERVER_SERVICE }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ NGXLogger.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLogger, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: NGXLogger, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_CONFIG] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_CONFIG_ENGINE_FACTORY] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_METADATA_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_RULES_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_MAPPER_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_WRITER_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_SERVER_SERVICE] }] }]; } }); /** * CustomNGXLoggerService is designed to allow users to get a new instance of a logger */ class CustomNGXLoggerService { constructor(logger, configEngineFactory, metadataService, ruleService, mapperService, writerService, serverService) { this.logger = logger; this.configEngineFactory = configEngineFactory; this.metadataService = metadataService; this.ruleService = ruleService; this.mapperService = mapperService; this.writerService = writerService; this.serverService = serverService; } /** * Create an instance of a logger * @deprecated this function does not have all the features, @see getNewInstance for every params available * @param config * @param serverService * @param logMonitor * @param mapperService * @returns */ create(config, serverService, logMonitor, mapperService) { return this.getNewInstance({ config, serverService, logMonitor, mapperService }); } /** * Get a new instance of NGXLogger * @param params list of optional params to use when creating an instance of NGXLogger * @returns the new instance of NGXLogger */ getNewInstance(params) { const logger = new NGXLogger(params?.config ?? this.logger.getConfigSnapshot(), params?.configEngineFactory ?? this.configEngineFactory, params?.metadataService ?? this.metadataService, params?.ruleService ?? this.ruleService, params?.mapperService ?? this.mapperService, params?.writerService ?? this.writerService, params?.serverService ?? this.serverService); if (params?.partialConfig) { logger.partialUpdateConfig(params.partialConfig); } if (params?.logMonitor) { logger.registerMonitor(params.logMonitor); } return logger; } } /** @nocollapse */ /** @nocollapse */ CustomNGXLoggerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: CustomNGXLoggerService, deps: [{ token: NGXLogger }, { token: TOKEN_LOGGER_CONFIG_ENGINE_FACTORY }, { token: TOKEN_LOGGER_METADATA_SERVICE }, { token: TOKEN_LOGGER_RULES_SERVICE }, { token: TOKEN_LOGGER_MAPPER_SERVICE }, { token: TOKEN_LOGGER_WRITER_SERVICE }, { token: TOKEN_LOGGER_SERVER_SERVICE }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ /** @nocollapse */ CustomNGXLoggerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: CustomNGXLoggerService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: CustomNGXLoggerService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: NGXLogger }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_CONFIG_ENGINE_FACTORY] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_METADATA_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_RULES_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_MAPPER_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_WRITER_SERVICE] }] }, { type: undefined, decorators: [{ type: Inject, args: [TOKEN_LOGGER_SERVER_SERVICE] }] }]; } }); class LoggerModule { static forRoot(config, customProvider) { if (!customProvider) { customProvider = {}; } // default config provider if (!customProvider.configProvider) { customProvider.configProvider = { provide: TOKEN_LOGGER_CONFIG, useValue: config || {} }; } else { // if the user provided its own config, we just make sure the injection token is correct if (customProvider.configProvider.provide !== TOKEN_LOGGER_CONFIG) { throw new Error(`Wrong injection token for configProvider, it should be ${TOKEN_LOGGER_CONFIG} and you used ${customProvider.configProvider.provide}`); } } // default configEngine provider if (!customProvider.configEngineFactoryProvider) { customProvider.configEngineFactoryProvider = { provide: TOKEN_LOGGER_CONFIG_ENGINE_FACTORY, useClass: NGXLoggerConfigEngineFactory }; } else { // if the user provided its own configEngineFactory, we just make sure the injection token is correct if (customProvider.configEngineFactoryProvider.provide !== TOKEN_LOGGER_CONFIG_ENGINE_FACTORY) { throw new Error(`Wrong injection token for configEngineFactoryProvider, it should be '${TOKEN_LOGGER_CONFIG_ENGINE_FACTORY}' and you used '${customProvider.configEngineFactoryProvider.provide}'`); } } // default metadata provider if (!customProvider.metadataProvider) { customProvider.metadataProvider = { provide: TOKEN_LOGGER_METADATA_SERVICE, useClass: NGXLoggerMetadataService }; } else { // if the user provided its own metadataService, we just make sure the injection token is correct if (customProvider.metadataProvider.provide !== TOKEN_LOGGER_METADATA_SERVICE) { throw new Error(`Wrong injection token for metadataProvider, it should be '${TOKEN_LOGGER_METADATA_SERVICE}' and you used '${customProvider.metadataProvider.provide}'`); } } // default rule provider if (!customProvider.ruleProvider) { customProvider.ruleProvider = { provide: TOKEN_LOGGER_RULES_SERVICE, useClass: NGXLoggerRulesService }; } else { // if the user provided its own ruleService, we just make sure the injection token is correct if (customProvider.ruleProvider.provide !== TOKEN_LOGGER_RULES_SERVICE) { throw new Error(`Wrong injection token for ruleProvider, it should be '${TOKEN_LOGGER_RULES_SERVICE}' and you used '${customProvider.ruleProvider.provide}'`); } } // default mapper provider if (!customProvider.mapperProvider) { customProvider.mapperProvider = { provide: TOKEN_LOGGER_MAPPER_SERVICE, useClass: NGXLoggerMapperService }; } else { // if the user provided its own mapperService, we just make sure the injection token is correct if (customProvider.mapperProvider.provide !== TOKEN_LOGGER_MAPPER_SERVICE) { throw new Error(`Wrong injection token for mapperProvider, it should be '${TOKEN_LOGGER_MAPPER_SERVICE}' and you used '${customProvider.mapperProvider.provide}'`); } } // default writer provider if (!customProvider.writerProvider) { customProvider.writerProvider = { provide: TOKEN_LOGGER_WRITER_SERVICE, useClass: NGXLoggerWriterService }; } else { // if the user provided its own writerService, we just make sure the injection token is correct if (customProvider.writerProvider.provide !== TOKEN_LOGGER_WRITER_SERVICE) { throw new Error(`Wrong injection token for writerProvider, it should be '${TOKEN_LOGGER_WRITER_SERVICE}' and you used '${customProvider.writerProvider.provide}'`); } } // default server provider if (!customProvider.serverProvider) { customProvider.serverProvider = { provide: TOKEN_LOGGER_SERVER_SERVICE, useClass: NGXLoggerServerService }; } else { // if the user provided its own serverService, we just make sure the injection token is correct if (customProvider.serverProvider.provide !== TOKEN_LOGGER_SERVER_SERVICE) { throw new Error(`Wrong injection token for serverProvider, it should be '${TOKEN_LOGGER_SERVER_SERVICE}' and you used '${customProvider.writerProvider.provide}'`); } } return { ngModule: LoggerModule, providers: [ NGXLogger, customProvider.configProvider, customProvider.configEngineFactoryProvider, customProvider.metadataProvider, customProvider.ruleProvider, customProvider.mapperProvider, customProvider.writerProvider, customProvider.serverProvider, CustomNGXLoggerService, ] }; } static forChild() { // todo : this forChild is useless for now because nothing is different from forRoot. // This should be implemented so that user can change the providers in the forChild return { ngModule: LoggerModule, }; } } /** @nocollapse */ /** @nocollapse */ LoggerModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: LoggerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); /** @nocollapse */ /** @nocollapse */ LoggerModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: LoggerModule, imports: [CommonModule] }); /** @nocollapse */ /** @nocollapse */ LoggerModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: LoggerModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: LoggerModule, decorators: [{ type: NgModule, args: [{ imports: [ CommonModule ], }] }] }); /* * Public API Surface of ngx-logger */ /** * Generated bundle index. Do not edit. */ export { CustomNGXLoggerService, DEFAULT_COLOR_SCHEME, LoggerModule, NGXLogger, NGXLoggerConfigEngine, NGXLoggerConfigEngineFactory, NGXLoggerMapperService, NGXLoggerMetadataService, NGXLoggerMonitor, NGXLoggerRulesService, NGXLoggerServerService, NGXLoggerWriterService, NgxLoggerLevel, TOKEN_LOGGER_CONFIG, TOKEN_LOGGER_CONFIG_ENGINE_FACTORY, TOKEN_LOGGER_MAPPER_SERVICE, TOKEN