Skip to content

Commit

Permalink
Fixes #518 by calling saveLog() as soon as createLogger() has finishe…
Browse files Browse the repository at this point in the history
…d loading for pending log messages.
  • Loading branch information
jamessimone authored and jongpie committed Oct 23, 2023
1 parent 77fe275 commit ae3403f
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const LogEntryBuilder = class {
/* eslint-disable no-console */
_logToConsole() {
this.#settingsPromise().then(setting => {
this.isConsoleLoggingEnabled = setting.isConsoleLoggingEnabled;
this.isConsoleLoggingEnabled = !!setting?.isConsoleLoggingEnabled;

if (!this.isConsoleLoggingEnabled) {
return;
Expand Down
79 changes: 57 additions & 22 deletions nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ import { newLogEntry } from './logEntryBuilder';
import getSettings from '@salesforce/apex/ComponentLogger.getSettings';
import saveComponentLogEntries from '@salesforce/apex/ComponentLogger.saveComponentLogEntries';

const LOADING_ENUM = {
loading: 'loading',
enabled: 'enabled',
disabled: 'disabled'
};

/* eslint-disable @lwc/lwc/no-dupe-class-members */
const LoggerService = class {
static settings = undefined;

#isSavingLog = false;
#componentLogEntries = [];
#scenario;
#loggingPromises = [];
Expand Down Expand Up @@ -113,36 +120,53 @@ const LoggerService = class {
* @return {Integer} The buffer's current size
*/
getBufferSize() {
return this.#componentLogEntries.length;
return this.#componentLogEntries.length + this.#loggingPromises.length;
}

/**
* @description Discards any entries that have been generated but not yet saved
* @return {Promise<void>} A promise to clear the entries
*/
flushBuffer() {
return Promise.all(this.#loggingPromises).then(() => {
this.#componentLogEntries = [];
this.#loggingPromises = [];
});
async flushBuffer() {
return Promise.all(this.#loggingPromises)
.then(() => {
this.#componentLogEntries = [];
this.#loggingPromises = [];
this.#isSavingLog = false;
})
.catch(err => console.error(err));
}

/**
* @description Saves any entries in Logger's buffer, using the specified save method for only this call
* All subsequent calls to saveLog() will use the transaction save method
* @param {String} saveMethod The enum value of LoggerService.SaveMethod to use for this specific save action
*/
saveLog(saveMethodName) {
if (this.getBufferSize() > 0) {
async saveLog(saveMethodName) {
this.#isSavingLog = true;

const filteredLogEntries = this.#componentLogEntries.filter(
possibleLogEntry =>
(possibleLogEntry.loadingEnum === LOADING_ENUM.loading &&
this._meetsUserLoggingLevel(possibleLogEntry.loggingLevel) === LOADING_ENUM.enabled) ||
possibleLogEntry.loadingEnum === LOADING_ENUM.enabled
);

if (filteredLogEntries.length > 0) {
let resolvedSaveMethodName;
if (!saveMethodName && LoggerService.settings && LoggerService.settings.defaultSaveMethodName) {
resolvedSaveMethodName = LoggerService.settings.defaultSaveMethodName;
} else {
resolvedSaveMethodName = saveMethodName;
}

Promise.all(this.#loggingPromises)
.then(saveComponentLogEntries({ componentLogEntries: this.#componentLogEntries, saveMethodName: resolvedSaveMethodName }))
return Promise.all(this.#loggingPromises)
.then(
saveComponentLogEntries({
componentLogEntries: filteredLogEntries,
saveMethodName: resolvedSaveMethodName
})
)
.then(this.flushBuffer())
.catch(error => {
if (LoggerService.settings.isConsoleLoggingEnabled === true) {
Expand Down Expand Up @@ -173,27 +197,38 @@ const LoggerService = class {
}

_meetsUserLoggingLevel(logEntryLoggingLevel) {
let logEntryLoggingLevelOrdinal = LoggerService.settings.supportedLoggingLevels[logEntryLoggingLevel];
return (
LoggerService.settings &&
LoggerService.settings.isEnabled === true &&
LoggerService.settings.userLoggingLevel.ordinal <= logEntryLoggingLevelOrdinal
);
if (LoggerService.settings && LoggerService.settings.supportedLoggingLevels && LoggerService.settings.userLoggingLevel) {
const currentIsEnabled =
LoggerService.settings.isEnabled === true &&
LoggerService.settings.userLoggingLevel.ordinal <= LoggerService.settings?.supportedLoggingLevels[logEntryLoggingLevel];
return currentIsEnabled ? LOADING_ENUM.enabled : LOADING_ENUM.disabled;
}
return LOADING_ENUM.loading;
}

_newEntry(loggingLevel, message) {
// Builder is returned immediately but console log will be determined after loadding settings from server
// Builder is returned immediately but console log will be determined after loading settings from server
const logEntryBuilder = newLogEntry(loggingLevel, this._loadSettingsFromServer);
logEntryBuilder.setMessage(message);
if (this.#scenario) {
logEntryBuilder.scenario = this.#scenario;
}
const loggingPromise = this._loadSettingsFromServer().then(() => {
if (this._meetsUserLoggingLevel(loggingLevel) === true) {
this.#componentLogEntries.push(logEntryBuilder.getComponentLogEntry());
}
});
const loggingPromise = this._loadSettingsFromServer()
.then(() => {
const isEnabledEnum = this._meetsUserLoggingLevel(loggingLevel);
if (isEnabledEnum === LOADING_ENUM.enabled || isEnabledEnum === LOADING_ENUM.loading) {
const componentLogEntry = logEntryBuilder.getComponentLogEntry();
componentLogEntry.loadingEnum = isEnabledEnum;
this.#componentLogEntries.push(componentLogEntry);
}
if (this.#isSavingLog) {
this.#isSavingLog = false;
this.saveLog();
}
})
.catch(err => console.error('there was an error building the logging promise: ' + err.message));
this.#loggingPromises.push(loggingPromise);

return logEntryBuilder;
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createElement } from 'lwc';
import LoggerDemo from 'c/loggerDemo';

import getSettings from '@salesforce/apex/ComponentLogger.getSettings';

const flushPromises = async () => {
await new Promise(process.nextTick);
};

const MOCK_GET_SETTINGS = {
defaultSaveMethod: 'EVENT_BUS',
isEnabled: true,
isConsoleLoggingEnabled: true,
supportedLoggingLevels: { FINEST: 2, FINER: 3, FINE: 4, DEBUG: 5, INFO: 6, WARN: 7, ERROR: 8 },
userLoggingLevel: { ordinal: 2, name: 'FINEST' }
};

jest.mock(
'@salesforce/apex/ComponentLogger.getSettings',
() => {
return {
default: jest.fn()
};
},
{ virtual: true }
);

describe('logger demo tests', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
jest.clearAllMocks();
});

it('mounts and saves log correctly in one go', async () => {
getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS });
const demo = createElement('c-logger-demo', { is: LoggerDemo });
document.body.appendChild(demo);

await flushPromises();

expect(demo.logger?.getBufferSize()).toBe(0);
});
});
15 changes: 15 additions & 0 deletions nebula-logger/extra-tests/lwc/loggerDemo/loggerDemo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!--
//------------------------------------------------------------------------------------------------//
// This file is part of the Nebula Logger project, released under the MIT License. //
// See LICENSE file or go to https://github.com/jongpie/NebulaLogger for full license details. //
//------------------------------------------------------------------------------------------------//
-->

<template>
<lightning-card title="Example">
<div>
✅ This component demonstrates how to use Nebula Logger by using &nbsp;<code>import { createLogger } from 'c/logger';</code> in your component's
JavaScript file. This approach was introduced in Nebula Logger v4.10.2, and is now the recommended approach.
</div>
</lightning-card>
</template>
27 changes: 27 additions & 0 deletions nebula-logger/extra-tests/lwc/loggerDemo/loggerDemo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------------------------//
// This file is part of the Nebula Logger project, released under the MIT License. //
// See LICENSE file or go to https://github.com/jongpie/NebulaLogger for full license details. //
//------------------------------------------------------------------------------------------------//

/* eslint-disable no-console */
import { LightningElement, api } from 'lwc';

import { createLogger } from 'c/logger';

export default class LoggerDemo extends LightningElement {
@api
logger;

connectedCallback() {
this.logger = createLogger();
console.log('>>> start of connectedCallback()');
try {
throw new Error('A bad thing happened here');
} catch (error) {
this.logger.error('>>> connectedCallback error: ' + JSON.stringify(error));
this.logger.saveLog().then(() => {
console.log('done with async save');
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__Tab</target>
</targets>
</LightningComponentBundle>

0 comments on commit ae3403f

Please sign in to comment.