Skip to content

Commit

Permalink
Properly handle logs created and saved through createLogger*( in pre-…
Browse files Browse the repository at this point in the history
…mounted instances (#577)

* Fixed #518 by calling saveLog() as soon as createLogger() has finished loading for pending log messages
  • Loading branch information
jamessimone authored Oct 24, 2023
1 parent 77fe275 commit b140e72
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 31 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.

## Unlocked Package - v4.11.11
## Unlocked Package - v4.11.12

[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Oih7QAC)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Oih7QAC)
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Mjx5QAC)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001Mjx5QAC)
[![View Documentation](./images/btn-view-documentation.png)](https://jongpie.github.io/NebulaLogger/)

`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001Oih7QAC`
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001Mjx5QAC`

`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001Oih7QAC`
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001Mjx5QAC`

---

Expand Down
2 changes: 1 addition & 1 deletion nebula-logger/core/main/logger-engine/classes/Logger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
global with sharing class Logger {
// There's no reliable way to get the version number dynamically in Apex
@TestVisible
private static final String CURRENT_VERSION_NUMBER = 'v4.11.11';
private static final String CURRENT_VERSION_NUMBER = 'v4.11.12';
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
private static final Set<String> IGNORED_APEX_CLASSES = initializeIgnoredApexClasses();
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const ComponentLogEntry = class {
}
};

/* eslint-disable @lwc/lwc/no-dupe-class-members */
const LogEntryBuilder = class {
#componentLogEntry;
#settingsPromise;
Expand Down Expand Up @@ -138,7 +139,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
2 changes: 1 addition & 1 deletion nebula-logger/core/main/logger-engine/lwc/logger/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { LightningElement, api } from 'lwc';
import { createLoggerService } from './loggerService';

const CURRENT_VERSION_NUMBER = 'v4.11.11';
const CURRENT_VERSION_NUMBER = 'v4.11.12';

export default class Logger extends LightningElement {
#loggerService = createLoggerService();
Expand Down
62 changes: 47 additions & 15 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,17 +120,18 @@ 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() {
async flushBuffer() {
return Promise.all(this.#loggingPromises).then(() => {
this.#componentLogEntries = [];
this.#loggingPromises = [];
this.#isSavingLog = false;
});
}

Expand All @@ -132,17 +140,31 @@ const LoggerService = class {
* 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 All @@ -153,6 +175,7 @@ const LoggerService = class {
}
});
}
return Promise.resolve();
}

_loadSettingsFromServer(forceReload) {
Expand All @@ -173,27 +196,36 @@ 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 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();
}
});
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 LoggerLWCDemo from 'c/loggerLWCDemo';

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: LoggerLWCDemo });
document.body.appendChild(demo);

await flushPromises();

expect(demo.logger?.getBufferSize()).toBe(0);
});
});
19 changes: 15 additions & 4 deletions nebula-logger/recipes/lwc/loggerLWCDemo/loggerLWCDemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,21 @@ export default class LoggerLWCDemo extends LightningElement {

connectedCallback() {
console.log('>>> start of connectedCallback()');
this.logger.info('>>> running connectedCallback(), using createLogger()');
this.logger.info('>>> adding an extra log entry');
this.logger.saveLog();
console.log('>>> done with connectedCallback()');
try {
this.logger.error('test error entry');
this.logger.warn('test warn entry');
this.logger.info('test info entry');
this.logger.debug('test debug entry');
this.logger.fine('test fine entry');
this.logger.finer('test finer entry');
this.logger.finest('test finest entry');
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');
});
}
}

disconnectedCallback() {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nebula-logger",
"version": "4.11.11",
"version": "4.11.12",
"description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.",
"author": "Jonathan Gillespie",
"license": "MIT",
Expand Down
7 changes: 4 additions & 3 deletions sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"package": "Nebula Logger - Core",
"path": "./nebula-logger/core",
"definitionFile": "./config/scratch-orgs/base-scratch-def.json",
"versionNumber": "4.11.11.NEXT",
"versionName": "Reduced Usage of Email Limits Consumption in LoggerEmailSender",
"versionDescription": "Updated LoggerEmailSender to use the instance method Messaging.SingleEmailMessage.setTargetObjectId() when sending failure emails to internal users, which does not count towards the transactional email limits",
"versionNumber": "4.11.12.NEXT",
"versionName": "Bugfix for Lightning Component Entries Not Always Saving",
"versionDescription": "Fixed an issue in the logger LWC where log entries in lightning components would be lost while trying to load LoggerSettings__c for the current user",
"releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases",
"unpackagedMetadata": {
"path": "./nebula-logger/extra-tests"
Expand Down Expand Up @@ -159,6 +159,7 @@
"Nebula Logger - [email protected]__c.loggedbyusernametext__c-and-logentry__c.loggedbyusernametext__c": "04t5Y000001OigJQAS",
"Nebula Logger - [email protected]": "04t5Y000001OigxQAC",
"Nebula Logger - Core@4.11.11-reduced-usage-of-email-limits-consumption-in-loggeremailsender": "04t5Y000001Oih7QAC",
"Nebula Logger - Core@4.11.12-bugfix-for-lightning-component-entries-not-always-saving": "04t5Y000001Mjx5QAC",
"Nebula Logger - Core Plugin - Async Failure Additions": "0Ho5Y000000blO4SAI",
"Nebula Logger - Core Plugin - Async Failure [email protected]": "04t5Y0000015lhiQAA",
"Nebula Logger - Core Plugin - Async Failure [email protected]": "04t5Y0000015lhsQAA",
Expand Down

0 comments on commit b140e72

Please sign in to comment.