From 8bdcb3b20c82bea7cb2bcddb14f62ae34004a8fc Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Sat, 28 Sep 2024 18:29:38 -0400 Subject: [PATCH] [skip ci][WIP] Added JS support for setting fields once per component, using logger.setField() --- README.md | 58 ++++++++++++++----- .../lwc/logger/__tests__/logger.test.js | 26 ++++++++- .../main/logger-engine/lwc/logger/logger.js | 9 +++ .../logger-engine/lwc/logger/loggerService.js | 15 +++++ 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 47119cca8..a2a0e41fd 100644 --- a/README.md +++ b/README.md @@ -645,29 +645,55 @@ The first step is to add a field to the platform event `LogEntryEvent__e` ![Custom Field on LogEntryEvent__e](./images/custom-field-log-entry-event.png) -- In Apex, populate your field(s) by calling the instance method overloads `LogEntryEventBuilder.setField(Schema.SObjectField field, Object fieldValue)` or `LogEntryEventBuilder.setField(Map fieldToValue)` - - ```apex Logger.info('hello, world') - // Set a single field - .setField(LogEntryEvent__e.SomeCustomTextField__c, 'some text value') - // Set multiple fields - .setField(new Map{ - LogEntryEvent__e.AnotherCustomTextField__c => 'another text value', - LogEntryEvent__e.SomeCustomDatetimeField__c => System.now() - }); +- In Apex, you have 2 ways to populate your custom fields + + 1. Set the field once per transaction - every `LogEntryEvent__e` logged in the transaction will then automatically have the specified field populated with the same value. + - This is typically used for fields that are mapped to an equivalent `Log__c` or `LoggerScenario__c` field. + + - How: call the static method overloads `Logger.setField(Schema.SObjectField field, Object fieldValue)` or `Logger.setField(Map fieldToValue)` + + 2. Set the field on a specific `LogEntryEvent__e` record - other records will not have the field automatically set. + - This is typically used for fields that are mapped to an equivalent `LogEntry__c` field. + - How: call the instance method overloads `LogEntryEventBuilder.setField(Schema.SObjectField field, Object fieldValue)` or `LogEntryEventBuilder.setField(Map fieldToValue)` + + ```apex + // Set My_Field__c on every log entry event created in this transaction with the same value + Logger.setField(LogEntryEvent__e.My_Field__c, 'some value that applies to the whole Apex transaction'); + + // Set fields on specific entries + Logger.warn('hello, world - "a value" set for Some_Other_Field__c').setField(LogEntryEvent__e.Some_Other_Field__c, 'a value') + Logger.warn('hello, world - "different value" set for Some_Other_Field__c').setField(LogEntryEvent__e.Some_Other_Field__c, 'different value') + Logger.info('hello, world - no value set for Some_Other_Field__c'); + + Logger.saveLog(); ``` -- In JavaScript, populate your field(s) by calling the instance function `LogEntryEventBuilder.setField(Object fieldToValue)` +- In JavaScript, you have 2 ways to populate your custom fields. These are very similar to the 2 ways available in Apex (above). + + 1. Set the field once per component - every `LogEntryEvent__e` logged in your component will then automatically have the specified field populated with the same value. + - This is typically used for fields that are mapped to an equivalent `Log__c` or `LoggerScenario__c` field. + + - How: call the `logger` LWC function `logger.setField(Object fieldToValue)` + + 2. Set the field on a specific `LogEntryEvent__e` record - other records will not have the field automatically set. + - This is typically used for fields that are mapped to an equivalent `LogEntry__c` field. + - How: call the instance function `LogEntryEventBuilder.setField(Object fieldToValue)` ```javascript - import { createLogger } from 'c/logger'; + import { getLogger } from 'c/logger'; export default class LoggerLWCImportDemo extends LightningElement { - logger; + logger = getLogger(); + + connectedCallback() { + // Set My_Field__c on every log entry event created in this component with the same value + this.logger.setField(LogEntryEvent__e.My_Field__c, 'some value that applies to the whole Apex transaction'); + + // Set fields on specific entries + this.logger.warn('hello, world - "a value" set for Some_Other_Field__c').setField({ Some_Other_Field__c: 'a value' }); + this.logger.warn('hello, world - "different value" set for Some_Other_Field__c').setField({ Some_Other_Field__c: 'different value' }); + this.logger.info('hello, world - no value set for Some_Other_Field__c'); - async connectedCallback() { - this.logger = await createLogger(); - this.logger.info('Hello, world').setField({ SomeCustomTextField__c: 'some text value', SomeCustomNumbertimeField__c: 123 }); this.logger.saveLog(); } } diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/logger.test.js b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/logger.test.js index 1fd88ca04..dfaf933ea 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/logger.test.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/logger.test.js @@ -162,7 +162,31 @@ describe('logger lwc import tests', () => { expect(logEntry.browser.windowResolution).toEqual(window.innerWidth + ' x ' + window.innerHeight); }); - it('sets multiple custom fields when using recommended import approach', async () => { + it('sets multiple custom component fields on all entries when using recommended import approach', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = await createLogger(); + await logger.getUserSettings(); + const logEntryBuilder = logger.info('example log entry'); + const logEntry = logEntryBuilder.getComponentLogEntry(); + const firstFakeFieldName = 'SomeField__c'; + const firstFieldMockValue = 'something'; + const secondFakeFieldName = 'AnotherField__c'; + const secondFieldMockValue = 'another value'; + expect(logEntry.fieldToValue[firstFakeFieldName]).toBeFalsy(); + expect(logEntry.fieldToValue[secondFakeFieldName]).toBeFalsy(); + + logger.setField({ + [firstFakeFieldName]: firstFieldMockValue, + [secondFakeFieldName]: secondFieldMockValue + }); + logger.saveLog(); + await Promise.resolve('save logs'); + + expect(logEntry.fieldToValue[firstFakeFieldName]).toEqual(firstFieldMockValue); + expect(logEntry.fieldToValue[secondFakeFieldName]).toEqual(secondFieldMockValue); + }); + + it('sets multiple custom entry fields on a single entry when using recommended import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); await logger.getUserSettings(); diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/logger.js b/nebula-logger/core/main/logger-engine/lwc/logger/logger.js index 7015f83c0..4a6500d68 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/logger.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/logger.js @@ -22,6 +22,15 @@ export default class Logger extends LightningElement { return this.#loggerService.getUserSettings(); } + /** + * @description Sets multiple field values on the builder's `LogEntryEvent__e` record + * @param {Object} fieldToValue An object containing the custom field name as a key, with the corresponding value to store. + * Example: `{"SomeField__c": "some value", "AnotherField__c": "another value"}` + */ + setField(fieldToValue) { + this.#loggerService.setField(fieldToValue); + } + /** * @description Sets the scenario name for the current transaction - this is stored in `LogEntryEvent__e.Scenario__c` * and `Log__c.Scenario__c`, and can be used to filter & group logs diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js index 930253e60..e6b404e63 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js @@ -12,11 +12,23 @@ const LoggerService = class { #componentLogEntries = []; #settings; #scenario; + #componentFieldToValue = {}; getUserSettings() { return this.#settings; } + /** + * @description Sets multiple field values on every generated `LogEntryEvent__e` record + * @param {Object} fieldToValue An object containing the custom field name as a key, with the corresponding value to store. + * Example: `{"SomeField__c": "some value", "AnotherField__c": "another value"}` + */ + setField(fieldToValue) { + if (!!fieldToValue && typeof fieldToValue === 'object' && !Array.isArray(fieldToValue)) { + Object.assign(this.#componentFieldToValue, fieldToValue); + } + } + setScenario(scenario) { this.#scenario = scenario; this.#componentLogEntries.forEach(logEntry => { @@ -76,6 +88,9 @@ const LoggerService = class { try { const logEntriesToSave = [...this.#componentLogEntries]; + logEntriesToSave.forEach(logEntry => { + Object.assign(logEntry.fieldToValue, this.#componentFieldToValue); + }); // this is an attempt to only flush the buffer for log entries that we are sending to Apex // rather than any that could be added if the saveLog call isn't awaited properly this.flushBuffer();