diff --git a/README.md b/README.md index 3038bcbd2..33db99e65 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,15 @@ The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, Process Builder & integrations. -## Unlocked Package - v4.14.5 +## Unlocked Package - v4.14.6 -[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oRXQAY) -[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oRXQAY) +[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oRhQAI) +[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oRhQAI) [![View Documentation](./images/btn-view-documentation.png)](https://jongpie.github.io/NebulaLogger/) -`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oRXQAY` +`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oRhQAI` -`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oRXQAY` +`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oRhQAI` --- @@ -590,9 +590,12 @@ Once you've implementing log entry tagging within Apex or Flow, you can choose h ## Adding Custom Fields to Nebula Logger's Data Model -As of `v4.13.14`, Nebula Logger supports defining, setting, and mapping custom fields within Nebula Logger's data model. This is helpful in orgs that want to extend Nebula Logger's included data model by creating their own org/project-specific fields. +Nebula Logger supports defining, setting, and mapping custom fields within Nebula Logger's data model. This is helpful in orgs that want to extend Nebula Logger's included data model by creating their own org/project-specific fields. -This feature requires that you populate your custom fields yourself, and is only available in Apex currently. The plan is to add in a future release the ability to also set custom fields via JavaScript & Flow. +This feature requires that you populate your custom fields yourself, and is only available in Apex & JavaScript currently. The plan is to add in a future release the ability to also set custom fields via Flow. + +- `v4.13.14` added this functionality for Apex +- `v4.14.6` added this functionality for JavaScript (lightning components) ### Adding Custom Fields to the Platform Event `LogEntryEvent__e` @@ -604,19 +607,35 @@ 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) -- Populate your field(s) in Apex by calling the instance method overloads `LogEntryEventBuilder.setField(Schema.SObjectField field, Object fieldValue)` or `LogEntryEventBuilder.setField(Map fieldToValue)` +- 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 - .setFields(new Map{ + .setField(new Map{ LogEntryEvent__e.AnotherCustomTextField__c => 'another text value', LogEntryEvent__e.SomeCustomDatetimeField__c => System.now() }); ``` +- In JavaScript, populate your field(s) by calling the instance function `LogEntryEventBuilder.setField(Object fieldToValue)` + + ```javascript + import { createLogger } from 'c/logger'; + + export default class LoggerLWCImportDemo extends LightningElement { + logger; + + async connectedCallback() { + this.logger = await createLogger(); + this.logger.info('Hello, world').setField({ SomeCustomTextField__c: 'some text value', SomeCustomNumbertimeField__c: 123 }); + this.logger.saveLog(); + } + } + ``` + ### Adding Custom Fields to the Custom Objects `Log__c`, `LogEntry__c`, and `LoggerScenario__c` If you want to store the data in one of Nebula Logger's custom objects, you can follow the above steps, and also... diff --git a/docs/apex/Logger-Engine/ComponentLogger.md b/docs/apex/Logger-Engine/ComponentLogger.md index a7befdb28..be7b38dbc 100644 --- a/docs/apex/Logger-Engine/ComponentLogger.md +++ b/docs/apex/Logger-Engine/ComponentLogger.md @@ -131,6 +131,10 @@ Context about the user's browser, automatically captured by Nebula Logger (Optional) A JavaScript Error to log +###### `fieldToValue` → `Map` + +(Optional) A map containing key-value pairs of fields to set on `LogEntryEvent__e` + ###### `loggingLevel` → `String` The name of the `LoggingLevel` enum value diff --git a/docs/lightning-components/LogEntryBuilder.md b/docs/lightning-components/LogEntryBuilder.md index a498ad4f9..336ece358 100644 --- a/docs/lightning-components/LogEntryBuilder.md +++ b/docs/lightning-components/LogEntryBuilder.md @@ -11,6 +11,7 @@ - [.setRecord(record)](#LogEntryBuilder+setRecord) [LogEntryBuilder](#LogEntryBuilder) - [.setScenario(scenario)](#LogEntryBuilder+setScenario) [LogEntryBuilder](#LogEntryBuilder) - [.setError(error)](#LogEntryBuilder+setError) [LogEntryBuilder](#LogEntryBuilder) + - [.setField(fieldToValue)](#LogEntryBuilder+setField) [LogEntryBuilder](#LogEntryBuilder) - [.parseStackTrace(error)](#LogEntryBuilder+parseStackTrace) [LogEntryBuilder](#LogEntryBuilder) - [.addTag(tag)](#LogEntryBuilder+addTag) [LogEntryBuilder](#LogEntryBuilder) - [.addTags(tags)](#LogEntryBuilder+addTags) [LogEntryBuilder](#LogEntryBuilder) @@ -94,6 +95,19 @@ Sets the log entry event's exception fields | ----- | ------------------ | -------------------------------------------------------------------------------- | | error | Error | The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use | + + +### logEntryBuilder.setField(fieldToValue) [LogEntryBuilder](#LogEntryBuilder) + +Sets multiple field values on the builder's `LogEntryEvent__e` record + +**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) +**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods + +| Param | Type | Description | +| ------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| fieldToValue | Object | 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"}` | + ### logEntryBuilder.parseStackTrace(error) [LogEntryBuilder](#LogEntryBuilder) diff --git a/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls b/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls index c8289ad34..d9f82f278 100644 --- a/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls +++ b/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls @@ -57,7 +57,11 @@ public inherited sharing class ComponentLogger { continue; } - LogEntryEvent__e logEntryEvent = logEntryEventBuilder.setTimestamp(componentLogEntry.timestamp).addTags(componentLogEntry.tags).getLogEntryEvent(); + Map customFieldToFieldValue = getCustomFieldToFieldValue(componentLogEntry.fieldToValue); + LogEntryEvent__e logEntryEvent = logEntryEventBuilder.setTimestamp(componentLogEntry.timestamp) + .setField(customFieldToFieldValue) + .addTags(componentLogEntry.tags) + .getLogEntryEvent(); if (componentLogEntry.recordId != null) { logEntryEventBuilder.setRecord(componentLogEntry.recordId); @@ -83,6 +87,23 @@ public inherited sharing class ComponentLogger { } } + private static Map getCustomFieldToFieldValue(Map fieldNameToValue) { + Map resolvedFieldToFieldValue = new Map(); + + if (fieldNameToValue == null || fieldNameToValue.isEmpty()) { + return resolvedFieldToFieldValue; + } + + Map fieldNameToField = Schema.LogEntryEvent__e.SObjectType.getDescribe().fields.getMap(); + for (String fieldName : fieldNameToValue.keySet()) { + Schema.SObjectField field = fieldNameToField.get(fieldName); + if (field != null) { + resolvedFieldToFieldValue.put(field, fieldNameToValue.get(fieldName)); + } + } + return resolvedFieldToFieldValue; + } + private static void setBrowserDetails(LogEntryEvent__e logEntryEvent, ComponentLogEntry componentLogEntry) { logEntryEvent.BrowserAddress__c = LoggerDataStore.truncateFieldValue(Schema.LogEntryEvent__e.BrowserAddress__c, componentLogEntry.browser?.address); logEntryEvent.BrowserFormFactor__c = LoggerDataStore.truncateFieldValue( @@ -285,6 +306,11 @@ public inherited sharing class ComponentLogger { @AuraEnabled public ComponentError error { get; set; } + /** + * @description (Optional) A map containing key-value pairs of fields to set on `LogEntryEvent__e` + */ + @AuraEnabled + public Map fieldToValue { get; set; } /** * @description The name of the `LoggingLevel` enum value */ diff --git a/nebula-logger/core/main/logger-engine/classes/Logger.cls b/nebula-logger/core/main/logger-engine/classes/Logger.cls index 23ab3c914..99b89a5ff 100644 --- a/nebula-logger/core/main/logger-engine/classes/Logger.cls +++ b/nebula-logger/core/main/logger-engine/classes/Logger.cls @@ -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.14.5'; + private static final String CURRENT_VERSION_NUMBER = 'v4.14.6'; private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG; private static final List LOG_ENTRIES_BUFFER = new List(); private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.'; 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 194ed5858..35a275ec9 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 @@ -163,6 +163,28 @@ 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 () => { + 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(); + + logEntryBuilder.setField({ + [firstFakeFieldName]: firstFieldMockValue, + [secondFakeFieldName]: secondFieldMockValue + }); + + expect(logEntry.fieldToValue[firstFakeFieldName]).toEqual(firstFieldMockValue); + expect(logEntry.fieldToValue[secondFakeFieldName]).toEqual(secondFieldMockValue); + }); + it('sets recordId when using recommended import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); @@ -658,6 +680,29 @@ describe('logger lwc legacy markup tests', () => { expect(logEntry.browser.windowResolution).toEqual(window.innerWidth + ' x ' + window.innerHeight); }); + it('sets multiple custom fields when using deprecated markup approach', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = createElement('c-logger', { is: Logger }); + document.body.appendChild(logger); + await flushPromises(); + const logEntryBuilder = await 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(); + + logEntryBuilder.setField({ + [firstFakeFieldName]: firstFieldMockValue, + [secondFakeFieldName]: secondFieldMockValue + }); + + expect(logEntry.fieldToValue[firstFakeFieldName]).toEqual(firstFieldMockValue); + expect(logEntry.fieldToValue[secondFakeFieldName]).toEqual(secondFieldMockValue); + }); + it('sets recordId when using deprecated markup approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = createElement('c-logger', { is: Logger }); diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js b/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js index aa9dbe439..674f986d9 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js @@ -6,7 +6,7 @@ import FORM_FACTOR from '@salesforce/client/formFactor'; import { log as lightningLog } from 'lightning/logger'; import { LoggerStackTrace } from './loggerStackTrace'; -const CURRENT_VERSION_NUMBER = 'v4.14.5'; +const CURRENT_VERSION_NUMBER = 'v4.14.6'; const LOGGING_LEVEL_EMOJIS = { ERROR: '⛔', @@ -19,27 +19,19 @@ const LOGGING_LEVEL_EMOJIS = { }; const ComponentBrowser = class { - address = null; - formFactor = null; - language = null; - screenResolution = null; - userAgent = null; - windowResolution = null; - - constructor() { - this.address = window.location.href; - this.formFactor = FORM_FACTOR; - this.language = window.navigator.language; - this.screenResolution = window.screen.availWidth + ' x ' + window.screen.availHeight; - this.userAgent = window.navigator.userAgent; - this.windowResolution = window.innerWidth + ' x ' + window.innerHeight; - } + address = window.location.href; + formFactor = FORM_FACTOR; + language = window.navigator.language; + screenResolution = window.screen.availWidth + ' x ' + window.screen.availHeight; + userAgent = window.navigator.userAgent; + windowResolution = window.innerWidth + ' x ' + window.innerHeight; }; // JavaScript equivalent to the Apex class ComponentLogger.ComponentLogEntry const ComponentLogEntry = class { browser = new ComponentBrowser(); error = null; + fieldToValue = {}; loggingLevel = null; message = null; originStackTrace = null; @@ -138,6 +130,24 @@ const LogEntryBuilder = class { return this; } + /** + * @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"}` + * @return {LogEntryBuilder} The same instance of `LogEntryBuilder`, useful for chaining methods + */ + setField(fieldToValue) { + if (!fieldToValue) { + return this; + } + + Object.keys(fieldToValue).forEach(fieldName => { + this.#componentLogEntry.fieldToValue[fieldName] = fieldToValue[fieldName]; + }); + + return this; + } + /** * @description Parses the provided error's stack trace and sets the log entry's origin & stack trace fields * @param {Error} error The instance of a JavaScript `Error` object with a stack trace to parse diff --git a/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls b/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls index 879e1fe29..adc36a1ac 100644 --- a/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls @@ -119,6 +119,60 @@ private class ComponentLogger_Tests { System.Assert.isNull(publishedLogEntryEvent.BrowserWindowResolution__c); } + @IsTest + static void it_should_save_component_log_entry_with_valid_custom_fields() { + LoggerStackTrace.ignoreOrigin(ComponentLogger_Tests.class); + LoggerDataStore.setMock(LoggerMockDataStore.getEventBus()); + ComponentLogger.ComponentLogEntry componentLogEntry = createMockComponentLogEntry(); + // Realistically, people shouldn't/wouldn't set fields like HttpRequestBody__c or HttpRequestMethod__c... + // But to avoid adding an extra field just for test purposes, we'll use some existing fields + componentLogEntry.fieldToValue = new Map{ + Schema.LogEntryEvent__e.HttpRequestBody__c.getDescribe().getName() => 'some value', + Schema.LogEntryEvent__e.HttpRequestMethod__c.getDescribe().getName() => 'another value' + }; + System.Assert.areEqual(0, Logger.saveLogCallCount); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishCallCount()); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + + ComponentLogger.saveComponentLogEntries(new List{ componentLogEntry }, null); + + LogEntryEvent__e publishedLogEntryEvent = (LogEntryEvent__e) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0); + System.Assert.areEqual('some value', publishedLogEntryEvent.HttpRequestBody__c); + System.Assert.areEqual('another value', publishedLogEntryEvent.HttpRequestMethod__c); + } + + @IsTest + static void it_should_save_component_log_entry_with_invalid_custom_fields() { + LoggerStackTrace.ignoreOrigin(ComponentLogger_Tests.class); + LoggerDataStore.setMock(LoggerMockDataStore.getEventBus()); + ComponentLogger.ComponentLogEntry componentLogEntry = createMockComponentLogEntry(); + componentLogEntry.fieldToValue = new Map{ 'Some Fake Field That Definitely Will Never Exist' => 'some value' }; + System.Assert.areEqual(0, Logger.saveLogCallCount); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishCallCount()); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + + ComponentLogger.saveComponentLogEntries(new List{ componentLogEntry }, null); + + LogEntryEvent__e publishedLogEntryEvent = (LogEntryEvent__e) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0); + System.Assert.isNotNull(publishedLogEntryEvent); + } + + @IsTest + static void it_should_save_component_log_entry_without_custom_fields() { + LoggerStackTrace.ignoreOrigin(ComponentLogger_Tests.class); + LoggerDataStore.setMock(LoggerMockDataStore.getEventBus()); + ComponentLogger.ComponentLogEntry componentLogEntry = createMockComponentLogEntry(); + componentLogEntry.fieldToValue = null; + System.Assert.areEqual(0, Logger.saveLogCallCount); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishCallCount()); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + + ComponentLogger.saveComponentLogEntries(new List{ componentLogEntry }, null); + + LogEntryEvent__e publishedLogEntryEvent = (LogEntryEvent__e) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0); + System.Assert.isNotNull(publishedLogEntryEvent); + } + @IsTest static void it_should_save_component_log_entry_with_queueable_job() { LoggerStackTrace.ignoreOrigin(ComponentLogger_Tests.class); diff --git a/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemo.cmp b/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemo.cmp index d633aec20..110b1e9f6 100644 --- a/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemo.cmp +++ b/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemo.cmp @@ -2,10 +2,10 @@ access="global" implements="forceCommunity:availableForAllPageTypes,flexipage:availableForAllPageTypes,force:appHostable,force:lightningQuickAction,lightning:availableForFlowScreens" > - - + +
This component demonstrates how to use Nebula Logger in Aura components
diff --git a/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemoController.js b/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemoController.js index 37c51ebd3..3d217a8f5 100644 --- a/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemoController.js +++ b/nebula-logger/recipes/aura/loggerAuraEmbedDemo/loggerAuraEmbedDemoController.js @@ -1,11 +1,11 @@ /* eslint-disable no-console, no-unused-expressions */ ({ saveLogExample: function (component) { - console.log("start of aura cmp's saveLog function"); + console.log("start of aura cmp's saveLogExample function"); const logger = component.find('logger'); console.log(logger); - logger.info(component.get('{!v.logMessage}')); + logger.info(component.get('{!v.logMessage}')).setField({ SomeLogEntryField__c: 'some text from loggerAuraEmbedDemo' }); logger.saveLog(); } }); diff --git a/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js b/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js index 77066f9b5..c7a8e1013 100644 --- a/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js +++ b/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js @@ -43,7 +43,11 @@ export default class LoggerLWCEmbedDemo extends LightningElement { .catch(error => { console.log('apex error', error); console.log('and a stack trace', new Error().stack); - const entry = logger.error(this.message).setError(error).addTag('lwc logging demo'); + const entry = logger + .error(this.message) + .setError(error) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }) + .addTags(this.tagsString.split(',')); console.log('entry==', JSON.parse(JSON.stringify(entry))); }); } @@ -53,42 +57,42 @@ export default class LoggerLWCEmbedDemo extends LightningElement { const logger = this.template.querySelector(LOGGER_NAME); console.log(logger); const someError = new TypeError('oops'); - logger.error(this.message).setError(someError).addTag('lwc logging demo'); + logger.error(this.message).setError(someError).setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }).addTags(this.tagsString.split(',')); } logWarnExample() { console.log('running logWarn for btn'); const logger = this.template.querySelector(LOGGER_NAME); console.log(logger); - logger.warn(this.message).addTags(this.tagsString.split(',')); + logger.warn(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }).addTags(this.tagsString.split(',')); } logInfoExample() { console.log('running logInfo for btn'); const logger = this.template.querySelector(LOGGER_NAME); console.log(logger); - logger.info(this.message).addTags(this.tagsString.split(',')); + logger.info(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }).addTags(this.tagsString.split(',')); } logDebugExample() { console.log('running logDebug for btn'); const logger = this.template.querySelector(LOGGER_NAME); console.log(logger); - logger.debug(this.message).addTags(this.tagsString.split(',')); + logger.debug(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }).addTags(this.tagsString.split(',')); } logFineExample() { console.log('running logFine for btn'); const logger = this.template.querySelector(LOGGER_NAME); console.log(logger); - logger.fine(this.message).addTags(this.tagsString.split(',')); + logger.fine(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }).addTags(this.tagsString.split(',')); } logFinerExample() { console.log('running logFiner for btn'); const logger = this.template.querySelector(LOGGER_NAME); console.log(logger); - logger.finer(this.message).addTags(this.tagsString.split(',')); + logger.finer(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }).addTags(this.tagsString.split(',')); } logFinestExample() { diff --git a/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js b/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js index d4a9f4cb7..21ca10a30 100644 --- a/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js +++ b/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js @@ -23,16 +23,19 @@ export default class LoggerLWCImportDemo extends LightningElement { this.logger = await createLogger(); console.log('>>> start of 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'); + this.logger.error('test error entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger.warn('test warn entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger.info('test info entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger.debug('test debug entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger.fine('test fine entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger.finer('test finer entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger.finest('test finest entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); throw new Error('A bad thing happened here'); } catch (error) { - this.logger.error('>>> connectedCallback error: ' + error.message).setError(error); + this.logger + .error('>>> connectedCallback error: ' + error.message) + .setError(error) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); this.logger.saveLog().then(() => { console.log('done with async save'); }); @@ -41,25 +44,25 @@ export default class LoggerLWCImportDemo extends LightningElement { disconnectedCallback() { console.log('>>> start of disconnectedCallback()'); - this.logger.info('>>> running disconnectedCallback(), using createLogger()'); - this.logger.info('>>> adding an extra log entry'); + this.logger.info('>>> running disconnectedCallback(), using createLogger()').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger.info('>>> adding an extra log entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); this.logger.saveLog(); console.log('>>> done with disconnectedCallback()'); } renderedCallback() { console.log('>>> start of renderedCallback()'); - this.logger?.info('>>> running renderedCallback(), using createLogger()'); - this.logger?.info('>>> adding an extra log entry'); + this.logger?.info('>>> running renderedCallback(), using createLogger()').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger?.info('>>> adding an extra log entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); this.logger?.saveLog(); console.log('>>> done with renderedCallback()'); } @wire(returnSomeString) wiredReturnSomeString({ error, data }) { - this.logger?.info('>>> logging inside a wire function'); + this.logger?.info('>>> logging inside a wire function').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); if (data) { - this.logger?.info('>>> wire function return value: ' + data); + this.logger?.info('>>> wire function return value: ' + data).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); } if (error) { this.logger?.error('>>> wire function error: ' + JSON.stringify(error)); @@ -92,7 +95,11 @@ export default class LoggerLWCImportDemo extends LightningElement { .catch(error => { console.log('apex error', error); console.log('and a stack trace', new Error().stack); - const entry = this.logger.error(this.message).setError(error).addTag('lwc logging demo'); + const entry = this.logger + .error(this.message) + .setError(error) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }) + .addTags(this.tagsString.split(',')); console.log('entry==', JSON.parse(JSON.stringify(entry))); }); } @@ -101,37 +108,41 @@ export default class LoggerLWCImportDemo extends LightningElement { console.log('running logError for btn'); console.log(this.logger); const someError = new TypeError('oops'); - this.logger.error(this.message).setError(someError).addTag('lwc logging demo'); + this.logger + .error(this.message) + .setError(someError) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }) + .addTags(this.tagsString.split(',')); } logWarnExample() { console.log('running logWarn for btn'); console.log(this.logger); - this.logger.warn(this.message).addTags(this.tagsString.split(',')); + this.logger.warn(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); } logInfoExample() { console.log('running logInfo for btn'); console.log(this.logger); - this.logger.info(this.message).addTags(this.tagsString.split(',')); + this.logger.info(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); } logDebugExample() { console.log('running logDebug for btn'); console.log(this.logger); - this.logger.debug(this.message).addTags(this.tagsString.split(',')); + this.logger.debug(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); } logFineExample() { console.log('running logFine for btn'); console.log(this.logger); - this.logger.fine(this.message).addTags(this.tagsString.split(',')); + this.logger.fine(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); } logFinerExample() { console.log('running logFiner for btn'); console.log(this.logger); - this.logger.finer(this.message).addTags(this.tagsString.split(',')); + this.logger.finer(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); } logFinestExample() { diff --git a/nebula-logger/recipes/permissionsets/LoggerRecipesAdmin.permissionset-meta.xml b/nebula-logger/recipes/permissionsets/LoggerRecipesAdmin.permissionset-meta.xml index 3b4c5f8e8..110413d48 100644 --- a/nebula-logger/recipes/permissionsets/LoggerRecipesAdmin.permissionset-meta.xml +++ b/nebula-logger/recipes/permissionsets/LoggerRecipesAdmin.permissionset-meta.xml @@ -24,6 +24,21 @@ LoggerLWCDemoController true + + false + Log__c.SomeLogField__c + true + + + false + LogEntry__c.SomeLogEntryField__c + true + + + false + LoggerScenario__c.SomeLoggerScenarioField__c + true + false Salesforce diff --git a/package.json b/package.json index 6be3f405b..d10245e60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nebula-logger", - "version": "4.14.5", + "version": "4.14.6", "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", diff --git a/sfdx-project.json b/sfdx-project.json index eeab032af..e4cf5949d 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -9,9 +9,9 @@ "path": "./nebula-logger/core", "definitionFile": "./config/scratch-orgs/build-base-scratch-def.json", "scopeProfiles": true, - "versionNumber": "4.14.5.NEXT", - "versionName": "Added Logger Settings to Utility Bar", - "versionDescription": "Added the loggerSettings LWC to the LoggerConsole app's utility bar flexipage so settings can be easily viewed/edited from anywhere in the console app", + "versionNumber": "4.14.6.NEXT", + "versionName": "Custom Field Mappings Support for Lightning Components", + "versionDescription": "Added the ability to set custom fields in JavaScript via a new function setField() in logEntryBuilder.js. This is equivalent to the Apex method overloads setField() in LogEntryEventBuilder.cls that were introduced in v4.13.14.", "releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases", "unpackagedMetadata": { "path": "./nebula-logger/extra-tests" @@ -190,6 +190,7 @@ "Nebula Logger - Core@4.14.3-logentryrecordpage-tab-visibility-bugfixes": "04t5Y0000015oQjQAI", "Nebula Logger - Core@4.14.4-optionally-auto-call-lightning-logger-lwc": "04t5Y0000015oRNQAY", "Nebula Logger - Core@4.14.5-added-logger-settings-to-utility-bar": "04t5Y0000015oRXQAY", + "Nebula Logger - Core@4.14.6-custom-field-mappings-support-for-lightning-components": "04t5Y0000015oRhQAI", "Nebula Logger - Core Plugin - Async Failure Additions": "0Ho5Y000000blO4SAI", "Nebula Logger - Core Plugin - Async Failure Additions@1.0.0": "04t5Y0000015lhiQAA", "Nebula Logger - Core Plugin - Async Failure Additions@1.0.1": "04t5Y0000015lhsQAA",