From 2445dcb05f24074c5d60872477d261c03ec2279a Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Fri, 11 Oct 2024 18:26:50 +0000 Subject: [PATCH] New synchronous JS function getLogger() + deprecated async function createLogger() (#775) * Fixed #728 by adding a new function getLogger() in logger LWC that can be called synchronously, and deprecated the async function createLogger() * This simplifies how developers use the logger LWC, and avoids some lingering JS stack trace issues that occur in async functions * The function createLogger() is still supported & functional, but it's now considered deprecated since it requires using 'await' * Resolved #763 by adding new logger.exception() JS function (equivalent to the Apex method Logger.exception()) * Fixed #776 by updating logic in loggerStackTrace.js to better handle parsing when lightning debug mode is disabled. Previously, stack traces worked when debug mode was enabled, but were inaccurate when debug mode was off due to some subtle differences in the generated stack traces * Changed recipes metadata to add a new demo LWC for the new/recommended getLogger() function, and updated the existing demo LWC for the createLogger() function to indicate that it's now deprecated * Scope creep: updated LogEntryEventBuilder to always set LoggedByUsername__c using System.UserInfo methods. Previously, it was only set when synchronous querying of User data was enabled --- .eslintignore | 3 +- README.md | 61 +- .../Logger_Test_Aura_Site1/views/home.json | 10 +- .../Logger_Test_LWR_Site1/views/home.json | 10 +- docs/apex/Logger-Engine/ComponentLogger.md | 4 +- docs/lightning-components/LogEntryBuilder.md | 181 +- docs/lightning-components/Logger.md | 69 +- .../logger-engine/classes/ComponentLogger.cls | 4 +- .../classes/LogEntryEventBuilder.cls | 2 +- .../main/logger-engine/classes/Logger.cls | 2 +- ...json => chromeBrowserError_debugMode.json} | 0 .../chromeBrowserError_withoutDebugMode.json | 3 + ...r.json => edgeBrowserError_debugMode.json} | 0 .../edgeBrowserError_withoutDebugMode.json | 3 + ...son => firefoxBrowserError_debugMode.json} | 0 .../firefoxBrowserError_withoutDebugMode.json | 3 + .../__tests__/data/getLoggerSettings.json | 3 +- .../lwc/logger/__tests__/logger.test.js | 1159 +++- .../__tests__/loggerServiceTaskQueue.test.js | 80 + .../logger/__tests__/loggerStackTrace.test.js | 53 +- .../lwc/logger/logEntryBuilder.js | 152 +- .../main/logger-engine/lwc/logger/logger.js | 44 +- .../logger-engine/lwc/logger/loggerService.js | 197 +- .../lwc/logger/loggerServiceTaskQueue.js | 59 + .../lwc/logger/loggerStackTrace.js | 90 +- .../classes/ComponentLogger_Tests.cls | 2 +- .../classes/LogEntryEventBuilder_Tests.cls | 18 +- .../profiles/Admin.profile-meta.xml | 5078 ----------------- .../applications/LoggerRecipes.app-meta.xml | 3 +- ...pes_Account_Record_Page.flexipage-meta.xml | 15 +- ...ogger_Recipes_Home_Page.flexipage-meta.xml | 10 +- ...ger_Recipes_Utility_Bar.flexipage-meta.xml | 42 +- ...er_Recipes_Flow_Screen_Demos.flow-meta.xml | 36 +- .../loggerLWCCreateLoggerImportDemo.test.js | 45 + .../loggerLWCCreateLoggerImportDemo.html | 40 + .../loggerLWCCreateLoggerImportDemo.js} | 58 +- ...ggerLWCCreateLoggerImportDemo.js-meta.xml} | 0 .../loggerLWCEmbedDemo.html | 2 +- .../loggerLWCEmbedDemo/loggerLWCEmbedDemo.js | 8 +- .../loggerLWCGetLoggerImportDemo.test.js} | 4 +- .../loggerLWCGetLoggerImportDemo.html} | 6 +- .../loggerLWCGetLoggerImportDemo.js | 165 + .../loggerLWCGetLoggerImportDemo.js-meta.xml | 15 + .../LoggerRecipesAdmin.permissionset-meta.xml | 6 +- ...reateLoggerImportDemo.quickAction-meta.xml | 8 + ...CGetLoggerImportDemo.quickAction-meta.xml} | 4 +- ...LWC_Create_Logger_Import_Demo.tab-meta.xml | 6 + ...r_LWC_Get_Logger_Import_Demo.tab-meta.xml} | 4 +- package.json | 2 +- scripts/build/sync-package-version-number.ps1 | 4 +- sfdx-project.json | 7 +- 51 files changed, 2187 insertions(+), 5593 deletions(-) rename nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/{chromeBrowserError.json => chromeBrowserError_debugMode.json} (100%) create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json rename nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/{edgeBrowserError.json => edgeBrowserError_debugMode.json} (100%) create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json rename nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/{firefoxBrowserError.json => firefoxBrowserError_debugMode.json} (100%) create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerServiceTaskQueue.test.js create mode 100644 nebula-logger/core/main/logger-engine/lwc/logger/loggerServiceTaskQueue.js create mode 100644 nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/__tests__/loggerLWCCreateLoggerImportDemo.test.js create mode 100644 nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.html rename nebula-logger/recipes/lwc/{loggerLWCImportDemo/loggerLWCImportDemo.js => loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js} (71%) rename nebula-logger/recipes/lwc/{loggerLWCImportDemo/loggerLWCImportDemo.js-meta.xml => loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js-meta.xml} (100%) rename nebula-logger/recipes/lwc/{loggerLWCImportDemo/__tests__/loggerLWCImportDemo.test.js => loggerLWCGetLoggerImportDemo/__tests__/loggerLWCGetLoggerImportDemo.test.js} (86%) rename nebula-logger/recipes/lwc/{loggerLWCImportDemo/loggerLWCImportDemo.html => loggerLWCGetLoggerImportDemo/loggerLWCGetLoggerImportDemo.html} (91%) create mode 100644 nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/loggerLWCGetLoggerImportDemo.js create mode 100644 nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/loggerLWCGetLoggerImportDemo.js-meta.xml create mode 100644 nebula-logger/recipes/quickActions/Account.loggerLWCCreateLoggerImportDemo.quickAction-meta.xml rename nebula-logger/recipes/quickActions/{Account.loggerLWCImportDemo.quickAction-meta.xml => Account.loggerLWCGetLoggerImportDemo.quickAction-meta.xml} (65%) create mode 100644 nebula-logger/recipes/tabs/Logger_LWC_Create_Logger_Import_Demo.tab-meta.xml rename nebula-logger/recipes/tabs/{Logger_LWC_Import_Demo.tab-meta.xml => Logger_LWC_Get_Logger_Import_Demo.tab-meta.xml} (55%) diff --git a/.eslintignore b/.eslintignore index 30bc243c8..752541413 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,5 @@ .husky/ .sfdx/ .vscode/ -test-coverage/ \ No newline at end of file +temp/ +test-coverage/ diff --git a/README.md b/README.md index 451e5b84f..6b5701092 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,17 @@ [![Build](https://github.com/jongpie/NebulaLogger/actions/workflows/build.yml/badge.svg)](https://github.com/jongpie/NebulaLogger/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/jongpie/NebulaLogger/branch/main/graph/badge.svg?token=1DJPDRM3N4)](https://codecov.io/gh/jongpie/NebulaLogger) -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. +The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, OmniStudio, and integrations. -## Unlocked Package - v4.14.12 +## Unlocked Package - v4.14.13 -[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oV0QAI) -[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oV0QAI) +[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oW3QAI) +[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oW3QAI) [![View Documentation](./images/btn-view-documentation.png)](https://github.com/jongpie/NebulaLogger/wiki) -`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oV0QAI` +`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oW3QAI` -`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oV0QAI` +`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oW3QAI` --- @@ -157,14 +157,13 @@ This results in 1 `Log__c` record with several related `LogEntry__c` records. For lightning component developers, the `logger` LWC provides very similar functionality that is offered in Apex. Simply incorporate the `logger` LWC into your component, and call the desired logging methods within your code. ```javascript -// For LWC, import logger's createLogger() function into your component -import { createLogger } from 'c/logger'; +// For LWC, import logger's getLogger() function into your component +import { getLogger } from 'c/logger'; -export default class LoggerLWCImportDemo extends LightningElement { - logger; +export default class LoggerDemo extends LightningElement { + logger = getLogger(); - async connectedCallback() { - this.logger = await createLogger(); + connectedCallback() { this.logger.info('Hello, world'); this.logger.saveLog(); } @@ -432,21 +431,31 @@ Each `LogEntry__c` record automatically stores the component's type ('Aura' or ' #### Example LWC Usage -For lightning component developers, the `logger` LWC provides very similar functionality that is offered in Apex. Simply import the `logger` LWC in your component, and call the desired logging methods within your code. +For lightning component developers, the `logger` LWC provides very similar functionality that is offered in Apex. Simply import the `getLogger` function in your component, use it to initialize an instance once per component, and call the desired logging methods within your code. ```javascript // For LWC, import logger's createLogger() function into your component -import { createLogger } from 'c/logger'; +import { getLogger } from 'c/logger'; +import callSomeApexMethod from '@salesforce/apex/LoggerLWCDemoController.callSomeApexMethod'; -export default class LoggerLWCImportDemo extends LightningElement { - logger; +export default class LoggerDemo extends LightningElement { + // Call getLogger() once per component + logger = getLogger(); async connectedCallback() { - // Call createLogger() once per component - this.logger = await createLogger(); - this.logger.setScenario('some scenario'); - this.logger.finer('initialized demo LWC'); + this.logger.finer('initialized demo LWC, using async connectedCallback'); + } + + @wire(callSomeApexMethod) + wiredCallSomeApexMethod({ error, data }) { + this.logger.info('logging inside a wire function'); + if (data) { + this.logger.info('wire function return value: ' + data); + } + if (error) { + this.logger.error('wire function error: ' + JSON.stringify(error)); + } } logSomeStuff() { @@ -467,7 +476,10 @@ export default class LoggerLWCImportDemo extends LightningElement { this.logger.debug('TODO - finishing implementation of doSomething()').addTag('another tag'); // TODO add the function's implementation below } catch (thrownError) { - this.logger.error('An unexpected error log entry using Nebula Logger with logging level == ERROR').setError(thrownError).addTag('some important tag'); + this.logger + .error('An unexpected error log entry using Nebula Logger with logging level == ERROR') + .setExceptionDetails(thrownError) + .addTag('some important tag'); } finally { this.logger.saveLog(); } @@ -660,13 +672,12 @@ The first step is to add a field to the platform event `LogEntryEvent__e` - In JavaScript, populate your field(s) by calling 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; + export default class loggerLWCGetLoggerImportDemo extends LightningElement { + logger = getLogger(); 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/config/scratch-orgs/experience-cloud/experiences/Logger_Test_Aura_Site1/views/home.json b/config/scratch-orgs/experience-cloud/experiences/Logger_Test_Aura_Site1/views/home.json index eb14305a2..cfa42fde8 100644 --- a/config/scratch-orgs/experience-cloud/experiences/Logger_Test_Aura_Site1/views/home.json +++ b/config/scratch-orgs/experience-cloud/experiences/Logger_Test_Aura_Site1/views/home.json @@ -26,7 +26,15 @@ "components": [ { "componentAttributes": {}, - "componentName": "c:loggerLWCImportDemo", + "componentName": "c:loggerLWCGetLoggerImportDemo", + "id": "715fc8fd-8d07-436a-bbbf-e0f45c93eedc", + "renderPriority": "NEUTRAL", + "renditionMap": {}, + "type": "component" + }, + { + "componentAttributes": {}, + "componentName": "c:loggerLWCCreateLoggerImportDemo", "id": "ef2d20d3-acfe-49ec-935e-e08ea2c6ec85", "renderPriority": "NEUTRAL", "renditionMap": {}, diff --git a/config/scratch-orgs/experience-cloud/experiences/Logger_Test_LWR_Site1/views/home.json b/config/scratch-orgs/experience-cloud/experiences/Logger_Test_LWR_Site1/views/home.json index 65f79c5c9..5811467b0 100644 --- a/config/scratch-orgs/experience-cloud/experiences/Logger_Test_LWR_Site1/views/home.json +++ b/config/scratch-orgs/experience-cloud/experiences/Logger_Test_LWR_Site1/views/home.json @@ -20,7 +20,15 @@ "components": [ { "componentAttributes": {}, - "componentName": "c:loggerLWCImportDemo", + "componentName": "c:loggerLWCGetLoggerImportDemo", + "id": "534c7039-bb9e-49b9-a00a-92fdb1665bad", + "renderPriority": "NEUTRAL", + "renditionMap": {}, + "type": "component" + }, + { + "componentAttributes": {}, + "componentName": "c:loggerLWCCreateLoggerImportDemo", "id": "eb678e7a-f879-4166-9d05-c3287a523795", "renderPriority": "NEUTRAL", "renditionMap": {}, diff --git a/docs/apex/Logger-Engine/ComponentLogger.md b/docs/apex/Logger-Engine/ComponentLogger.md index be7b38dbc..6958efa6b 100644 --- a/docs/apex/Logger-Engine/ComponentLogger.md +++ b/docs/apex/Logger-Engine/ComponentLogger.md @@ -55,7 +55,7 @@ return The transaction ID (based on `Logger.getTransactionId())` ### Inner Classes -#### ComponentLogger.ComponentBrowser class +#### ComponentLogger.ComponentBrowserContext class A DTO object used to log details about the user's browser @@ -123,7 +123,7 @@ A DTO object used to create log entries for lightning components ##### Properties -###### `browser` → `ComponentBrowser` +###### `browser` → `ComponentBrowserContext` Context about the user's browser, automatically captured by Nebula Logger diff --git a/docs/lightning-components/LogEntryBuilder.md b/docs/lightning-components/LogEntryBuilder.md index 336ece358..5c4fe4140 100644 --- a/docs/lightning-components/LogEntryBuilder.md +++ b/docs/lightning-components/LogEntryBuilder.md @@ -1,157 +1,186 @@ - - -## LogEntryBuilder - -**Kind**: global class - -- [LogEntryBuilder](#LogEntryBuilder) - - [new LogEntryBuilder(loggingLevel, isConsoleLoggingEnabled, isLightningLoggerEnabled)](#new_LogEntryBuilder_new) - - [.setMessage(message)](#LogEntryBuilder+setMessage) [LogEntryBuilder](#LogEntryBuilder) - - [.setRecordId(recordId)](#LogEntryBuilder+setRecordId) [LogEntryBuilder](#LogEntryBuilder) - - [.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) - - [.getComponentLogEntry()](#LogEntryBuilder+getComponentLogEntry) ComponentLogEntry - - - -### new LogEntryBuilder(loggingLevel, isConsoleLoggingEnabled, isLightningLoggerEnabled) - -Constructor used to generate each JavaScript-based log entry event -This class is the JavaScript-equivalent of the Apex class `LogEntryBuilder` - -| Param | Type | Description | -| ------------------------ | -------------------- | ------------------------------------------------------------------------------- | -| loggingLevel | String | The `LoggingLevel` enum to use for the builder's instance of `LogEntryEvent__e` | -| isConsoleLoggingEnabled | Boolean | Determines if `console.log()` methods are execute | -| isLightningLoggerEnabled | Boolean | Determines if `lightning-logger` LWC is called | - - - -### logEntryBuilder.setMessage(message) [LogEntryBuilder](#LogEntryBuilder) +## Functions + +
+
setMessage(message) LogEntryBuilder
+

Sets the log entry event's message field

+
+
setRecordId(recordId) LogEntryBuilder
+

Sets the log entry event's record fields

+
+
setRecord(record) LogEntryBuilder
+

Sets the log entry event's record fields

+
+
setScenario(scenario) LogEntryBuilder
+

Sets the log entry event's scenario field

+
+
setError(error) LogEntryBuilder
+

Deprecated - use setExceptionDetails(exception) instead + The name of this method is very similar to the logger function logger.error(), + resulting in confusion when used together: + logger.error('Unexpected error').setError(someErrorObject); + The new setExceptionDetails(exception) function provides the exact same functionality, + but aligns with the Apex builder's method name, and helps reduce the confusion with logger.error()

+
+
setExceptionDetails(exception) LogEntryBuilder
+

Sets the log entry event's exception fields

+
+
setField(fieldToValue) LogEntryBuilder
+

Sets multiple field values on the builder's LogEntryEvent__e record

+
+
parseStackTrace(originStackTraceError) LogEntryBuilder
+

Parses the provided error's stack trace and sets the log entry's origin & stack trace fields

+
+
addTag(tag) LogEntryBuilder
+

Appends the tag to the existing list of tags

+
+
addTags(tags) LogEntryBuilder
+

Appends the tag to the existing list of tags

+
+
getComponentLogEntry() ComponentLogEntry
+

Returns the object used to save log entry data

+
+
+ + + +## setMessage(message) LogEntryBuilder Sets the log entry event's message field -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ------- | ------------------- | -------------------------------------------------- | | message | String | The string to use to set the entry's message field | - + -### logEntryBuilder.setRecordId(recordId) [LogEntryBuilder](#LogEntryBuilder) +## setRecordId(recordId) LogEntryBuilder Sets the log entry event's record fields -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | -------- | ------------------- | ------------------------------------------------- | | recordId | String | The ID of the SObject record related to the entry | - + -### logEntryBuilder.setRecord(record) [LogEntryBuilder](#LogEntryBuilder) +## setRecord(record) LogEntryBuilder Sets the log entry event's record fields -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ------ | ------------------- | ----------------------------------------------------------------------------------------------------- | | record | Object | The `SObject` record related to the entry. The JSON of the record is automatically added to the entry | - + -### logEntryBuilder.setScenario(scenario) [LogEntryBuilder](#LogEntryBuilder) +## setScenario(scenario) LogEntryBuilder Sets the log entry event's scenario field -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | -------- | ------------------- | --------------------------------------------------- | | scenario | String | The string to use to set the entry's scenario field | - + -### logEntryBuilder.setError(error) [LogEntryBuilder](#LogEntryBuilder) +## setError(error) LogEntryBuilder -Sets the log entry event's exception fields +Deprecated - use `setExceptionDetails(exception)` instead +The name of this method is very similar to the logger function logger.error(), +resulting in confusion when used together: +`logger.error('Unexpected error').setError(someErrorObject);` +The new `setExceptionDetails(exception)` function provides the exact same functionality, +but aligns with the Apex builder's method name, and helps reduce the confusion with `logger.error()` -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ----- | ------------------ | -------------------------------------------------------------------------------- | | error | Error | The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use | - + + +## setExceptionDetails(exception) LogEntryBuilder + +Sets the log entry event's exception fields + +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods + +| Param | Type | Description | +| --------- | ------------------ | -------------------------------------------------------------------------------- | +| exception | Error | The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use | + + -### logEntryBuilder.setField(fieldToValue) [LogEntryBuilder](#LogEntryBuilder) +## setField(fieldToValue) 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 +**Kind**: global function +**Returns**: 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) +## parseStackTrace(originStackTraceError) LogEntryBuilder Parses the provided error's stack trace and sets the log entry's origin & stack trace fields -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods -| Param | Type | Description | -| ----- | ------------------ | ----------------------------------------------------------------------- | -| error | Error | The instance of a JavaScript `Error` object with a stack trace to parse | +| Param | Type | Description | +| --------------------- | ------------------ | ----------------------------------------------------------------------- | +| originStackTraceError | Error | The instance of a JavaScript `Error` object with a stack trace to parse | - + -### logEntryBuilder.addTag(tag) [LogEntryBuilder](#LogEntryBuilder) +## addTag(tag) LogEntryBuilder Appends the tag to the existing list of tags -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ----- | ------------------- | ---------------------------------------------------- | | tag | String | The string to add as a tag for the current log entry | - + -### logEntryBuilder.addTags(tags) [LogEntryBuilder](#LogEntryBuilder) +## addTags(tags) LogEntryBuilder Appends the tag to the existing list of tags -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) -**Returns**: [LogEntryBuilder](#LogEntryBuilder) - The same instance of `LogEntryBuilder`, useful for chaining methods +**Kind**: global function +**Returns**: LogEntryBuilder - The same instance of `LogEntryBuilder`, useful for chaining methods | Param | Type | Description | | ----- | --------------------------------- | -------------------------------------------------------- | | tags | Array.<String> | The list of strings to add as tags for the current entry | - + -### logEntryBuilder.getComponentLogEntry() ComponentLogEntry +## getComponentLogEntry() ComponentLogEntry Returns the object used to save log entry data -**Kind**: instance method of [LogEntryBuilder](#LogEntryBuilder) +**Kind**: global function **Returns**: ComponentLogEntry - An instance of `ComponentLogEntry` that matches the Apex class `ComponentLogger.ComponentLogEntry` diff --git a/docs/lightning-components/Logger.md b/docs/lightning-components/Logger.md index 5cae829f0..8bfc5866c 100644 --- a/docs/lightning-components/Logger.md +++ b/docs/lightning-components/Logger.md @@ -1,10 +1,3 @@ -## Constants - -
-
createLogger Promise.<LoggerService>
-
-
- ## Functions
@@ -15,6 +8,10 @@

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

+
exception(message, exception) LogEntryBuilder
+

Creates a new log entry with logging level == LoggingLevel.ERROR, + automatically saves the log, and then throws the provided exception

+
error(message) LogEntryBuilder

Creates a new log entry with logging level == LoggingLevel.ERROR

@@ -46,14 +43,21 @@

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

+
createLogger() Promise.<LoggerService>
+

Deprecated - use getLogger() instead + Async function that returns a fully-loaded logger service. + Requires some code to be executed async, so the service is not immediately available. + Example: const logger = await createLogger();

+
+
getLogger() LoggerService
+

Recommended approach + Synchronous function that returns a ready-to-use logger service. + Internally, some code is still executed async, but the service can immediately + be used, without awaiting a Promise + Example: const logger = getLogger();

+
- - -## createLogger Promise.<LoggerService> - -**Kind**: global constant -**Returns**: Promise.<LoggerService> - a LoggerService instance ## getUserSettings() ComponentLogger.ComponentLoggerSettings @@ -75,6 +79,21 @@ and `Log__c.Scenario__c`, and can be used to filter & group logs | -------- | ------------------- | ------------------------------------------------------ | | scenario | String | The name to use for the current transaction's scenario | + + +## exception(message, exception) LogEntryBuilder + +Creates a new log entry with logging level == `LoggingLevel.ERROR`, +automatically saves the log, and then throws the provided exception + +**Kind**: global function +**Returns**: LogEntryBuilder - The new entry's instance of `LogEntryEventBuilder`, useful for chaining methods + +| Param | Type | Description | +| --------- | ------------------- | -------------------------------------------------------------------------------- | +| message | String | The string to use to set the entry's message field | +| exception | Error | The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use | + ## error(message) LogEntryBuilder @@ -193,3 +212,27 @@ All subsequent calls to saveLog() will use the transaction save method | Param | Type | Description | | ---------- | ------------------- | ------------------------------------------------------------------------ | | saveMethod | String | The enum value of Logger.SaveMethod to use for this specific save action | + + + +## createLogger() Promise.<LoggerService> + +Deprecated - use `getLogger()` instead +Async function that returns a fully-loaded logger service. +Requires some code to be executed async, so the service is not immediately available. +Example: `const logger = await createLogger();` + +**Kind**: global function +**Returns**: Promise.<LoggerService> - A Promise that resolves an instance of `LoggerService` + + +## getLogger() LoggerService + +Recommended approach +Synchronous function that returns a ready-to-use logger service. +Internally, some code is still executed async, but the service can immediately +be used, without awaiting a Promise +Example: `const logger = getLogger();` + +**Kind**: global function +**Returns**: LoggerService - An instance of `LoggerService` diff --git a/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls b/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls index 9d465d5b0..6cb3b14bb 100644 --- a/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls +++ b/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls @@ -307,7 +307,7 @@ public inherited sharing class ComponentLogger { * @description Context about the user's browser, automatically captured by Nebula Logger */ @AuraEnabled - public ComponentBrowser browser { get; set; } + public ComponentBrowserContext browser { get; set; } /** * @description (Optional) A JavaScript Error to log @@ -379,7 +379,7 @@ public inherited sharing class ComponentLogger { /** * @description A DTO object used to log details about the user's browser */ - public class ComponentBrowser { + public class ComponentBrowserContext { /** * @description The URL displayed in the user's browser */ diff --git a/nebula-logger/core/main/logger-engine/classes/LogEntryEventBuilder.cls b/nebula-logger/core/main/logger-engine/classes/LogEntryEventBuilder.cls index 741c989c1..6d8938bf2 100644 --- a/nebula-logger/core/main/logger-engine/classes/LogEntryEventBuilder.cls +++ b/nebula-logger/core/main/logger-engine/classes/LogEntryEventBuilder.cls @@ -996,7 +996,6 @@ global with sharing class LogEntryEventBuilder { } logEntryEvent.LoggedByFederationIdentifier__c = CACHED_USER.FederationIdentifier; - logEntryEvent.LoggedByUsername__c = CACHED_USER.Username; logEntryEvent.ProfileName__c = CACHED_USER.Profile.Name; logEntryEvent.UserLicenseDefinitionKey__c = CACHED_USER.Profile.UserLicense.LicenseDefinitionKey; logEntryEvent.UserLicenseId__c = CACHED_USER.Profile.UserLicenseId; @@ -1007,6 +1006,7 @@ global with sharing class LogEntryEventBuilder { private static void setUserInfoDetails(LogEntryEvent__e templateLogEntryEvent) { templateLogEntryEvent.Locale__c = System.UserInfo.getLocale(); templateLogEntryEvent.LoggedById__c = System.UserInfo.getUserId(); + templateLogEntryEvent.LoggedByUsername__c = System.UserInfo.getUsername(); templateLogEntryEvent.ProfileId__c = System.UserInfo.getProfileId(); templateLogEntryEvent.ThemeDisplayed__c = System.UserInfo.getUiThemeDisplayed(); templateLogEntryEvent.TimeZoneId__c = System.UserInfo.getTimeZone().getId(); diff --git a/nebula-logger/core/main/logger-engine/classes/Logger.cls b/nebula-logger/core/main/logger-engine/classes/Logger.cls index f65ed72ae..a95882434 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.12'; + private static final String CURRENT_VERSION_NUMBER = 'v4.14.13'; 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__/data/chromeBrowserError.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_debugMode.json similarity index 100% rename from nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError.json rename to nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_debugMode.json diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json new file mode 100644 index 000000000..3dc8cc4ad --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/chromeBrowserError_withoutDebugMode.json @@ -0,0 +1,3 @@ +{ + "stack": "Error\n at Proxy.info (modules/c/logger.js:3:10548)\n at eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:23715)\n at en. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:67030)\n at mo (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:43179)\n at c. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:21644)\n at a. [as info] (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:542:412)\n at https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:85471\n at en.eval [as applyTrapForOneOrMoreArgs] (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4751)\n at en.eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4255)\n at saveLogExample (https://dream-ability-7823.scratch.lightning.force.com/lightning/page/components/c/loggerAuraEmbedDemo.js:14:12)" +} diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_debugMode.json similarity index 100% rename from nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError.json rename to nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_debugMode.json diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json new file mode 100644 index 000000000..09d45d611 --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/edgeBrowserError_withoutDebugMode.json @@ -0,0 +1,3 @@ +{ + "stack": "Error\nat Proxy.info (modules/c/logger.js:3:11782)\nat eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:23715)\nat en. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:67030)\nat mo (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:43179)\nat c. (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:21644)\nat a.info (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:542:412)\nat https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:85471\nat en.eval [as applyTrapForOneOrMoreArgs] (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4751)\nat en.eval (eval at (https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:98298), :3:4255)\nat saveLogExample (https://dream-ability-7823.scratch.lightning.force.com/lightning/page/components/c/loggerAuraEmbedDemo.js:14:12)" +} diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_debugMode.json similarity index 100% rename from nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError.json rename to nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_debugMode.json diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json new file mode 100644 index 000000000..f4624442f --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/data/firefoxBrowserError_withoutDebugMode.json @@ -0,0 +1,3 @@ +{ + "stack": "info@modules/c/logger.js:3:11782\nzE/ eval:3:23715\nVt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:67030\nmo@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:43179\nSn/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:49:21644\nR.prototype.hi/ eval:3:4751\nRt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js line 105 > eval:3:4255\nsaveLogExample@https://dream-ability-7823.scratch.lightning.force.com/lightning/page/components/c/loggerAuraEmbedDemo.js:14:12\nzE/ eval:3:23715\nHt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:68352\nGt/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:105:66429\nK.prototype.Zc@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:628:177\nK.prototype.Xa@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:627:306\nhandleClick@https://static.lightning.force.com/cs226/aurafile/%7B%22mode%22%3A%22PROD%22%2C%22cac%22%3A1%2C%22app%22%3A%22one%3Aone%22%2C%22ls%22%3A1%2C%22lrmc%22%3A%22-386269907%22%7D/pIFDZ5nqs77gFqsVXe-2Jw/apppart1-4.js:980:6\nK.prototype.Zc@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:628:177\nuj/<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:514:349\nD.Xa@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:994:221\nuj@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:514:333\nXK.prototype.zr@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:1064:402\nZD.prototype.Xl@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:381:255\n$D/b<@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:380:130\nBq@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:514:301\nb@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:990:72\nt@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:28:5715\nEventListener.handleEvent*ar@https://static.lightning.force.com/cs226/auraFW/javascript/eGx3MHlRT1lEMUpQaWVxbGRUM1h0Z2hZX25NdHFVdGpDN3BnWlROY1ZGT3cyNTAuOC40LTYuNC41/aura_prod.js:46:5450\nM.prototype.uk { +jest.mock( + '@salesforce/apex/ComponentLogger.saveComponentLogEntries', + () => { + return { + default: jest.fn() + }; + }, + { virtual: true } +); + +describe('logger lwc recommended sync getLogger() import approach tests', () => { + beforeEach(() => { + disableSystemMessages(); + // One of logger's features (when enabled) is to auto-call the browser's console + // so devs can see a log entry easily. But during Jest tests, seeing all of the + // console statements is... a bit overwhelming, so the global console functions + // are overwritten with an empty function so they're no-ops / they don't show up + // in the test logs + console.error = jest.fn(); + console.warn = jest.fn(); + console.info = jest.fn(); + console.debug = jest.fn(); + console.log = jest.fn(); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns user settings', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + + const userSettings = logger.getUserSettings(); + + expect(userSettings.defaultSaveMethod).toEqual('EVENT_BUS'); + expect(userSettings.isEnabled).toEqual(true); + expect(userSettings.isConsoleLoggingEnabled).toEqual(false); + expect(userSettings.isLightningLoggerEnabled).toEqual(false); + }); + + it('sets a scenario on all subsequent entries', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const scenario = 'some scenario'; + const message = 'some message'; + + const firstLogEntry = logger.finest(message).getComponentLogEntry(); + logger.setScenario(scenario); + const secondLogEntry = logger.info(message).getComponentLogEntry(); + + expect(firstLogEntry.scenario).toBeUndefined(); + expect(secondLogEntry.scenario).toEqual(scenario); + }); + + it('calls console.info a single time on initialization', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + enableSystemMessages(); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + + logger.error('some message'); + logger.warn('some message'); + logger.info('some message'); + logger.debug('some message'); + logger.fine('some message'); + logger.finer('some message'); + logger.finest('some message'); + + await flushPromises('Resolve async task queue'); + expect(console.info).toHaveBeenCalledTimes(1); + const expectedInitializationMessage = 'ℹ️ INFO: logger component initialized\n' + JSON.stringify(new BrowserContext(), null, 2); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.info.mock.calls[0][2]).toBe(expectedInitializationMessage); + }); + + it('logs an ERROR entry', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel ERROR'; + + const logEntry = logger.error(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toEqual(1); + expect(logEntry.loggingLevel).toEqual('ERROR'); + expect(logEntry.message).toEqual(message); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls console.error for an ERROR entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isConsoleLoggingEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel ERROR'; + + const componentLogEntry = logger.error(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(1); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.error.mock.calls[0][2]).toBe('⛔ ERROR: ' + componentLogEntry.message); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls lightning/logger.log for an ERROR entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isLightningLoggerEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel ERROR'; + + const componentLogEntry = logger.error(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(1); + expect(lightningLog.mock.calls[0][0]).toBe(componentLogEntry); + }); + + it('logs a WARN entry', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel WARN'; + + const logEntry = logger.warn(message).getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(1); + expect(logEntry.loggingLevel).toEqual('WARN'); + expect(logEntry.message).toEqual(message); + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls console.warn for an WARN entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isConsoleLoggingEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel WARN'; + + const componentLogEntry = logger.warn(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(1); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.warn.mock.calls[0][2]).toBe('⚠️ WARN: ' + componentLogEntry.message); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls lightning/logger.log for an WARN entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isLightningLoggerEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel WARN'; + + const componentLogEntry = logger.warn(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(1); + expect(lightningLog.mock.calls[0][0]).toBe(componentLogEntry); + }); + + it('logs an INFO entry', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel INFO'; + + const logEntry = logger.info(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toEqual(1); + expect(logEntry.loggingLevel).toEqual('INFO'); + expect(logEntry.message).toEqual(message); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls console.info for an INFO entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isConsoleLoggingEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel INFO'; + + const componentLogEntry = logger.info(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toEqual(1); + expect(componentLogEntry.loggingLevel).toEqual('INFO'); + expect(componentLogEntry.message).toEqual(message); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + // console.info is always called once when c/logger is initialized, + // so in this case, we check for 2 calls / index 1 + expect(console.info).toHaveBeenCalledTimes(1); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.info.mock.calls[0][2]).toBe('ℹ️ INFO: ' + componentLogEntry.message); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls lightning/logger.log for an INFO entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isLightningLoggerEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel INFO'; + + const componentLogEntry = logger.info(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(1); + expect(lightningLog.mock.calls[0][0]).toBe(componentLogEntry); + }); + + it('logs a DEBUG entry', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel DEBUG'; + + const logEntry = logger.debug(message).getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(1); + expect(logEntry.loggingLevel).toEqual('DEBUG'); + expect(logEntry.message).toEqual(message); + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls console.debug for an DEBUG entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isConsoleLoggingEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel DEBUG'; + + const componentLogEntry = logger.debug(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(1); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.debug.mock.calls[0][2]).toBe('🐞 DEBUG: ' + componentLogEntry.message); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls lightning/logger.log for an DEBUG entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isLightningLoggerEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel DEBUG'; + + const componentLogEntry = logger.warn(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(1); + expect(lightningLog.mock.calls[0][0]).toBe(componentLogEntry); + }); + + it('logs a FINE entry', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINE'; + + const logEntry = logger.fine(message).getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(1); + expect(logEntry.loggingLevel).toEqual('FINE'); + expect(logEntry.message).toEqual(message); + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls console.debug for an FINE entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isConsoleLoggingEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINE'; + + const componentLogEntry = logger.fine(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(1); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.debug.mock.calls[0][2]).toBe('👍 FINE: ' + componentLogEntry.message); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls lightning/logger.log for an FINE entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isLightningLoggerEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINE'; + + const componentLogEntry = logger.fine(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(1); + expect(lightningLog.mock.calls[0][0]).toBe(componentLogEntry); + }); + + it('logs a FINER entry', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINER'; + + const logEntry = logger.finer(message).getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(1); + expect(logEntry.loggingLevel).toEqual('FINER'); + expect(logEntry.message).toEqual(message); + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls console.debug for an FINER entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isConsoleLoggingEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINER'; + + const componentLogEntry = logger.finer(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(1); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.debug.mock.calls[0][2]).toBe('👌 FINER: ' + componentLogEntry.message); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls lightning/logger.log for an FINER entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isLightningLoggerEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINER'; + + const componentLogEntry = logger.finer(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(1); + expect(lightningLog.mock.calls[0][0]).toBe(componentLogEntry); + }); + + it('logs a FINEST entry', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINEST'; + + const logEntry = logger.finest(message).getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(1); + expect(logEntry.loggingLevel).toEqual('FINEST'); + expect(logEntry.message).toEqual(message); + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls console.debug for an FINEST entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isConsoleLoggingEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINEST'; + + const componentLogEntry = logger.finest(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(1); + // The first 2 args (index 0 & 1) passed to console statements are a 'Nebula Logger' prefix & text formatting + expect(console.debug.mock.calls[0][2]).toBe('🌟 FINEST: ' + componentLogEntry.message); + expect(lightningLog).toHaveBeenCalledTimes(0); + }); + + it('calls lightning/logger.log for an FINEST entry when enabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isLightningLoggerEnabled: true }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'component log entry with loggingLevel FINEST'; + + const componentLogEntry = logger.finest(message).getComponentLogEntry(); + + await flushPromises('Resolve async task queue'); + expect(console.error).toHaveBeenCalledTimes(0); + expect(console.warn).toHaveBeenCalledTimes(0); + expect(console.info).toHaveBeenCalledTimes(0); + expect(console.debug).toHaveBeenCalledTimes(0); + expect(lightningLog).toHaveBeenCalledTimes(1); + expect(lightningLog.mock.calls[0][0]).toBe(componentLogEntry); + }); + + it('sets browser details', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + await logger.getUserSettings(); + + const logEntry = logger.info('example log entry').getComponentLogEntry(); + + expect(logEntry.browser.address).toEqual(window.location.href); + expect(logEntry.browser.formFactor).toEqual(FORM_FACTOR); + expect(logEntry.browser.language).toEqual(window.navigator.language); + expect(logEntry.browser.screenResolution).toEqual(window.screen.availWidth + ' x ' + window.screen.availHeight); + expect(logEntry.browser.userAgent).toEqual(window.navigator.userAgent); + expect(logEntry.browser.windowResolution).toEqual(window.innerWidth + ' x ' + window.innerHeight); + }); + + it('sets multiple custom fields', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + 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', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + await logger.getUserSettings(); + const logEntryBuilder = logger.info('example log entry'); + const logEntry = logEntryBuilder.getComponentLogEntry(); + expect(logEntry.recordId).toBeFalsy(); + const mockUserId = '0052F000008yLcEQAU'; + + logEntryBuilder.setRecordId(mockUserId); + + expect(logEntry.recordId).toEqual(mockUserId); + }); + + it('sets record', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + await logger.getUserSettings(); + const logEntryBuilder = logger.info('example log entry'); + const logEntry = logEntryBuilder.getComponentLogEntry(); + expect(logEntry.record).toBeFalsy(); + const mockUserRecord = { Id: '0052F000008yLcEQAU', FirstName: 'Jonathan', LastName: 'Gillespie' }; + + logEntryBuilder.setRecord(mockUserRecord); + + expect(logEntry.record).toEqual(mockUserRecord); + }); + + it('sets JavaScript error details', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + await logger.getUserSettings(); + const logEntryBuilder = logger.info('example log entry'); + const logEntry = logEntryBuilder.getComponentLogEntry(); + expect(logEntry.error).toBeFalsy(); + const error = new TypeError('oops'); + expect(error).toBeTruthy(); + expect(error.message).toBeTruthy(); + expect(error.stack).toBeTruthy(); + + logEntryBuilder.setExceptionDetails(error); + + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + }); + + it('sets Apex error details', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const logEntryBuilder = logger.info('example log entry'); + const logEntry = logEntryBuilder.getComponentLogEntry(); + expect(logEntry.error).toBeFalsy(); + const error = { + body: { + exceptionType: 'System.DmlException', + message: 'Some Apex error, oh no!', + stackTrace: 'Class.SomeApexClass.runSomeMethod: line 314, column 42' + } + }; + expect(error).toBeTruthy(); + expect(error.body.exceptionType).toBeTruthy(); + expect(error.body.message).toBeTruthy(); + expect(error.body.stackTrace).toBeTruthy(); + + logEntryBuilder.setExceptionDetails(error); + + expect(logEntry.error.message).toEqual(error.body.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual(error.body.exceptionType); + }); + + it('adds tags', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const logEntryBuilder = logger.info('example log entry'); + const logEntry = logEntryBuilder.getComponentLogEntry(); + expect(logEntry.recordId).toBeFalsy(); + const mockTags = ['first tag', 'second tag', 'third tag']; + + logEntryBuilder.addTags(mockTags); + + expect(logEntry.tags.length).toEqual(mockTags.length); + }); + + it('deduplicates tags', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const logEntryBuilder = logger.info('example log entry'); + const logEntry = logEntryBuilder.getComponentLogEntry(); + expect(logEntry.recordId).toBeFalsy(); + const mockTags = ['duplicate tag', 'duplicate tag']; + expect(mockTags.length).toEqual(2); + expect(new Set(mockTags).size).toEqual(1); + + for (let i = 0; i < mockTags.length; i++) { + logEntryBuilder.addTag(mockTags[i]); + } + + expect(logEntry.tags.length).toEqual(1); + }); + + it('auto-saves log & throws exception', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const message = 'some message'; + const mockError = new TypeError('oops'); + let thrownError; + + try { + logger.exception(message, mockError); + } catch (error) { + thrownError = error; + } + + expect(thrownError).toBe(mockError); + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toBe(0); + expect(saveComponentLogEntries).toHaveBeenCalledTimes(1); + expect(saveComponentLogEntries.mock.calls[0][0].componentLogEntries.length).toEqual(1); + const savedComponentLogEntry = saveComponentLogEntries.mock.calls[0][0].componentLogEntries[0]; + expect(savedComponentLogEntry.loggingLevel).toEqual('ERROR'); + expect(savedComponentLogEntry.message).toEqual(message); + }); + + it('still works for ERROR logging level when disabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const settings = logger.getUserSettings({ forceReload: true }); + expect(settings.isEnabled).toEqual(false); + const error = new TypeError('oops'); + + const logEntry = logger + .error('example ERROR log entry') + .setMessage('some message') + .setRecordId('some_record_Id') + .setRecord({ Id: 'some_record_Id' }) + .setExceptionDetails(error) + .addTag('a tag') + .addTags(['a second tag', 'a third tag']) + .getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(0); + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + expect(logEntry.loggingLevel).toEqual('ERROR'); + expect(logEntry.originStackTrace).toBeTruthy(); + expect(logEntry.record).toEqual({ Id: 'some_record_Id' }); + expect(logEntry.recordId).toEqual('some_record_Id'); + expect(logEntry.tags).toEqual(['a tag', 'a second tag', 'a third tag']); + expect(logEntry.timestamp).toBeTruthy(); + }); + + it('still works for WARN logging level when disabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const settings = await logger.getUserSettings({ forceReload: true }); + expect(settings.isEnabled).toEqual(false); + const error = new TypeError('oops'); + + const logEntry = logger + .warn('example WARN log entry') + .setMessage('some message') + .setRecordId('some_record_Id') + .setRecord({ Id: 'some_record_Id' }) + .setExceptionDetails(error) + .addTag('a tag') + .addTags(['a second tag', 'a third tag']) + .getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(0); + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + expect(logEntry.loggingLevel).toEqual('WARN'); + expect(logEntry.originStackTrace).toBeTruthy(); + expect(logEntry.record).toEqual({ Id: 'some_record_Id' }); + expect(logEntry.recordId).toEqual('some_record_Id'); + expect(logEntry.tags).toEqual(['a tag', 'a second tag', 'a third tag']); + expect(logEntry.timestamp).toBeTruthy(); + }); + + it('still works for INFO logging level when disabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const settings = await logger.getUserSettings({ forceReload: true }); + expect(settings.isEnabled).toEqual(false); + const error = new TypeError('oops'); + + const logEntry = logger + .info('example INFO log entry') + .setMessage('some message') + .setRecordId('some_record_Id') + .setRecord({ Id: 'some_record_Id' }) + .setExceptionDetails(error) + .addTag('a tag') + .addTags(['a second tag', 'a third tag']) + .getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(0); + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + expect(logEntry.loggingLevel).toEqual('INFO'); + expect(logEntry.originStackTrace).toBeTruthy(); + expect(logEntry.record).toEqual({ Id: 'some_record_Id' }); + expect(logEntry.recordId).toEqual('some_record_Id'); + expect(logEntry.tags).toEqual(['a tag', 'a second tag', 'a third tag']); + expect(logEntry.timestamp).toBeTruthy(); + }); + + it('still works for DEBUG logging level when disabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const settings = await logger.getUserSettings({ forceReload: true }); + expect(settings.isEnabled).toEqual(false); + const error = new TypeError('oops'); + + const logEntry = logger + .debug('example DEBUG log entry') + .setMessage('some message') + .setRecordId('some_record_Id') + .setRecord({ Id: 'some_record_Id' }) + .setExceptionDetails(error) + .addTag('a tag') + .addTags(['a second tag', 'a third tag']) + .getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(0); + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + expect(logEntry.loggingLevel).toEqual('DEBUG'); + expect(logEntry.originStackTrace).toBeTruthy(); + expect(logEntry.record).toEqual({ Id: 'some_record_Id' }); + expect(logEntry.recordId).toEqual('some_record_Id'); + expect(logEntry.tags).toEqual(['a tag', 'a second tag', 'a third tag']); + expect(logEntry.timestamp).toBeTruthy(); + }); + + it('still works for FINE logging level when disabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const settings = await logger.getUserSettings({ forceReload: true }); + expect(settings.isEnabled).toEqual(false); + const error = new TypeError('oops'); + + const logEntry = logger + .fine('example FINE log entry') + .setMessage('some message') + .setRecordId('some_record_Id') + .setRecord({ Id: 'some_record_Id' }) + .setExceptionDetails(error) + .addTag('a tag') + .addTags(['a second tag', 'a third tag']) + .getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(0); + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + expect(logEntry.loggingLevel).toEqual('FINE'); + expect(logEntry.originStackTrace).toBeTruthy(); + expect(logEntry.record).toEqual({ Id: 'some_record_Id' }); + expect(logEntry.recordId).toEqual('some_record_Id'); + expect(logEntry.tags).toEqual(['a tag', 'a second tag', 'a third tag']); + expect(logEntry.timestamp).toBeTruthy(); + }); + + it('still works for FINER logging level when disabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const settings = await logger.getUserSettings({ forceReload: true }); + expect(settings.isEnabled).toEqual(false); + const error = new TypeError('oops'); + + const logEntry = logger + .finer('example FINER log entry') + .setMessage('some message') + .setRecordId('some_record_Id') + .setRecord({ Id: 'some_record_Id' }) + .setExceptionDetails(error) + .addTag('a tag') + .addTags(['a second tag', 'a third tag']) + .getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(0); + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + expect(logEntry.loggingLevel).toEqual('FINER'); + expect(logEntry.originStackTrace).toBeTruthy(); + expect(logEntry.record).toEqual({ Id: 'some_record_Id' }); + expect(logEntry.recordId).toEqual('some_record_Id'); + expect(logEntry.tags).toEqual(['a tag', 'a second tag', 'a third tag']); + expect(logEntry.timestamp).toBeTruthy(); + }); + + it('still works for FINEST logging level when disabled', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + const settings = await logger.getUserSettings({ forceReload: true }); + expect(settings.isEnabled).toEqual(false); + const error = new TypeError('oops'); + + const logEntry = logger + .finest('example FINEST log entry') + .setMessage('some message') + .setRecordId('some_record_Id') + .setRecord({ Id: 'some_record_Id' }) + .setExceptionDetails(error) + .addTag('a tag') + .addTags(['a second tag', 'a third tag']) + .getComponentLogEntry(); + + expect(logger.getBufferSize()).toEqual(0); + expect(logEntry.error.message).toEqual(error.message); + expect(logEntry.error.stackTrace).toBeTruthy(); + expect(logEntry.error.type).toEqual('JavaScript.TypeError'); + expect(logEntry.loggingLevel).toEqual('FINEST'); + expect(logEntry.originStackTrace).toBeTruthy(); + expect(logEntry.record).toEqual({ Id: 'some_record_Id' }); + expect(logEntry.recordId).toEqual('some_record_Id'); + expect(logEntry.tags).toEqual(['a tag', 'a second tag', 'a third tag']); + expect(logEntry.timestamp).toBeTruthy(); + }); + + it('flushes buffer', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + await logger.getUserSettings({ forceReload: true }); + const numberOfLogEntries = 3; + for (let i = 0; i < numberOfLogEntries; i++) { + logger.info('entry number: ' + i); + } + expect(logger.getBufferSize()).toEqual(numberOfLogEntries); + + await logger.flushBuffer(); + + expect(logger.getBufferSize()).toEqual(0); + }); + + it('saves log entries and flushes buffer', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); + await logger.getUserSettings({ forceReload: true }); + const firstEntryBuilder = logger.info('example INFO log entry added BEFORE saveLog()'); + const secondEntryBuilder = logger.debug('example DEBUG log entry added BEFORE saveLog()'); + await flushPromises(); + expect(logger.getBufferSize()).toBe(2); + + logger.saveLog(); + logger.warn('example WARN log entry added AFTER saveLog()'); + + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toBe(1); + expect(saveComponentLogEntries).toHaveBeenCalledTimes(1); + const expectedApexParameter = { + componentLogEntries: [firstEntryBuilder.getComponentLogEntry(), secondEntryBuilder.getComponentLogEntry()], + saveMethodName: undefined + }; + expect(saveComponentLogEntries.mock.calls[0][0]).toEqual(expectedApexParameter); + }); +}); + +describe('logger lwc deprecated async createLogger() import tests', () => { + beforeEach(() => { + disableSystemMessages(); + // One of logger's features (when enabled) is to auto-call the browser's console + // so devs can see a log entry easily. But during Jest tests, seeing all of the + // console statements is... a bit overwhelming, so the global console functions + // are overwritten with an empty function so they're no-ops / they don't show up + // in the test logs + const emptyFunction = () => ''; + console.error = emptyFunction; + console.warn = emptyFunction; + console.info = emptyFunction; + console.debug = emptyFunction; + }); afterEach(async () => { jest.clearAllMocks(); }); - it('returns user settings when using recommended import approach', async () => { + it('returns user settings when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); @@ -39,11 +1020,16 @@ describe('logger lwc import tests', () => { expect(userSettings.defaultSaveMethod).toEqual('EVENT_BUS'); expect(userSettings.isEnabled).toEqual(true); - expect(userSettings.isConsoleLoggingEnabled).toEqual(true); + expect(userSettings.isConsoleLoggingEnabled).toEqual(false); + expect(userSettings.isLightningLoggerEnabled).toEqual(false); }); - it('sets a log scenario on all entries when using recommended import approach', async () => { + it('sets a scenario on all subsequent entries when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + // const logger = getLogger(); + // getLogger() is built to be sync, but internally, some async tasks must execute + // before some sync tasks are executed + await flushPromises('Resolve async task queue'); const logger = await createLogger(); const scenario = 'some scenario'; const message = 'some message'; @@ -52,11 +1038,11 @@ describe('logger lwc import tests', () => { logger.setScenario(scenario); const secondLogEntry = logger.info(message).getComponentLogEntry(); - expect(firstLogEntry.scenario).toEqual(scenario); + expect(firstLogEntry.scenario).toBeUndefined(); expect(secondLogEntry.scenario).toEqual(scenario); }); - it('logs an ERROR entry when using recommended import approach', async () => { + it('logs an ERROR entry when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const message = 'component log entry with loggingLevel ERROR'; @@ -69,7 +1055,7 @@ describe('logger lwc import tests', () => { expect(logEntry.message).toEqual(message); }); - it('logs a WARN entry when using recommended import approach', async () => { + it('logs a WARN entry when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const message = 'component log entry with loggingLevel WARN'; @@ -82,7 +1068,7 @@ describe('logger lwc import tests', () => { expect(logEntry.message).toEqual(message); }); - it('logs an INFO entry when using recommended import approach', async () => { + it('logs an INFO entry when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const message = 'component log entry with loggingLevel INFO'; @@ -95,7 +1081,7 @@ describe('logger lwc import tests', () => { expect(logEntry.message).toEqual(message); }); - it('logs a DEBUG entry when using recommended import approach', async () => { + it('logs a DEBUG entry when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const message = 'component log entry with loggingLevel DEBUG'; @@ -108,7 +1094,7 @@ describe('logger lwc import tests', () => { expect(logEntry.message).toEqual(message); }); - it('logs a FINE entry when using recommended import approach', async () => { + it('logs a FINE entry when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const message = 'component log entry with loggingLevel FINE'; @@ -121,7 +1107,7 @@ describe('logger lwc import tests', () => { expect(logEntry.message).toEqual(message); }); - it('logs a FINER entry when using recommended import approach', async () => { + it('logs a FINER entry when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const message = 'component log entry with loggingLevel FINER'; @@ -134,20 +1120,19 @@ describe('logger lwc import tests', () => { expect(logEntry.message).toEqual(message); }); - it('logs a FINEST entry when using recommended import approach', async () => { + it('logs a FINEST entry when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const message = 'component log entry with loggingLevel FINEST'; const logEntry = logger.finest(message).getComponentLogEntry(); - await flushPromises(); expect(logger.getBufferSize()).toEqual(1); expect(logEntry.loggingLevel).toEqual('FINEST'); expect(logEntry.message).toEqual(message); }); - it('sets browser details when using recommended import approach', async () => { + it('sets browser details when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); await logger.getUserSettings(); @@ -162,7 +1147,7 @@ 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 fields when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); await logger.getUserSettings(); @@ -184,7 +1169,7 @@ describe('logger lwc import tests', () => { expect(logEntry.fieldToValue[secondFakeFieldName]).toEqual(secondFieldMockValue); }); - it('sets recordId when using recommended import approach', async () => { + it('sets recordId when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); await logger.getUserSettings(); @@ -198,7 +1183,7 @@ describe('logger lwc import tests', () => { expect(logEntry.recordId).toEqual(mockUserId); }); - it('sets record when using recommended import approach', async () => { + it('sets record when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); await logger.getUserSettings(); @@ -212,7 +1197,7 @@ describe('logger lwc import tests', () => { expect(logEntry.record).toEqual(mockUserRecord); }); - it('sets JavaScript error details when using recommended import approach', async () => { + it('sets JavaScript error details when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); await logger.getUserSettings(); @@ -231,7 +1216,7 @@ describe('logger lwc import tests', () => { expect(logEntry.error.type).toEqual('JavaScript.TypeError'); }); - it('sets Apex error details when using recommended import approach', async () => { + it('sets Apex error details when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const logEntryBuilder = logger.info('example log entry'); @@ -256,7 +1241,7 @@ describe('logger lwc import tests', () => { expect(logEntry.error.type).toEqual(error.body.exceptionType); }); - it('adds tags when using recommended import approach', async () => { + it('adds tags when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const logEntryBuilder = logger.info('example log entry'); @@ -269,7 +1254,7 @@ describe('logger lwc import tests', () => { expect(logEntry.tags.length).toEqual(mockTags.length); }); - it('deduplicates tags when using recommended import approach', async () => { + it('deduplicates tags when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); const logEntryBuilder = logger.info('example log entry'); @@ -286,7 +1271,30 @@ describe('logger lwc import tests', () => { expect(logEntry.tags.length).toEqual(1); }); - it('still works for ERROR logging level when disabled when using recommended import approach', async () => { + it('auto-saves log & throws exception when using deprecated async createLogger() import approach', async () => { + getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); + const logger = await createLogger(); + const message = 'some message'; + const mockError = new TypeError('oops'); + let thrownError; + + try { + logger.exception(message, mockError); + } catch (error) { + thrownError = error; + } + + expect(thrownError).toBe(mockError); + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toBe(0); + expect(saveComponentLogEntries).toHaveBeenCalledTimes(1); + expect(saveComponentLogEntries.mock.calls[0][0].componentLogEntries.length).toEqual(1); + const savedComponentLogEntry = saveComponentLogEntries.mock.calls[0][0].componentLogEntries[0]; + expect(savedComponentLogEntry.loggingLevel).toEqual('ERROR'); + expect(savedComponentLogEntry.message).toEqual(message); + }); + + it('still works for ERROR logging level when disabled when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = await createLogger(); const settings = logger.getUserSettings({ forceReload: true }); @@ -316,7 +1324,7 @@ describe('logger lwc import tests', () => { expect(logEntry.timestamp).toBeTruthy(); }); - it('still works for WARN logging level when disabled when using recommended import approach', async () => { + it('still works for WARN logging level when disabled when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = await createLogger(); const settings = await logger.getUserSettings({ forceReload: true }); @@ -346,7 +1354,7 @@ describe('logger lwc import tests', () => { expect(logEntry.timestamp).toBeTruthy(); }); - it('still works for INFO logging level when disabled when using recommended import approach', async () => { + it('still works for INFO logging level when disabled when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = await createLogger(); const settings = await logger.getUserSettings({ forceReload: true }); @@ -376,7 +1384,7 @@ describe('logger lwc import tests', () => { expect(logEntry.timestamp).toBeTruthy(); }); - it('still works for DEBUG logging level when disabled when using recommended import approach', async () => { + it('still works for DEBUG logging level when disabled when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = await createLogger(); const settings = await logger.getUserSettings({ forceReload: true }); @@ -406,7 +1414,7 @@ describe('logger lwc import tests', () => { expect(logEntry.timestamp).toBeTruthy(); }); - it('still works for FINE logging level when disabled when using recommended import approach', async () => { + it('still works for FINE logging level when disabled when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = await createLogger(); const settings = await logger.getUserSettings({ forceReload: true }); @@ -436,7 +1444,7 @@ describe('logger lwc import tests', () => { expect(logEntry.timestamp).toBeTruthy(); }); - it('still works for FINER logging level when disabled when using recommended import approach', async () => { + it('still works for FINER logging level when disabled when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = await createLogger(); const settings = await logger.getUserSettings({ forceReload: true }); @@ -466,7 +1474,7 @@ describe('logger lwc import tests', () => { expect(logEntry.timestamp).toBeTruthy(); }); - it('still works for FINEST logging level when disabled when using recommended import approach', async () => { + it('still works for FINEST logging level when disabled when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = await createLogger(); const settings = await logger.getUserSettings({ forceReload: true }); @@ -496,7 +1504,7 @@ describe('logger lwc import tests', () => { expect(logEntry.timestamp).toBeTruthy(); }); - it('flushes buffer when using recommended import approach', async () => { + it('flushes buffer when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); await logger.getUserSettings({ forceReload: true }); @@ -512,23 +1520,42 @@ describe('logger lwc import tests', () => { expect(logger.getBufferSize()).toEqual(0); }); - it('saves log entries and flushes buffer when using recommended import approach', async () => { + it('saves log entries and flushes buffer when using deprecated async createLogger() import approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = await createLogger(); - await logger.getUserSettings({ forceReload: true }); - logger.info('example INFO log entry'); - logger.debug('example DEBUG log entry'); + const firstEntryBuilder = logger.info('example INFO log entry added BEFORE saveLog()'); + const secondEntryBuilder = logger.debug('example DEBUG log entry added BEFORE saveLog()'); await flushPromises(); expect(logger.getBufferSize()).toBe(2); logger.saveLog(); - - await flushPromises(); - expect(logger.getBufferSize()).toBe(0); + logger.warn('example WARN log entry added AFTER saveLog()'); + + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toBe(1); + expect(saveComponentLogEntries).toHaveBeenCalledTimes(1); + const expectedApexParameter = { + componentLogEntries: [firstEntryBuilder.getComponentLogEntry(), secondEntryBuilder.getComponentLogEntry()], + saveMethodName: undefined + }; + expect(saveComponentLogEntries.mock.calls[0][0]).toEqual(expectedApexParameter); }); }); describe('logger lwc legacy markup tests', () => { + beforeEach(() => { + disableSystemMessages(); + // One of logger's features (when enabled) is to auto-call the browser's console + // so devs can see a log entry easily. But during Jest tests, seeing all of the + // console statements is... a bit overwhelming, so the global console functions + // are overwritten with an empty function so they're no-ops / they don't show up + // in the test logs + const emptyFunction = () => ''; + console.error = emptyFunction; + console.warn = emptyFunction; + console.info = emptyFunction; + console.debug = emptyFunction; + }); afterEach(async () => { while (document.body.firstChild) { document.body.removeChild(document.body.firstChild); @@ -546,10 +1573,11 @@ describe('logger lwc legacy markup tests', () => { expect(userSettings.defaultSaveMethod).toEqual('EVENT_BUS'); expect(userSettings.isEnabled).toEqual(true); - expect(userSettings.isConsoleLoggingEnabled).toEqual(true); + expect(userSettings.isConsoleLoggingEnabled).toEqual(false); + expect(userSettings.isLightningLoggerEnabled).toEqual(false); }); - it('sets a log scenario on all entries when using deprecated markup approach', async () => { + it('sets a scenario on all subsequent entries when using deprecated markup approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = createElement('c-logger', { is: Logger }); document.body.appendChild(logger); @@ -561,7 +1589,7 @@ describe('logger lwc legacy markup tests', () => { logger.setScenario(scenario); const secondLogEntry = logger.info(message).getComponentLogEntry(); - expect(firstLogEntry.scenario).toEqual(scenario); + expect(firstLogEntry.scenario).toBeUndefined(); expect(secondLogEntry.scenario).toEqual(scenario); }); @@ -813,6 +1841,31 @@ describe('logger lwc legacy markup tests', () => { expect(logEntry.tags.length).toEqual(1); }); + it('auto-saves log & throws exception 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 message = 'some message'; + const mockError = new TypeError('oops'); + let thrownError; + + try { + logger.exception(message, mockError); + } catch (error) { + thrownError = error; + } + + expect(thrownError).toBe(mockError); + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toBe(0); + expect(saveComponentLogEntries).toHaveBeenCalledTimes(1); + expect(saveComponentLogEntries.mock.calls[0][0].componentLogEntries.length).toEqual(1); + const savedComponentLogEntry = saveComponentLogEntries.mock.calls[0][0].componentLogEntries[0]; + expect(savedComponentLogEntry.loggingLevel).toEqual('ERROR'); + expect(savedComponentLogEntry.message).toEqual(message); + }); + it('still works for ERROR logging level when disabled when using deprecated markup approach', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS, isEnabled: false }); const logger = createElement('c-logger', { is: Logger }); @@ -1055,17 +2108,23 @@ describe('logger lwc legacy markup tests', () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); const logger = createElement('c-logger', { is: Logger }); document.body.appendChild(logger); - await flushPromises(); - const settings = await logger.getUserSettings({ forceReload: true }); - expect(settings.isEnabled).toEqual(true); - logger.info('example INFO log entry'); - logger.debug('example DEBUG log entry'); + await flushPromises('Resolve async task queue'); + await logger.getUserSettings({ forceReload: true }); + const firstEntryBuilder = logger.info('example INFO log entry added BEFORE saveLog()'); + const secondEntryBuilder = logger.debug('example DEBUG log entry added BEFORE saveLog()'); await flushPromises(); expect(logger.getBufferSize()).toBe(2); logger.saveLog(); - - await flushPromises(); - expect(logger.getBufferSize()).toBe(0); + logger.warn('example WARN log entry added AFTER saveLog()'); + + await flushPromises('Resolve async task queue'); + expect(logger.getBufferSize()).toBe(1); + expect(saveComponentLogEntries).toHaveBeenCalledTimes(1); + const expectedApexParameter = { + componentLogEntries: [firstEntryBuilder.getComponentLogEntry(), secondEntryBuilder.getComponentLogEntry()], + saveMethodName: undefined + }; + expect(saveComponentLogEntries.mock.calls[0][0]).toEqual(expectedApexParameter); }); }); diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerServiceTaskQueue.test.js b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerServiceTaskQueue.test.js new file mode 100644 index 000000000..494a58695 --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerServiceTaskQueue.test.js @@ -0,0 +1,80 @@ +import LoggerServiceTaskQueue from '../loggerServiceTaskQueue'; + +describe('logger task queue tests', () => { + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('correctly executes a single sync task action', async () => { + const firstInput = 'first string'; + const secondInput = 'second string'; + const syncActionFunction = (firstInput, secondInput) => { + const output = `Sync firstInput was "${firstInput}", secondInput was "${secondInput}"`; + return output; + }; + const taskQueue = new LoggerServiceTaskQueue(); + + taskQueue.enqueueTask(syncActionFunction, firstInput, secondInput); + const processedTasks = await taskQueue.executeTasks(); + + expect(processedTasks.length).toEqual(1); + expect(processedTasks[0].actionFunction).toEqual(syncActionFunction); + expect(processedTasks[0].actionArguments.length).toEqual(2); + expect(processedTasks[0].actionArguments[0]).toEqual(firstInput); + expect(processedTasks[0].actionArguments[1]).toEqual(secondInput); + expect(processedTasks[0].output).toEqual('Sync firstInput was "first string", secondInput was "second string"'); + }); + + it('correctly executes a single async task action', async () => { + const input = 'some string'; + const asyncActionFunction = async input => { + const output = 'Async input was: ' + input; + return output; + }; + const taskQueue = new LoggerServiceTaskQueue(); + + taskQueue.enqueueTask(asyncActionFunction, input); + const processedTasks = await taskQueue.executeTasks(); + + expect(processedTasks.length).toEqual(1); + expect(processedTasks[0].actionFunction).toEqual(asyncActionFunction); + expect(processedTasks[0].actionArguments.length).toEqual(1); + expect(processedTasks[0].actionArguments[0]).toEqual(input); + expect(processedTasks[0].output).toEqual('Async input was: ' + input); + }); + + it('correctly executes a mix of sync and async tasks in order', async () => { + const pendingTasks = []; + for (let i = 0; i < 3; i++) { + const task = { + isAsync: false, + actionFunction: () => { + return i; + }, + actionArguments: [i] + }; + // Make one task, somewhere in the middle of the queue, an async task + if (i == 1) { + task.isAsync = true; + task.actionFunction = async () => { + return i; + }; + } + pendingTasks.push(task); + } + const taskQueue = new LoggerServiceTaskQueue(); + + // taskQueue.disableAutoProcessing(); + pendingTasks.forEach(task => { + taskQueue.enqueueTask(task.actionFunction, task.actionArguments); + }); + const processedTasks = await taskQueue.executeTasks(); + + expect(processedTasks.length).toEqual(3); + const expectedCombinedOutput = '012'; + const actualCombinedOutput = processedTasks.reduce((accumulator, currentTask) => { + return accumulator + currentTask.output; + }, ''); + expect(actualCombinedOutput).toEqual(expectedCombinedOutput); + }); +}); diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js index 1a2c01e56..963c96238 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js @@ -1,8 +1,11 @@ -import { LoggerStackTrace } from '../loggerStackTrace'; +import LoggerStackTrace from '../loggerStackTrace'; -const CHROME_BROWSER_ERROR = require('./data/chromeBrowserError.json'); -const EDGE_BROWSER_ERROR = require('./data/edgeBrowserError.json'); -const FIREFOX_BROWSER_ERROR = require('./data/firefoxBrowserError.json'); +const CHROME_BROWSER_ERROR_DEBUG_MODE = require('./data/chromeBrowserError_debugMode.json'); +const CHROME_BROWSER_ERROR_WITHOUT_DEBUG_MODE = require('./data/chromeBrowserError_withoutDebugMode.json'); +const EDGE_BROWSER_ERROR_DEBUG_MODE = require('./data/edgeBrowserError_debugMode.json'); +const EDGE_BROWSER_ERROR_WITHOUT_DEBUG_MODE = require('./data/edgeBrowserError_withoutDebugMode.json'); +const FIREFOX_BROWSER_ERROR_DEBUG_MODE = require('./data/firefoxBrowserError_debugMode.json'); +const FIREFOX_BROWSER_ERROR_WITHOUT_DEBUG_MODE = require('./data/firefoxBrowserError_withoutDebugMode.json'); // These tests are very basic (at least for now), but provide validation // that the stack trace parsing works as expected. @@ -17,33 +20,63 @@ describe('logger stack trace parsing tests', () => { jest.clearAllMocks(); }); - it('correctly parses Chrome stack trace', async () => { + it('correctly parses Chrome stack trace when debug mode is enabled', async () => { const loggerStackTrace = new LoggerStackTrace(); - const originStackTrace = loggerStackTrace.parse(CHROME_BROWSER_ERROR); + const originStackTrace = loggerStackTrace.parse(CHROME_BROWSER_ERROR_DEBUG_MODE); expect(originStackTrace.componentName).toEqual('c/loggerChromeLWCEmbedDemo'); expect(originStackTrace.functionName).toEqual('logInfoExample'); expect(originStackTrace.metadataType).toEqual('LightningComponentBundle'); }); - it('correctly parses Edge stack trace', async () => { + it('correctly parses Chrome stack trace when debug mode is disabled', async () => { const loggerStackTrace = new LoggerStackTrace(); - const originStackTrace = loggerStackTrace.parse(EDGE_BROWSER_ERROR); + const originStackTrace = loggerStackTrace.parse(CHROME_BROWSER_ERROR_WITHOUT_DEBUG_MODE); + + expect(originStackTrace.componentName).toEqual('c/loggerAuraEmbedDemo'); + expect(originStackTrace.functionName).toEqual('saveLogExample'); + expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); + }); + + it('correctly parses Edge stack trace when debug mode is enabled', async () => { + const loggerStackTrace = new LoggerStackTrace(); + + const originStackTrace = loggerStackTrace.parse(EDGE_BROWSER_ERROR_DEBUG_MODE); expect(originStackTrace.componentName).toEqual('c/loggerEdgeAuraEmbedDemo'); expect(originStackTrace.functionName).toEqual('saveLogExample'); expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); }); - it('correctly parses Firefox stack trace', async () => { + it('correctly parses Edge stack trace when debug mode is disabled', async () => { const loggerStackTrace = new LoggerStackTrace(); - const originStackTrace = loggerStackTrace.parse(FIREFOX_BROWSER_ERROR); + const originStackTrace = loggerStackTrace.parse(EDGE_BROWSER_ERROR_WITHOUT_DEBUG_MODE); + + expect(originStackTrace.componentName).toEqual('c/loggerAuraEmbedDemo'); + expect(originStackTrace.functionName).toEqual('saveLogExample'); + expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); + }); + + it('correctly parses Firefox stack trace when debug mode is enabled', async () => { + const loggerStackTrace = new LoggerStackTrace(); + + const originStackTrace = loggerStackTrace.parse(FIREFOX_BROWSER_ERROR_DEBUG_MODE); expect(originStackTrace.componentName).toEqual('c/loggerFirefoxLWCImportDemo'); expect(originStackTrace.functionName).toEqual('logInfoExample'); expect(originStackTrace.metadataType).toEqual('LightningComponentBundle'); }); + + it('correctly parses Firefox stack trace when debug mode is disabled', async () => { + const loggerStackTrace = new LoggerStackTrace(); + + const originStackTrace = loggerStackTrace.parse(FIREFOX_BROWSER_ERROR_WITHOUT_DEBUG_MODE); + + expect(originStackTrace.componentName).toEqual('c/loggerAuraEmbedDemo'); + expect(originStackTrace.functionName).toEqual('saveLogExample'); + expect(originStackTrace.metadataType).toEqual('AuraDefinitionBundle'); + }); }); 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 d0b648575..563a35425 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js @@ -2,34 +2,12 @@ // 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. // //------------------------------------------------------------------------------------------------// -import FORM_FACTOR from '@salesforce/client/formFactor'; -import { log as lightningLog } from 'lightning/logger'; -import { LoggerStackTrace } from './loggerStackTrace'; -const CURRENT_VERSION_NUMBER = 'v4.14.12'; - -const LOGGING_LEVEL_EMOJIS = { - ERROR: '⛔', - WARN: '⚠️', - INFO: 'ℹ️', - DEBUG: '🐞', - FINE: '👍', - FINER: '👌', - FINEST: '🌟' -}; - -const ComponentBrowser = class { - 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; -}; +import LoggerStackTrace from './loggerStackTrace'; // JavaScript equivalent to the Apex class ComponentLogger.ComponentLogEntry -const ComponentLogEntry = class { - browser = new ComponentBrowser(); +class ComponentLogEntry { + browser = null; error = null; fieldToValue = {}; loggingLevel = null; @@ -41,28 +19,24 @@ const ComponentLogEntry = class { tags = []; timestamp = new Date().toISOString(); - constructor(loggingLevel) { + constructor(loggingLevel, browser) { this.loggingLevel = loggingLevel; + this.browser = browser; } -}; +} /* eslint-disable @lwc/lwc/no-dupe-class-members */ -const LogEntryBuilder = class { +export default class LogEntryEventBuilder { #componentLogEntry; - #isConsoleLoggingEnabled; - #isLightningLoggerEnabled; /** * @description Constructor used to generate each JavaScript-based log entry event * This class is the JavaScript-equivalent of the Apex class `LogEntryBuilder` * @param {String} loggingLevel The `LoggingLevel` enum to use for the builder's instance of `LogEntryEvent__e` - * @param {Boolean} isConsoleLoggingEnabled Determines if `console.log()` methods are execute - * @param {Boolean} isLightningLoggerEnabled Determines if `lightning-logger` LWC is called + * @param {Object} browserContext An `Object` containing details about the user's browser */ - constructor(loggingLevel, isConsoleLoggingEnabled, isLightningLoggerEnabled) { - this.#componentLogEntry = new ComponentLogEntry(loggingLevel); - this.#isConsoleLoggingEnabled = isConsoleLoggingEnabled; - this.#isLightningLoggerEnabled = isLightningLoggerEnabled; + constructor(loggingLevel, componentBrowserContext) { + this.#componentLogEntry = new ComponentLogEntry(loggingLevel, componentBrowserContext); } /** @@ -72,8 +46,6 @@ const LogEntryBuilder = class { */ setMessage(message) { this.#componentLogEntry.message = message; - this._logToConsole(); - this._logToLightningLogger(); return this; } @@ -108,24 +80,38 @@ const LogEntryBuilder = class { } /** - * @description Sets the log entry event's exception fields + * @description Deprecated - use `setExceptionDetails(exception)` instead + * The name of this method is very similar to the logger function logger.error(), + * resulting in confusion when used together: + * `logger.error('Unexpected error').setError(someErrorObject);` + * The new `setExceptionDetails(exception)` function provides the exact same functionality, + * but aligns with the Apex builder's method name, and helps reduce the confusion with `logger.error()` * @param {Error} error The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use * @return {LogEntryBuilder} The same instance of `LogEntryBuilder`, useful for chaining methods */ setError(error) { - if (!error) { + return this.setExceptionDetails(error); + } + + /** + * @description Sets the log entry event's exception fields + * @param {Error} exception The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use + * @return {LogEntryBuilder} The same instance of `LogEntryBuilder`, useful for chaining methods + */ + setExceptionDetails(exception) { + if (!exception) { return this; } this.#componentLogEntry.error = {}; - if (error.body) { - this.#componentLogEntry.error.message = error.body.message; - this.#componentLogEntry.error.stackTrace = error.body.stackTrace; - this.#componentLogEntry.error.type = error.body.exceptionType; + if (exception.body) { + this.#componentLogEntry.error.message = exception.body.message; + this.#componentLogEntry.error.stackTrace = exception.body.stackTrace; + this.#componentLogEntry.error.type = exception.body.exceptionType; } else { - this.#componentLogEntry.error.message = error.message; - this.#componentLogEntry.error.stackTrace = new LoggerStackTrace().parse(error); - this.#componentLogEntry.error.type = 'JavaScript.' + error.name; + this.#componentLogEntry.error.message = exception.message; + this.#componentLogEntry.error.stackTrace = new LoggerStackTrace().parse(exception); + this.#componentLogEntry.error.type = 'JavaScript.' + exception.name; } return this; } @@ -150,7 +136,7 @@ const LogEntryBuilder = class { /** * @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 + * @param {Error} originStackTraceError The instance of a JavaScript `Error` object with a stack trace to parse * @return {LogEntryBuilder} The same instance of `LogEntryBuilder`, useful for chaining methods */ parseStackTrace(originStackTraceError) { @@ -193,74 +179,4 @@ const LogEntryBuilder = class { getComponentLogEntry() { return this.#componentLogEntry; } - - /* eslint-disable no-console */ - _logToConsole() { - if (!this.#isConsoleLoggingEnabled) { - return; - } - - const consoleMessagePrefix = `%c Nebula Logger ${CURRENT_VERSION_NUMBER} `; - const consoleFormatting = 'background: #0c598d; color: #fff; font-size: 12px; font-weight:bold;'; - let consoleLoggingFunction; - switch (this.#componentLogEntry.loggingLevel) { - case 'ERROR': - consoleLoggingFunction = console.error; - break; - case 'WARN': - consoleLoggingFunction = console.warn; - break; - case 'INFO': - consoleLoggingFunction = console.info; - break; - default: - consoleLoggingFunction = console.debug; - break; - } - - const loggingLevelEmoji = LOGGING_LEVEL_EMOJIS[this.#componentLogEntry.loggingLevel]; - const qualifiedMessage = `${loggingLevelEmoji} ${this.#componentLogEntry.loggingLevel}: ${this.#componentLogEntry.message}`; - consoleLoggingFunction( - consoleMessagePrefix, - consoleFormatting, - qualifiedMessage, - // Some JS stack traces are huuuuge, so don't print it in the browser console. - // The stack trace will still be saved on the backend. - // '\n' + JSON.stringify(this.#componentLogEntry, replacer, 2) - '\n' + - JSON.stringify( - { - origin: { - component: this.#componentLogEntry.originStackTrace?.componentName, - function: this.#componentLogEntry.originStackTrace?.functionName, - metadataType: this.#componentLogEntry.originStackTrace?.metadataType - }, - scenario: this.#componentLogEntry.scenario, - timestamp: this.#componentLogEntry.timestamp - }, - (_, value) => value ?? undefined, - 2 - ) - ); - } - - _logToLightningLogger() { - if (this.#isLightningLoggerEnabled) { - lightningLog(this.#componentLogEntry); - } - } -}; - -let hasInitialized = false; -export function newLogEntry(loggingLevel, isConsoleLoggingEnabled, isLightningLoggerEnabled) { - if (!hasInitialized) { - const consoleMessagePrefix = `%c Nebula Logger ${CURRENT_VERSION_NUMBER} `; - const consoleFormatting = 'background: #0c598d; color: #fff; font-size: 12px; font-weight:bold;'; - const browserDetails = new ComponentBrowser(); - /* eslint-disable no-console */ - console.info(consoleMessagePrefix, consoleFormatting, 'ℹ️ INFO: logger component initialized\n' + JSON.stringify(browserDetails, null, 2)); - - hasInitialized = true; - } - return new LogEntryBuilder(loggingLevel, isConsoleLoggingEnabled, isLightningLoggerEnabled); } 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..fcabcaabd 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/logger.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/logger.js @@ -4,13 +4,13 @@ //------------------------------------------------------------------------------------------------// import { LightningElement, api } from 'lwc'; -import { createLoggerService } from './loggerService'; +import LoggerService from './loggerService'; export default class Logger extends LightningElement { #loggerService; - async connectedCallback() { - this.#loggerService = await createLoggerService(); + connectedCallback() { + this.#loggerService = new LoggerService(); } /** @@ -32,6 +32,20 @@ export default class Logger extends LightningElement { this.#loggerService.setScenario(scenario); } + /** + * @description Creates a new log entry with logging level == `LoggingLevel.ERROR`, + * automatically saves the log, and then throws the provided exception + * @param {String} message The string to use to set the entry's message field + * @param {Error} exception The instance of a JavaScript `Error` object to use, or an Apex HTTP error to use + * @return {LogEntryBuilder} The new entry's instance of `LogEntryEventBuilder`, useful for chaining methods + */ + @api + exception(message, exception) { + // An error has to be initialized here (in logger.js, not loggerService.js) to use for stack trace parsing + // so that the stack trace is accurate/has full context when the logger LWC is embedded in a component's markup + this.#loggerService.exception(message, exception, new Error()); + } + /** * @description Creates a new log entry with logging level == `LoggingLevel.ERROR` * @param {String} message The string to use to set the entry's message field @@ -145,8 +159,26 @@ export default class Logger extends LightningElement { } /** - * @return {Promise} a LoggerService instance + * @description Deprecated - use `getLogger()` instead + * Async function that returns a fully-loaded logger service. + * Requires some code to be executed async, so the service is not immediately available. + * Example: `const logger = await createLogger();` + * @return {Promise} A Promise that resolves an instance of `LoggerService` */ -const createLogger = createLoggerService; +export async function createLogger() { + const service = getLogger(); + await Promise.resolve('Resolve Apex call for user settings'); + return service; +} -export { createLogger }; +/** + * @description Recommended approach + * Synchronous function that returns a ready-to-use logger service. + * Internally, some code is still executed async, but the service can immediately + * be used, without awaiting a Promise + * Example: `const logger = getLogger();` + * @return {LoggerService} An instance of `LoggerService` + */ +export function getLogger() { + return new LoggerService(); +} 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..5ccfa3f66 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js @@ -3,25 +3,80 @@ // See LICENSE file or go to https://github.com/jongpie/NebulaLogger for full license details. // //------------------------------------------------------------------------------------------------// -import { newLogEntry } from './logEntryBuilder'; +import FORM_FACTOR from '@salesforce/client/formFactor'; +import { log as lightningLog } from 'lightning/logger'; +import LogEntryEventBuilder from './logEntryBuilder'; +import LoggerServiceTaskQueue from './loggerServiceTaskQueue'; import getSettings from '@salesforce/apex/ComponentLogger.getSettings'; import saveComponentLogEntries from '@salesforce/apex/ComponentLogger.saveComponentLogEntries'; +const CURRENT_VERSION_NUMBER = 'v4.14.13'; + +const CONSOLE_OUTPUT_CONFIG = { + messagePrefix: `%c Nebula Logger ${CURRENT_VERSION_NUMBER} `, + messageFormatting: 'background: #0c598d; color: #fff; font-size: 12px; font-weight:bold;' +}; +const LOGGING_LEVEL_EMOJIS = { + ERROR: '⛔', + WARN: '⚠️', + INFO: 'ℹ️', + DEBUG: '🐞', + FINE: '👍', + FINER: '👌', + FINEST: '🌟' +}; + +let areSystemMessagesEnabled = true; + +export function enableSystemMessages() { + areSystemMessagesEnabled = true; +} + +export function disableSystemMessages() { + areSystemMessagesEnabled = false; +} + +export class BrowserContext { + 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; +} + /* eslint-disable @lwc/lwc/no-dupe-class-members */ -const LoggerService = class { +export default class LoggerService { + static hasInitialized = false; + #componentLogEntries = []; #settings; #scenario; + #taskQueue = new LoggerServiceTaskQueue(); + + constructor() { + this._loadSettingsFromServer(); + if (areSystemMessagesEnabled && !LoggerService.hasInitialized) { + this._logToConsole('INFO', 'logger component initialized\n' + JSON.stringify(new BrowserContext(), null, 2)); + + LoggerService.hasInitialized = true; + } + } + + // TODO deprecate? Or make it async? getUserSettings() { return this.#settings; } setScenario(scenario) { this.#scenario = scenario; - this.#componentLogEntries.forEach(logEntry => { - logEntry.scenario = this.#scenario; - }); + } + + exception(message, exception, originStackTraceError) { + this.error(message, originStackTraceError).setExceptionDetails(exception); + this.saveLog(); + throw exception; } error(message, originStackTraceError) { @@ -66,71 +121,107 @@ const LoggerService = class { * on subsequent saveLog() calls */ async saveLog(saveMethodName) { - if (this.#componentLogEntries.length === 0) { - return; - } - - if (!saveMethodName && this.#settings?.defaultSaveMethodName) { - saveMethodName = this.#settings.defaultSaveMethodName; - } + const saveLogTask = async providedSaveMethodName => { + if (this.#componentLogEntries.length === 0) { + return; + } - try { const logEntriesToSave = [...this.#componentLogEntries]; - // 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(); - await saveComponentLogEntries({ - componentLogEntries: logEntriesToSave, - saveMethodName - }); - } catch (error) { - if (this.#settings.isConsoleLoggingEnabled === true) { - /* eslint-disable-next-line no-console */ - console.error(error); - /* eslint-disable-next-line no-console */ - console.error(this.#componentLogEntries); + providedSaveMethodName = providedSaveMethodName ?? this.#settings.defaultSaveMethodName; + try { + await saveComponentLogEntries({ + componentLogEntries: logEntriesToSave, + saveMethodName: providedSaveMethodName + }); + } catch (error) { + if (this.#settings.isConsoleLoggingEnabled === true) { + /* eslint-disable-next-line no-console */ + console.error(error); + /* eslint-disable-next-line no-console */ + console.error(this.#componentLogEntries); + } + throw error; } - throw error; - } + }; + + this.#taskQueue.enqueueTask(saveLogTask, saveMethodName); + await this.#taskQueue.executeTasks(); } async _loadSettingsFromServer() { - try { - const settings = await getSettings(); - this.#settings = Object.freeze({ - ...settings, - supportedLoggingLevels: Object.freeze(settings.supportedLoggingLevels), - userLoggingLevel: Object.freeze(settings.userLoggingLevel) - }); - } catch (error) { - /* eslint-disable-next-line no-console */ - console.error(error); - throw error; - } - } + const loadSettingsTask = async () => { + try { + const retrievedSettings = await getSettings(); + this.#settings = Object.freeze({ + ...retrievedSettings, + supportedLoggingLevels: Object.freeze(retrievedSettings.supportedLoggingLevels), + userLoggingLevel: Object.freeze(retrievedSettings.userLoggingLevel) + }); + } catch (error) { + /* eslint-disable-next-line no-console */ + console.error(error); + throw error; + } + }; - _meetsUserLoggingLevel(logEntryLoggingLevel) { - return this.#settings.isEnabled === true && this.#settings.userLoggingLevel.ordinal <= this.#settings?.supportedLoggingLevels[logEntryLoggingLevel]; + this.#taskQueue.enqueueTask(loadSettingsTask); + await this.#taskQueue.executeTasks(); } _newEntry(loggingLevel, message, originStackTraceError) { originStackTraceError = originStackTraceError ?? new Error(); - const logEntryBuilder = newLogEntry(loggingLevel, this.#settings?.isConsoleLoggingEnabled, this.#settings?.isLightningLoggerEnabled) + const logEntryBuilder = new LogEntryEventBuilder(loggingLevel, new BrowserContext()) .parseStackTrace(originStackTraceError) .setMessage(message) .setScenario(this.#scenario); - if (this._meetsUserLoggingLevel(loggingLevel)) { - this.#componentLogEntries.push(logEntryBuilder.getComponentLogEntry()); - } + const logEntry = logEntryBuilder.getComponentLogEntry(); + + const loggingLevelCheckTask = providedLoggingLevel => { + if (this._meetsUserLoggingLevel(providedLoggingLevel)) { + this.#componentLogEntries.push(logEntry); + + if (this.#settings.isConsoleLoggingEnabled) { + this._logToConsole(logEntry.loggingLevel, logEntry.message, logEntry); + } + if (this.#settings.isLightningLoggerEnabled) { + lightningLog(logEntry); + } + } + }; + + this.#taskQueue.enqueueTask(loggingLevelCheckTask, loggingLevel); + this.#taskQueue.executeTasks(); return logEntryBuilder; } -}; -const createLoggerService = async function () { - const service = new LoggerService(); - await service._loadSettingsFromServer(); - return service; -}; + _meetsUserLoggingLevel(logEntryLoggingLevel) { + return this.#settings.isEnabled === true && this.#settings.userLoggingLevel.ordinal <= this.#settings.supportedLoggingLevels[logEntryLoggingLevel]; + } -export { createLoggerService }; + /* eslint-disable no-console */ + _logToConsole(loggingLevel, message, componentLogEntry) { + const consoleLoggingFunction = console[loggingLevel.toLowerCase()] ?? console.debug; + const loggingLevelEmoji = LOGGING_LEVEL_EMOJIS[loggingLevel]; + const qualifiedMessage = `${loggingLevelEmoji} ${loggingLevel}: ${message}`; + const formattedComponentLogEntryString = !componentLogEntry + ? '' + : '\n' + + JSON.stringify( + { + origin: { + component: componentLogEntry.originStackTrace?.componentName, + function: componentLogEntry.originStackTrace?.functionName, + metadataType: componentLogEntry.originStackTrace?.metadataType + }, + scenario: componentLogEntry.scenario, + timestamp: componentLogEntry.timestamp + }, + (_, value) => value ?? undefined, + 2 + ); + + consoleLoggingFunction(CONSOLE_OUTPUT_CONFIG.messagePrefix, CONSOLE_OUTPUT_CONFIG.messageFormatting, qualifiedMessage, formattedComponentLogEntryString); + } +} diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerServiceTaskQueue.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerServiceTaskQueue.js new file mode 100644 index 000000000..bc26aca90 --- /dev/null +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerServiceTaskQueue.js @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------------------------// +// 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. // +//------------------------------------------------------------------------------------------------// + +/* +This class handles enqueueing & executing any provided functions in the order that they're specified, +regardless if they're sync or async functions. Some terminology used: +- "Action function": a JS function that needs to be executed - it could be sync or async +- "Task": refers to the action function + its arguments + any other info needed to process the action +- "Task queue"': refers to the collection of tasks that are enqueued, and need to be executed in order (serially) + +Using a queue that execute tasks in the specified order ensures that a few areas behave as expected in the logger LWC: +- On initialization, the async Apex call to load LoggerSettings__c finishes first, before anything else that + depends on the settings happens. For example, the settings need to be loaded before logger.newEntry() is used so that logger knows: + - Is logging enabled for the current user + - Does the entry's logging level (ERROR, WARN, INFO, etc.) match the current user's configured logging level + - Is calling console.log() enabled for the current user + - Is calling the lighting-logger LWC enabled for the current user +- When saveLog() is called, the async Apex call to save the data needs to be finished with the current/expected list of pending + component log entries +- While saveLog() is running, adding additional log entries (based on other async operations, user interactions like + button clicks, etc.) should be queued up in the buffer, waiting for another subsequent saveLog() call to be made +*/ + +/* eslint-disable @lwc/lwc/no-dupe-class-members */ +export default class LoggerServiceTaskQueue { + #isProcessing = false; + #taskQueue = []; + + enqueueTask(actionFunction, ...actionArguments) { + this.#taskQueue.push({ actionFunction, actionArguments }); + } + + async executeTasks() { + const processedTasks = []; + // Don't have multiple threads processing the same queue + // Doing so results in the queue being processed wildly out of order + if (this.#isProcessing) { + return processedTasks; + } + + this.#isProcessing = true; + + /* eslint-disable no-await-in-loop */ + while (this.#taskQueue.length > 0) { + const task = this.#taskQueue.shift(); + task.output = task.actionFunction(...task.actionArguments); + if (task.output instanceof Promise) { + task.output = await task.output; + } + processedTasks.push(task); + } + + this.#isProcessing = false; + + return processedTasks; + } +} diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js index 496eba1c0..982a5ffa6 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js @@ -44,16 +44,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/; const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m; const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/; class ErrorStackParser { parse(error) { let stackTraceParticles; - if (typeof error.stacktrace !== 'undefined' || typeof error['opera#sourceloc'] !== 'undefined') { - stackTraceParticles = this.parseOpera(error); - } else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) { + if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) { stackTraceParticles = this.parseV8OrIE(error); } else if (error.stack) { stackTraceParticles = this.parseFFOrSafari(error); @@ -146,82 +143,6 @@ class ErrorStackParser { }; }, this); } - - parseOpera(e) { - if (!e.stacktrace || (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length)) { - return this.parseOpera9(e); - } else if (!e.stack) { - return this.parseOpera10(e); - } - return this.parseOpera11(e); - } - - parseOpera9(e) { - const lineRE = /Line (\d+).*script (?:in )?(\S+)/i; - const lines = e.message.split('\n'); - const result = []; - - for (let i = 2, len = lines.length; i < len; i += 2) { - const match = lineRE.exec(lines[i]); - if (match) { - result.push({ - fileName: match[2], - lineNumber: match[1], - source: lines[i] - }); - } - } - - return result; - } - - parseOpera10(e) { - const lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i; - const lines = e.stacktrace.split('\n'); - const result = []; - - for (let i = 0, len = lines.length; i < len; i += 2) { - const match = lineRE.exec(lines[i]); - if (match) { - result.push({ - functionName: match[3] || undefined, - fileName: match[2], - lineNumber: match[1], - source: lines[i] - }); - } - } - - return result; - } - - // Opera 10.65+ Error.stack very similar to FF/Safari - parseOpera11(error) { - const filtered = error.stack.split('\n').filter(function (line) { - return !!line.match(FIREFOX_SAFARI_STACK_REGEXP) && !line.match(/^Error created at/); - }, this); - - return filtered.map(function (line) { - const tokens = line.split('@'); - const locationParts = this.extractLocation(tokens.pop()); - const functionCall = tokens.shift() || ''; - const functionName = functionCall.replace(//, '$2').replace(/\([^)]*\)/g, '') || undefined; - let argsRaw; - if (functionCall.match(/\(([^)]*)\)/)) { - argsRaw = functionCall.replace(/^[^(]+\(([^)]*)\)$/, '$1'); - } - const args = argsRaw === undefined || argsRaw === '[arguments not available]' ? undefined : argsRaw.split(','); - - return { - functionName: functionName, - args: args, - fileName: locationParts[0], - lineNumber: locationParts[1], - columnNumber: locationParts[2], - source: line - }; - }, this); - } } /* End of code originally copied from stacktrace.js */ @@ -229,7 +150,7 @@ class ErrorStackParser { The code below is specific to Nebula Logger - it leverages stacktrace.js plus some additional parsing logic to handle Salesforce-specific stack traces in LWC & Aura components */ -export class LoggerStackTrace { +export default class LoggerStackTrace { parse(originStackTraceError) { if (!originStackTraceError) { return this; @@ -243,11 +164,8 @@ export class LoggerStackTrace { return; } - if (!originStackTraceParticle && currentStackTraceParticle.fileName?.endsWith('aura_proddebug.js')) { - return; - } - - if (!originStackTraceParticle && currentStackTraceParticle.fileName?.endsWith('aura_proddebug')) { + const ignoredAuraFilenamesRegEx = /aura_prod(?:\.js|debug(?:\.js)?)$/; + if (!originStackTraceParticle && ignoredAuraFilenamesRegEx.test(currentStackTraceParticle.fileName)) { return; } 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 adc36a1ac..bea499996 100644 --- a/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls @@ -495,7 +495,7 @@ private class ComponentLogger_Tests { ProfileId = System.UserInfo.getProfileId() ); ComponentLogger.ComponentLogEntry componentLogEntry = new ComponentLogger.ComponentLogEntry(); - componentLogEntry.browser = new ComponentLogger.ComponentBrowser(); + componentLogEntry.browser = new ComponentLogger.ComponentBrowserContext(); componentLogEntry.browser.address = 'https://flow-ruby-5228.scratch.lightning.force.com/lightning/n/Logger_lwc_demo?c__asdfsdf=asdf'; componentLogEntry.browser.formFactor = 'Large'; componentLogEntry.browser.language = 'en-US'; diff --git a/nebula-logger/core/tests/logger-engine/classes/LogEntryEventBuilder_Tests.cls b/nebula-logger/core/tests/logger-engine/classes/LogEntryEventBuilder_Tests.cls index 44c1f57df..4878d6669 100644 --- a/nebula-logger/core/tests/logger-engine/classes/LogEntryEventBuilder_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/LogEntryEventBuilder_Tests.cls @@ -301,7 +301,6 @@ private class LogEntryEventBuilder_Tests { System.Assert.areEqual(0, mockSelector.getCachedUserQueryCount()); System.Assert.isNull(logEntryEvent.LoggedByFederationIdentifier__c); - System.Assert.isNull(logEntryEvent.LoggedByUsername__c); System.Assert.isNull(logEntryEvent.ProfileName__c); System.Assert.isNull(logEntryEvent.UserLicenseDefinitionKey__c); System.Assert.isNull(logEntryEvent.UserLicenseId__c); @@ -324,7 +323,6 @@ private class LogEntryEventBuilder_Tests { System.Assert.areNotEqual(0, mockSelector.getCachedUserQueryCount()); Schema.User cachedUser = LoggerEngineDataSelector.getInstance().getCachedUser(); System.Assert.areEqual(cachedUser.FederationIdentifier, logEntryEvent.LoggedByFederationIdentifier__c); - System.Assert.areEqual(cachedUser.Username, logEntryEvent.LoggedByUsername__c); System.Assert.areEqual(cachedUser.Profile.Name, logEntryEvent.ProfileName__c); System.Assert.areEqual(cachedUser.Profile.UserLicense.LicenseDefinitionKey, logEntryEvent.UserLicenseDefinitionKey__c); System.Assert.areEqual(cachedUser.Profile.UserLicenseId, logEntryEvent.UserLicenseId__c); @@ -1799,7 +1797,6 @@ private class LogEntryEventBuilder_Tests { MockLoggerEngineDataSelector mockSelector = new MockLoggerEngineDataSelector(); LoggerEngineDataSelector.setMock(mockSelector); Schema.User mockUser = LoggerMockDataCreator.createUser(); - mockUser.Id = System.UserInfo.getUserId(); mockSelector.setCachedUser(mockUser); LogEntryEventBuilder builder = new LogEntryEventBuilder(getUserSettings(), System.LoggingLevel.INFO, true); @@ -1811,14 +1808,23 @@ private class LogEntryEventBuilder_Tests { System.Assert.areEqual(organization.Name, builder.getLogEntryEvent().OrganizationName__c); System.Assert.areEqual(organization.NamespacePrefix, builder.getLogEntryEvent().OrganizationNamespacePrefix__c); System.Assert.areEqual(organization.OrganizationType, builder.getLogEntryEvent().OrganizationType__c); - // Verify user fields + // Verify user fields set from System.UserInfo + System.Assert.areEqual(System.UserInfo.getUserId(), builder.getLogEntryEvent().LoggedById__c); + System.Assert.areEqual(System.UserInfo.getLocale(), builder.getLogEntryEvent().Locale__c); + System.Assert.areEqual(System.UserInfo.getUserId(), builder.getLogEntryEvent().LoggedById__c); + System.Assert.areEqual(System.UserInfo.getUsername(), builder.getLogEntryEvent().LoggedByUsername__c); + System.Assert.areEqual(System.UserInfo.getProfileId(), builder.getLogEntryEvent().ProfileId__c); + System.Assert.areEqual(System.UserInfo.getUiThemeDisplayed(), builder.getLogEntryEvent().ThemeDisplayed__c); + System.Assert.areEqual(System.UserInfo.getTimeZone().getId(), builder.getLogEntryEvent().TimeZoneId__c); + System.Assert.areEqual(System.UserInfo.getTimeZone().getDisplayName(), builder.getLogEntryEvent().TimeZoneName__c); + System.Assert.areEqual(System.UserInfo.getUserRoleId(), builder.getLogEntryEvent().UserRoleId__c); + System.Assert.areEqual(System.UserInfo.getUserType(), builder.getLogEntryEvent().UserType__c); + // Verify user fields set from queried User System.Assert.areEqual(mockUser.FederationIdentifier, builder.getLogEntryEvent().LoggedByFederationIdentifier__c); - System.Assert.areEqual(mockUser.Id, builder.getLogEntryEvent().LoggedById__c); System.Assert.areEqual(mockUser.Profile.Name, builder.getLogEntryEvent().ProfileName__c); System.Assert.areEqual(mockUser.Profile.UserLicense.LicenseDefinitionKey, builder.getLogEntryEvent().UserLicenseDefinitionKey__c); System.Assert.areEqual(mockUser.Profile.UserLicense.Name, builder.getLogEntryEvent().UserLicenseName__c); System.Assert.areEqual(mockUser.Profile.UserLicenseId, builder.getLogEntryEvent().UserLicenseId__c); - System.Assert.areEqual(mockUser.Username, builder.getLogEntryEvent().LoggedByUsername__c); System.Assert.areEqual(mockUser.UserRole?.Name, builder.getLogEntryEvent().UserRoleName__c); } diff --git a/nebula-logger/managed-package/core/main/deprecated/profiles/Admin.profile-meta.xml b/nebula-logger/managed-package/core/main/deprecated/profiles/Admin.profile-meta.xml index 81278905c..bd1443d82 100644 --- a/nebula-logger/managed-package/core/main/deprecated/profiles/Admin.profile-meta.xml +++ b/nebula-logger/managed-package/core/main/deprecated/profiles/Admin.profile-meta.xml @@ -1,5081 +1,3 @@ - - LoggerConsole - false - false - - - LoggerRecipes - false - false - - - AccessType - true - - - Account - true - - - Account_Batch_Logger_Example - true - - - Account_Queueable_Logger_Example - true - - - ApexClass - true - - - ApexEmailNotification - true - - - ApexTrigger - true - - - Assert - true - - - AsyncApexJob - true - - - AuraHandledException - true - - - AuthSession - true - - - BatchApexErrorEvent - true - - - BatchableContext - true - - - CalloutException - true - - - Comparable - true - - - ComponentLogger - true - - - ComponentLogger_Tests - true - - - CustomPermission - true - - - Database - true - - - DisplayType - true - - - DmlException - true - - - EventBus - true - - - ExampleBigObjectDataGenerator - true - - - ExampleClassWithComplexLogging - true - - - ExampleClassWithLogging - true - - - ExampleInboundEmailHandler - true - - - FeatureManagement - true - - - FieldSet - true - - - FieldSetMember - true - - - FinalizerContext - true - - - FlowCollectionLogEntry - true - - - FlowCollectionLogEntry_Tests - true - - - FlowDefinitionView - true - - - FlowExecutionErrorEvent - true - - - FlowLogEntry - true - - - FlowLogEntry_Tests - true - - - FlowLogger - true - - - FlowLogger_Tests - true - - - FlowRecordLogEntry - true - - - FlowRecordLogEntry_Tests - true - - - FlowVersionView - true - - - Http - true - - - HttpCalloutMock - true - - - HttpRequest - true - - - HttpResponse - true - - - IllegalArgumentException - true - - - InstallHandler - true - - - JSON - true - - - Limits - true - - - LogBatchApexErrorEventHandler - true - - - LogBatchApexErrorEventHandler_Tests - true - - - LogBatchPurgeController - true - - - LogBatchPurgeController_Tests - true - - - LogBatchPurgeScheduler - true - - - LogBatchPurgeScheduler_Tests - true - - - LogBatchPurger - true - - - LogBatchPurger_Tests - true - - - LogBatchPurger_Tests_Database - true - - - LogBatchPurger_Tests_Flow - true - - - LogEntryArchiveBuilder - true - - - LogEntryArchiveBuilder_Tests - true - - - LogEntryArchiveController - true - - - LogEntryArchiveController_Tests - true - - - LogEntryArchivePlugin - true - - - LogEntryArchivePlugin_Tests - true - - - LogEntryEventBuilder - true - - - LogEntryEventBuilder_Tests - true - - - LogEntryEventBuilder_Tests_Network - true - - - LogEntryEventBuilder_Tests_Security - true - - - LogEntryEventHandler - true - - - LogEntryEventHandler_Tests - true - - - LogEntryEventHandler_Tests_FieldMappings - true - - - LogEntryEventStreamController - true - - - LogEntryEventStreamController_Tests - true - - - LogEntryFieldSetPicklist - true - - - LogEntryFieldSetPicklist_Tests - true - - - LogEntryHandler - true - - - LogEntryHandler_Tests - true - - - LogEntryHandler_Tests_EmailMessage - true - - - LogEntryHandler_Tests_Flow - true - - - LogEntryMetadataViewerController - true - - - LogEntryMetadataViewerController_Tests - true - - - LogEntryMetadataViwrCtlr_Tests_Security - true - - - LogEntryTagHandler - true - - - LogEntryTagHandler_Tests - true - - - LogFinalizer - true - - - LogFinalizer_Tests - true - - - LogFlowExecutionErrorEventHandler - true - - - LogFlowExecutionErrorEventHandler_Tests - true - - - LogHandler - true - - - LogHandler_Tests - true - - - LogManagementDataSelector - true - - - LogManagementDataSelector_Tests - true - - - LogManagementDataSelector_Tests_Flow - true - - - LogMassDeleteExtension - true - - - LogMassDeleteExtension_Tests - true - - - LogMessage - true - - - LogMessage_Tests - true - - - LogRetentionFilter - true - - - LogRetentionFilter_Tests - true - - - LogRetentionRulesPlugin - true - - - LogRetentionRulesPlugin_Tests - true - - - LogViewerController - true - - - LogViewerController_Tests - true - - - Logger - true - - - LoggerBatchableContext - true - - - LoggerBatchableContext_Tests - true - - - LoggerBenchmarking_Tests - true - - - LoggerCache - true - - - LoggerCache_Tests - true - - - LoggerCache_Tests_PlatformCache - true - - - LoggerDataStore - true - - - LoggerDataStore_Tests - true - - - LoggerEmailSender - true - - - LoggerEmailSender_Tests - true - - - LoggerEngineDataSelector - true - - - LoggerEngineDataSelector_Tests - true - - - LoggerEngineDataSelector_Tests_Network - true - - - LoggerFieldMapper - true - - - LoggerFieldMapper_Tests - true - - - LoggerHomeHeaderController - true - - - LoggerHomeHeaderController_Tests - true - - - LoggerLWCDemoController - true - - - LoggerMockDataCreator - true - - - LoggerMockDataStore - true - - - LoggerParameter - true - - - LoggerParameter_Tests - true - - - LoggerPlugin - true - - - LoggerPlugin_Tests - true - - - LoggerSObjectHandler - true - - - LoggerSObjectHandler_Tests - true - - - LoggerSObjectHandler_Tests_Flow - true - - - LoggerSObjectMetadata - true - - - LoggerSObjectMetadata_Tests - true - - - LoggerSObjectProxy - true - - - LoggerSObjectProxy_Tests - true - - - LoggerScenarioHandler - true - - - LoggerScenarioHandler_Tests - true - - - LoggerScenarioRule - true - - - LoggerScenarioRule_Tests - true - - - LoggerSettingsController - true - - - LoggerSettingsController_Tests - true - - - LoggerSettingsController_Tests_Security - true - - - LoggerStackTrace - true - - - LoggerStackTrace_Tests - true - - - LoggerTagHandler - true - - - LoggerTagHandler_Tests - true - - - LoggerTestConfigurator - true - - - LoggerTriggerableContext - true - - - LoggerTriggerableContext_Tests - true - - - Logger_Tests - true - - - Logger_Tests_InboundEmailHandler - true - - - Logger_Tests_MergeResult - true - - - Logger_Tests_Network - true - - - LoggingLevel - true - - - LoginHistory - true - - - Matcher - true - - - NebulaLogger_E2E_Tests - true - - - Network - true - - - Organization - true - - - Pattern - true - - - PermissionSet - true - - - PermissionSetAssignment - true - - - PicklistEntry - true - - - Profile - true - - - Queueable - true - - - QueueableContext - true - - - Quiddity - true - - - RelatedLogEntriesController - true - - - RelatedLogEntriesController_Tests - true - - - Request - true - - - RestContext - true - - - RestRequest - true - - - RestResponse - true - - - SObjectAccessDecision - true - - - SObjectField - true - - - SObjectType - true - - - Schedulable - true - - - SchedulableContext - true - - - Search - true - - - Security - true - - - SlackLoggerPlugin - true - - - SlackLoggerPlugin_Tests - true - - - SoapType - true - - - Test - true - - - Topic - true - - - TopicAssignment - true - - - TriggerOperation - true - - - Type - true - - - UUID - true - - - User - true - - - UserInfo - true - - - UserRecordAccess - true - - - UserRole - true - - false - - false - Account.AccountNumber - false - - - true - Account.AccountSource - true - - - true - Account.AnnualRevenue - true - - - true - Account.BillingAddress - true - - - true - Account.Description - true - - - true - Account.Fax - true - - - true - Account.Industry - true - - - true - Account.Jigsaw - true - - - true - Account.NumberOfEmployees - true - - - false - Account.Ownership - false - - - true - Account.ParentId - true - - - true - Account.Phone - true - - - false - Account.Rating - false - - - true - Account.ShippingAddress - true - - - false - Account.Sic - false - - - true - Account.SicDesc - true - - - false - Account.Site - false - - - false - Account.TickerSymbol - false - - - true - Account.Type - true - - - true - Account.Website - true - - - false - LogEntryArchive__b.ApiReleaseNumber__c - false - - - false - LogEntryArchive__b.ApiReleaseVersion__c - false - - - false - LogEntryArchive__b.ApiVersion__c - false - - - false - LogEntryArchive__b.ArchiveRetentionDate__c - false - - - false - LogEntryArchive__b.ArchivedById__c - false - - - false - LogEntryArchive__b.ArchivedByUsername__c - false - - - false - LogEntryArchive__b.ArchivedDate__c - false - - - false - LogEntryArchive__b.ClosedById__c - false - - - false - LogEntryArchive__b.ClosedByUsername__c - false - - - false - LogEntryArchive__b.ClosedDate__c - false - - - false - LogEntryArchive__b.Comments__c - false - - - false - LogEntryArchive__b.ComponentType__c - false - - - false - LogEntryArchive__b.DatabaseResultCollectionSize__c - false - - - false - LogEntryArchive__b.DatabaseResultCollectionType__c - false - - - false - LogEntryArchive__b.DatabaseResultJson__c - false - - - false - LogEntryArchive__b.DatabaseResultType__c - false - - - false - LogEntryArchive__b.EpochTimestamp__c - false - - - false - LogEntryArchive__b.EventUuid__c - false - - - false - LogEntryArchive__b.ExceptionMessage__c - false - - - false - LogEntryArchive__b.ExceptionStackTrace__c - false - - - false - LogEntryArchive__b.ExceptionType__c - false - - - false - LogEntryArchive__b.HttpRequestBodyMasked__c - false - - - false - LogEntryArchive__b.HttpRequestBody__c - false - - - false - LogEntryArchive__b.HttpRequestCompressed__c - false - - - false - LogEntryArchive__b.HttpRequestEndpoint__c - false - - - false - LogEntryArchive__b.HttpRequestMethod__c - false - - - false - LogEntryArchive__b.HttpResponseBodyMasked__c - false - - - false - LogEntryArchive__b.HttpResponseBody__c - false - - - false - LogEntryArchive__b.HttpResponseHeaderKeys__c - false - - - false - LogEntryArchive__b.HttpResponseStatusCode__c - false - - - false - LogEntryArchive__b.HttpResponseStatus__c - false - - - false - LogEntryArchive__b.IsClosed__c - false - - - false - LogEntryArchive__b.IsResolved__c - false - - - false - LogEntryArchive__b.Issue__c - false - - - false - LogEntryArchive__b.LimitsAggregateQueriesMax__c - false - - - false - LogEntryArchive__b.LimitsAggregateQueriesUsed__c - false - - - false - LogEntryArchive__b.LimitsAggregateQueryMax__c - false - - - false - LogEntryArchive__b.LimitsAsyncCallsMax__c - false - - - false - LogEntryArchive__b.LimitsAsyncCallsUsed__c - false - - - false - LogEntryArchive__b.LimitsCalloutsMax__c - false - - - false - LogEntryArchive__b.LimitsCalloutsUsed__c - false - - - false - LogEntryArchive__b.LimitsCpuTimeMax__c - false - - - false - LogEntryArchive__b.LimitsCpuTimeUsed__c - false - - - false - LogEntryArchive__b.LimitsDmlRowsMax__c - false - - - false - LogEntryArchive__b.LimitsDmlRowsUsed__c - false - - - false - LogEntryArchive__b.LimitsDmlStatementsMax__c - false - - - false - LogEntryArchive__b.LimitsDmlStatementsUsed__c - false - - - false - LogEntryArchive__b.LimitsEmailInvocationsMax__c - false - - - false - LogEntryArchive__b.LimitsEmailInvocationsUsed__c - false - - - false - LogEntryArchive__b.LimitsFutureCallsMax__c - false - - - false - LogEntryArchive__b.LimitsFutureCallsUsed__c - false - - - false - LogEntryArchive__b.LimitsHeapSizeMax__c - false - - - false - LogEntryArchive__b.LimitsHeapSizeUsed__c - false - - - false - LogEntryArchive__b.LimitsMobilePushApexCallsMax__c - false - - - false - LogEntryArchive__b.LimitsMobilePushApexCallsUsed__c - false - - - false - LogEntryArchive__b.LimitsPublishImmediateDmlStatementsMax__c - false - - - false - LogEntryArchive__b.LimitsPublishImmediateDmlStatementsUsed__c - false - - - false - LogEntryArchive__b.LimitsQueueableJobsMax__c - false - - - false - LogEntryArchive__b.LimitsQueueableJobsUsed__c - false - - - false - LogEntryArchive__b.LimitsSoqlQueriesMax__c - false - - - false - LogEntryArchive__b.LimitsSoqlQueriesUsed__c - false - - - false - LogEntryArchive__b.LimitsSoqlQueryLocatorRowsMax__c - false - - - false - LogEntryArchive__b.LimitsSoqlQueryLocatorRowsUsed__c - false - - - false - LogEntryArchive__b.LimitsSoqlQueryRowsMax__c - false - - - false - LogEntryArchive__b.LimitsSoqlQueryRowsUsed__c - false - - - false - LogEntryArchive__b.LimitsSoslSearchesMax__c - false - - - false - LogEntryArchive__b.LimitsSoslSearchesUsed__c - false - - - false - LogEntryArchive__b.Locale__c - false - - - false - LogEntryArchive__b.LogEntryName__c - false - - - false - LogEntryArchive__b.LogName__c - false - - - false - LogEntryArchive__b.LogPurgeAction__c - false - - - false - LogEntryArchive__b.LogRetentionDate__c - false - - - false - LogEntryArchive__b.LoggedById__c - false - - - false - LogEntryArchive__b.LoggedByUsername__c - false - - - false - LogEntryArchive__b.LoggerVersionNumber__c - false - - - false - LogEntryArchive__b.LoginApplication__c - false - - - false - LogEntryArchive__b.LoginBrowser__c - false - - - false - LogEntryArchive__b.LoginDomain__c - false - - - false - LogEntryArchive__b.LoginHistoryId__c - false - - - false - LogEntryArchive__b.LoginPlatform__c - false - - - false - LogEntryArchive__b.LoginType__c - false - - - false - LogEntryArchive__b.LogoutUrl__c - false - - - false - LogEntryArchive__b.MessageMasked__c - false - - - false - LogEntryArchive__b.MessageTruncated__c - false - - - false - LogEntryArchive__b.Message__c - false - - - false - LogEntryArchive__b.NetworkId__c - false - - - false - LogEntryArchive__b.NetworkLoginUrl__c - false - - - false - LogEntryArchive__b.NetworkLogoutUrl__c - false - - - false - LogEntryArchive__b.NetworkName__c - false - - - false - LogEntryArchive__b.NetworkSelfRegistrationUrl__c - false - - - false - LogEntryArchive__b.NetworkUrlPathPrefix__c - false - - - false - LogEntryArchive__b.OrganizationDomainUrl__c - false - - - false - LogEntryArchive__b.OrganizationEnvironmentType__c - false - - - false - LogEntryArchive__b.OrganizationId__c - false - - - false - LogEntryArchive__b.OrganizationInstanceName__c - false - - - false - LogEntryArchive__b.OrganizationInstanceReleaseCycle__c - false - - - false - LogEntryArchive__b.OrganizationName__c - false - - - false - LogEntryArchive__b.OrganizationNamespacePrefix__c - false - - - false - LogEntryArchive__b.OrganizationType__c - false - - - false - LogEntryArchive__b.OriginLocation__c - false - - - false - LogEntryArchive__b.OriginType__c - false - - - false - LogEntryArchive__b.ParentLogTransactionId__c - false - - - false - LogEntryArchive__b.Priority__c - false - - - false - LogEntryArchive__b.ProfileId__c - false - - - false - LogEntryArchive__b.ProfileName__c - false - - - false - LogEntryArchive__b.RecordCollectionSize__c - false - - - false - LogEntryArchive__b.RecordCollectionType__c - false - - - false - LogEntryArchive__b.RecordId__c - false - - - false - LogEntryArchive__b.RecordJsonMasked__c - false - - - false - LogEntryArchive__b.RecordJson__c - false - - - false - LogEntryArchive__b.RecordName__c - false - - - false - LogEntryArchive__b.RecordSObjectClassification__c - false - - - false - LogEntryArchive__b.RecordSObjectTypeNamespace__c - false - - - false - LogEntryArchive__b.RecordSObjectType__c - false - - - false - LogEntryArchive__b.Scenario__c - false - - - false - LogEntryArchive__b.SessionId__c - false - - - false - LogEntryArchive__b.SessionSecurityLevel__c - false - - - false - LogEntryArchive__b.SessionType__c - false - - - false - LogEntryArchive__b.SourceIp__c - false - - - false - LogEntryArchive__b.StackTrace__c - false - - - false - LogEntryArchive__b.Status__c - false - - - false - LogEntryArchive__b.SystemMode__c - false - - - false - LogEntryArchive__b.Tags__c - false - - - false - LogEntryArchive__b.ThemeDisplayed__c - false - - - false - LogEntryArchive__b.TimeZoneId__c - false - - - false - LogEntryArchive__b.TimeZoneName__c - false - - - false - LogEntryArchive__b.TimestampString__c - false - - - false - LogEntryArchive__b.TriggerIsExecuting__c - false - - - false - LogEntryArchive__b.TriggerOperationType__c - false - - - false - LogEntryArchive__b.TriggerSObjectType__c - false - - - false - LogEntryArchive__b.UserLicenseDefinitionKey__c - false - - - false - LogEntryArchive__b.UserLicenseId__c - false - - - false - LogEntryArchive__b.UserLicenseName__c - false - - - false - LogEntryArchive__b.UserLoggingLevelOrdinal__c - false - - - false - LogEntryArchive__b.UserLoggingLevel__c - false - - - false - LogEntryArchive__b.UserRoleId__c - false - - - false - LogEntryArchive__b.UserRoleName__c - false - - - false - LogEntryArchive__b.UserType__c - false - - - false - LogEntryDataMaskRule__mdt.ApplyToMessage__c - false - - - false - LogEntryDataMaskRule__mdt.ApplyToRecordJson__c - false - - - false - LogEntryDataMaskRule__mdt.IsEnabled__c - false - - - false - LogEntryEvent__e.ApiVersion__c - false - - - false - LogEntryEvent__e.AsyncContextChildJobId__c - false - - - false - LogEntryEvent__e.AsyncContextParentJobId__c - false - - - false - LogEntryEvent__e.AsyncContextTriggerId__c - false - - - false - LogEntryEvent__e.AsyncContextType__c - false - - - false - LogEntryEvent__e.BrowserAddress__c - false - - - false - LogEntryEvent__e.BrowserFormFactor__c - false - - - false - LogEntryEvent__e.BrowserLanguage__c - false - - - false - LogEntryEvent__e.BrowserScreenResolution__c - false - - - false - LogEntryEvent__e.BrowserUrl__c - false - - - false - LogEntryEvent__e.BrowserUserAgent__c - false - - - false - LogEntryEvent__e.BrowserWindowResolution__c - false - - - false - LogEntryEvent__e.ComponentType__c - false - - - false - LogEntryEvent__e.DatabaseResultCollectionSize__c - false - - - false - LogEntryEvent__e.DatabaseResultCollectionType__c - false - - - false - LogEntryEvent__e.DatabaseResultJson__c - false - - - false - LogEntryEvent__e.DatabaseResultType__c - false - - - false - LogEntryEvent__e.EntryScenario__c - false - - - false - LogEntryEvent__e.EpochTimestamp__c - false - - - false - LogEntryEvent__e.ExceptionLocation__c - false - - - false - LogEntryEvent__e.ExceptionMessage__c - false - - - false - LogEntryEvent__e.ExceptionSourceActionName__c - false - - - false - LogEntryEvent__e.ExceptionSourceApiName__c - false - - - false - LogEntryEvent__e.ExceptionSourceMetadataType__c - false - - - false - LogEntryEvent__e.ExceptionStackTrace__c - false - - - false - LogEntryEvent__e.ExceptionType__c - false - - - false - LogEntryEvent__e.HttpRequestBodyMasked__c - false - - - false - LogEntryEvent__e.HttpRequestBody__c - false - - - false - LogEntryEvent__e.HttpRequestCompressed__c - false - - - false - LogEntryEvent__e.HttpRequestEndpoint__c - false - - - false - LogEntryEvent__e.HttpRequestMethod__c - false - - - false - LogEntryEvent__e.HttpResponseBodyMasked__c - false - - - false - LogEntryEvent__e.HttpResponseBody__c - false - - - false - LogEntryEvent__e.HttpResponseHeaderKeys__c - false - - - false - LogEntryEvent__e.HttpResponseHeaders__c - false - - - false - LogEntryEvent__e.HttpResponseStatusCode__c - false - - - false - LogEntryEvent__e.HttpResponseStatus__c - false - - - false - LogEntryEvent__e.ImpersonatedById__c - false - - - false - LogEntryEvent__e.LimitsAggregateQueriesMax__c - false - - - false - LogEntryEvent__e.LimitsAggregateQueriesUsed__c - false - - - false - LogEntryEvent__e.LimitsAggregateQueryMax__c - false - - - false - LogEntryEvent__e.LimitsAsyncCallsMax__c - false - - - false - LogEntryEvent__e.LimitsAsyncCallsUsed__c - false - - - false - LogEntryEvent__e.LimitsCalloutsMax__c - false - - - false - LogEntryEvent__e.LimitsCalloutsUsed__c - false - - - false - LogEntryEvent__e.LimitsCpuTimeMax__c - false - - - false - LogEntryEvent__e.LimitsCpuTimeUsed__c - false - - - false - LogEntryEvent__e.LimitsDmlRowsMax__c - false - - - false - LogEntryEvent__e.LimitsDmlRowsUsed__c - false - - - false - LogEntryEvent__e.LimitsDmlStatementsMax__c - false - - - false - LogEntryEvent__e.LimitsDmlStatementsUsed__c - false - - - false - LogEntryEvent__e.LimitsEmailInvocationsMax__c - false - - - false - LogEntryEvent__e.LimitsEmailInvocationsUsed__c - false - - - false - LogEntryEvent__e.LimitsFutureCallsMax__c - false - - - false - LogEntryEvent__e.LimitsFutureCallsUsed__c - false - - - false - LogEntryEvent__e.LimitsHeapSizeMax__c - false - - - false - LogEntryEvent__e.LimitsHeapSizeUsed__c - false - - - false - LogEntryEvent__e.LimitsMobilePushApexCallsMax__c - false - - - false - LogEntryEvent__e.LimitsMobilePushApexCallsUsed__c - false - - - false - LogEntryEvent__e.LimitsPublishImmediateDmlStatementsMax__c - false - - - false - LogEntryEvent__e.LimitsPublishImmediateDmlStatementsUsed__c - false - - - false - LogEntryEvent__e.LimitsQueueableJobsMax__c - false - - - false - LogEntryEvent__e.LimitsQueueableJobsUsed__c - false - - - false - LogEntryEvent__e.LimitsSoqlQueriesMax__c - false - - - false - LogEntryEvent__e.LimitsSoqlQueriesUsed__c - false - - - false - LogEntryEvent__e.LimitsSoqlQueryLocatorRowsMax__c - false - - - false - LogEntryEvent__e.LimitsSoqlQueryLocatorRowsUsed__c - false - - - false - LogEntryEvent__e.LimitsSoqlQueryRowsMax__c - false - - - false - LogEntryEvent__e.LimitsSoqlQueryRowsUsed__c - false - - - false - LogEntryEvent__e.LimitsSoslSearchesMax__c - false - - - false - LogEntryEvent__e.LimitsSoslSearchesUsed__c - false - - - false - LogEntryEvent__e.Locale__c - false - - - false - LogEntryEvent__e.LoggedByFederationIdentifier__c - false - - - false - LogEntryEvent__e.LoggedById__c - false - - - false - LogEntryEvent__e.LoggedByUsername__c - false - - - false - LogEntryEvent__e.LoggerVersionNumber__c - false - - - false - LogEntryEvent__e.LoggingLevelOrdinal__c - false - - - false - LogEntryEvent__e.LoggingLevel__c - false - - - false - LogEntryEvent__e.LoginApplication__c - false - - - false - LogEntryEvent__e.LoginBrowser__c - false - - - false - LogEntryEvent__e.LoginDomain__c - false - - - false - LogEntryEvent__e.LoginHistoryId__c - false - - - false - LogEntryEvent__e.LoginPlatform__c - false - - - false - LogEntryEvent__e.LoginType__c - false - - - false - LogEntryEvent__e.LogoutUrl__c - false - - - false - LogEntryEvent__e.MessageMasked__c - false - - - false - LogEntryEvent__e.MessageTruncated__c - false - - - false - LogEntryEvent__e.Message__c - false - - - false - LogEntryEvent__e.NetworkId__c - false - - - false - LogEntryEvent__e.NetworkLoginUrl__c - false - - - false - LogEntryEvent__e.NetworkLogoutUrl__c - false - - - false - LogEntryEvent__e.NetworkName__c - false - - - false - LogEntryEvent__e.NetworkSelfRegistrationUrl__c - false - - - false - LogEntryEvent__e.NetworkUrlPathPrefix__c - false - - - false - LogEntryEvent__e.OrganizationApiVersion__c - false - - - false - LogEntryEvent__e.OrganizationDomainUrl__c - false - - - false - LogEntryEvent__e.OrganizationEnvironmentType__c - false - - - false - LogEntryEvent__e.OrganizationId__c - false - - - false - LogEntryEvent__e.OrganizationInstanceName__c - false - - - false - LogEntryEvent__e.OrganizationName__c - false - - - false - LogEntryEvent__e.OrganizationNamespacePrefix__c - false - - - false - LogEntryEvent__e.OrganizationType__c - false - - - false - LogEntryEvent__e.OriginLocation__c - false - - - false - LogEntryEvent__e.OriginSourceActionName__c - false - - - false - LogEntryEvent__e.OriginSourceApiName__c - false - - - false - LogEntryEvent__e.OriginSourceMetadataType__c - false - - - false - LogEntryEvent__e.OriginType__c - false - - - false - LogEntryEvent__e.ParentLogTransactionId__c - false - - - false - LogEntryEvent__e.ParentSessionId__c - false - - - false - LogEntryEvent__e.ProfileId__c - false - - - false - LogEntryEvent__e.ProfileName__c - false - - - false - LogEntryEvent__e.RecordCollectionSize__c - false - - - false - LogEntryEvent__e.RecordCollectionType__c - false - - - false - LogEntryEvent__e.RecordId__c - false - - - false - LogEntryEvent__e.RecordJsonMasked__c - false - - - false - LogEntryEvent__e.RecordJson__c - false - - - false - LogEntryEvent__e.RecordSObjectClassification__c - false - - - false - LogEntryEvent__e.RecordSObjectTypeNamespace__c - false - - - false - LogEntryEvent__e.RecordSObjectType__c - false - - - false - LogEntryEvent__e.RequestId__c - false - - - false - LogEntryEvent__e.RestRequestBodyMasked__c - false - - - false - LogEntryEvent__e.RestRequestBody__c - false - - - false - LogEntryEvent__e.RestRequestHeaderKeys__c - false - - - false - LogEntryEvent__e.RestRequestHeaders__c - false - - - false - LogEntryEvent__e.RestRequestMethod__c - false - - - false - LogEntryEvent__e.RestRequestParameters__c - false - - - false - LogEntryEvent__e.RestRequestRemoteAddress__c - false - - - false - LogEntryEvent__e.RestRequestResourcePath__c - false - - - false - LogEntryEvent__e.RestRequestUri__c - false - - - false - LogEntryEvent__e.RestResponseBodyMasked__c - false - - - false - LogEntryEvent__e.RestResponseBody__c - false - - - false - LogEntryEvent__e.RestResponseHeaderKeys__c - false - - - false - LogEntryEvent__e.RestResponseHeaders__c - false - - - false - LogEntryEvent__e.RestResponseStatusCode__c - false - - - false - LogEntryEvent__e.Scenario__c - false - - - false - LogEntryEvent__e.SessionId__c - false - - - false - LogEntryEvent__e.SessionSecurityLevel__c - false - - - false - LogEntryEvent__e.SessionType__c - false - - - false - LogEntryEvent__e.SomeLogEntryField__c - false - - - false - LogEntryEvent__e.SomeLogField__c - false - - - false - LogEntryEvent__e.SomeLoggerScenarioField__c - false - - - false - LogEntryEvent__e.SourceIp__c - false - - - false - LogEntryEvent__e.StackTrace__c - false - - - false - LogEntryEvent__e.SystemMode__c - false - - - false - LogEntryEvent__e.Tags__c - false - - - false - LogEntryEvent__e.ThemeDisplayed__c - false - - - false - LogEntryEvent__e.TimeZoneId__c - false - - - false - LogEntryEvent__e.TimeZoneName__c - false - - - false - LogEntryEvent__e.TimestampString__c - false - - - false - LogEntryEvent__e.Topics__c - false - - - false - LogEntryEvent__e.TransactionScenario__c - false - - - false - LogEntryEvent__e.TriggerIsExecuting__c - false - - - false - LogEntryEvent__e.TriggerOperationType__c - false - - - false - LogEntryEvent__e.TriggerSObjectType__c - false - - - false - LogEntryEvent__e.UserLicenseDefinitionKey__c - false - - - false - LogEntryEvent__e.UserLicenseId__c - false - - - false - LogEntryEvent__e.UserLicenseName__c - false - - - false - LogEntryEvent__e.UserLoggingLevelOrdinal__c - false - - - false - LogEntryEvent__e.UserLoggingLevel__c - false - - - false - LogEntryEvent__e.UserRoleId__c - false - - - false - LogEntryEvent__e.UserRoleName__c - false - - - false - LogEntryEvent__e.UserType__c - false - - - false - LogEntryTagRule__mdt.ComparisonValue__c - false - - - false - LogEntryTagRule__mdt.IsEnabled__c - false - - - false - LogEntryTagRule__mdt.Tags__c - false - - - false - LogEntryTag__c.LogEntryOrigin__c - false - - - false - LogEntryTag__c.LogEntryTimestamp__c - false - - - false - LogEntryTag__c.LogLink__c - false - - - false - LogEntryTag__c.LoggedByUsernameLink__c - false - - - false - LogEntryTag__c.UniqueId__c - false - - - false - LogEntry__c.ApexClassApiVersion__c - false - - - false - LogEntry__c.ApexClassCreatedDate__c - false - - - false - LogEntry__c.ApexClassId__c - false - - - false - LogEntry__c.ApexClassLastModifiedDate__c - false - - - false - LogEntry__c.ApexClassName__c - false - - - false - LogEntry__c.ApexInnerClassName__c - false - - - false - LogEntry__c.ApexMethodName__c - false - - - false - LogEntry__c.BrowserAddress__c - false - - - false - LogEntry__c.BrowserFormFactor__c - false - - - false - LogEntry__c.BrowserLanguage__c - false - - - false - LogEntry__c.BrowserScreenResolution__c - false - - - false - LogEntry__c.BrowserUrl__c - false - - - false - LogEntry__c.BrowserUserAgent__c - false - - - false - LogEntry__c.BrowserWindowResolution__c - false - - - false - LogEntry__c.ComponentApiName__c - false - - - false - LogEntry__c.ComponentFunctionName__c - false - - - false - LogEntry__c.ComponentType__c - false - - - false - LogEntry__c.DatabaseResultCollectionSize__c - false - - - false - LogEntry__c.DatabaseResultCollectionType__c - false - - - false - LogEntry__c.DatabaseResultJson__c - false - - - false - LogEntry__c.DatabaseResultType__c - false - - - false - LogEntry__c.EntryScenarioLink__c - false - - - false - LogEntry__c.EntryScenarioName__c - false - - - false - LogEntry__c.EntryScenarioText__c - false - - - false - LogEntry__c.EntryScenario__c - false - - - false - LogEntry__c.EpochTimestamp__c - false - - - false - LogEntry__c.EventUuid__c - false - - - false - LogEntry__c.ExceptionLocation__c - false - - - false - LogEntry__c.ExceptionMessage__c - false - - - false - LogEntry__c.ExceptionSourceActionName__c - false - - - false - LogEntry__c.ExceptionSourceApiName__c - false - - - false - LogEntry__c.ExceptionSourceApiVersion__c - false - - - false - LogEntry__c.ExceptionSourceCreatedById__c - false - - - false - LogEntry__c.ExceptionSourceCreatedByLink__c - false - - - false - LogEntry__c.ExceptionSourceCreatedByUsername__c - false - - - false - LogEntry__c.ExceptionSourceCreatedDate__c - false - - - false - LogEntry__c.ExceptionSourceId__c - false - - - false - LogEntry__c.ExceptionSourceLastModifiedById__c - false - - - false - LogEntry__c.ExceptionSourceLastModifiedByLink__c - false - - - false - LogEntry__c.ExceptionSourceLastModifiedByUsername__c - false - - - false - LogEntry__c.ExceptionSourceLastModifiedDate__c - false - - - false - LogEntry__c.ExceptionSourceMetadataType__c - false - - - false - LogEntry__c.ExceptionSourceSnippet__c - false - - - false - LogEntry__c.ExceptionStackTrace__c - false - - - false - LogEntry__c.ExceptionType__c - false - - - false - LogEntry__c.FlowActiveVersionId__c - false - - - false - LogEntry__c.FlowDescription__c - false - - - false - LogEntry__c.FlowDurableId__c - false - - - false - LogEntry__c.FlowLabel__c - false - - - false - LogEntry__c.FlowLastModifiedByName__c - false - - - false - LogEntry__c.FlowLastModifiedDate__c - false - - - false - LogEntry__c.FlowProcessType__c - false - - - false - LogEntry__c.FlowRecordTriggerType__c - false - - - false - LogEntry__c.FlowTriggerOrder__c - false - - - false - LogEntry__c.FlowTriggerSObjectType__c - false - - - false - LogEntry__c.FlowTriggerType__c - false - - - false - LogEntry__c.FlowVersionApiVersionRuntime__c - false - - - false - LogEntry__c.FlowVersionNumber__c - false - - - false - LogEntry__c.FlowVersionRunInMode__c - false - - - false - LogEntry__c.HasDatabaseResultJson__c - false - - - false - LogEntry__c.HasDatabaseResult__c - false - - - false - LogEntry__c.HasExceptionSourceSnippet__c - false - - - false - LogEntry__c.HasExceptionStackTrace__c - false - - - false - LogEntry__c.HasException__c - false - - - false - LogEntry__c.HasHttpRequestBody__c - false - - - false - LogEntry__c.HasHttpResponseBody__c - false - - - false - LogEntry__c.HasHttpResponseHeaderKeys__c - false - - - false - LogEntry__c.HasHttpResponseHeaders__c - false - - - false - LogEntry__c.HasInlineTags__c - false - - - false - LogEntry__c.HasOriginSourceSnippet__c - false - - - false - LogEntry__c.HasRecordId__c - false - - - false - LogEntry__c.HasRecordJson__c - false - - - false - LogEntry__c.HasRestRequestBody__c - false - - - false - LogEntry__c.HasRestRequestHeaderKeys__c - false - - - false - LogEntry__c.HasRestRequestHeaders__c - false - - - false - LogEntry__c.HasRestResponseBody__c - false - - - false - LogEntry__c.HasRestResponseHeaderKeys__c - false - - - false - LogEntry__c.HasRestResponseHeaders__c - false - - - false - LogEntry__c.HasStackTrace__c - false - - - false - LogEntry__c.HttpRequestBodyMasked__c - false - - - false - LogEntry__c.HttpRequestBody__c - false - - - false - LogEntry__c.HttpRequestCompressed__c - false - - - false - LogEntry__c.HttpRequestEndpoint__c - false - - - false - LogEntry__c.HttpRequestMethod__c - false - - - false - LogEntry__c.HttpResponseBodyMasked__c - false - - - false - LogEntry__c.HttpResponseBody__c - false - - - false - LogEntry__c.HttpResponseHeaderKeys__c - false - - - false - LogEntry__c.HttpResponseHeaders__c - false - - - false - LogEntry__c.HttpResponseStatusCode__c - false - - - false - LogEntry__c.HttpResponseStatus__c - false - - - false - LogEntry__c.LimitsAggregateQueriesMax__c - false - - - false - LogEntry__c.LimitsAggregateQueriesUsed__c - false - - - false - LogEntry__c.LimitsAggregateQueries__c - false - - - false - LogEntry__c.LimitsAsyncCallsMax__c - false - - - false - LogEntry__c.LimitsAsyncCallsUsed__c - false - - - false - LogEntry__c.LimitsAsyncCalls__c - false - - - false - LogEntry__c.LimitsCalloutsMax__c - false - - - false - LogEntry__c.LimitsCalloutsUsed__c - false - - - false - LogEntry__c.LimitsCallouts__c - false - - - false - LogEntry__c.LimitsCpuTimeMax__c - false - - - false - LogEntry__c.LimitsCpuTimeUsed__c - false - - - false - LogEntry__c.LimitsCpuTime__c - false - - - false - LogEntry__c.LimitsDmlRowsMax__c - false - - - false - LogEntry__c.LimitsDmlRowsUsed__c - false - - - false - LogEntry__c.LimitsDmlRows__c - false - - - false - LogEntry__c.LimitsDmlStatementsMax__c - false - - - false - LogEntry__c.LimitsDmlStatementsUsed__c - false - - - false - LogEntry__c.LimitsDmlStatements__c - false - - - false - LogEntry__c.LimitsEmailInvocationsMax__c - false - - - false - LogEntry__c.LimitsEmailInvocationsUsed__c - false - - - false - LogEntry__c.LimitsEmailInvocations__c - false - - - false - LogEntry__c.LimitsFutureCallsMax__c - false - - - false - LogEntry__c.LimitsFutureCallsUsed__c - false - - - false - LogEntry__c.LimitsFutureCalls__c - false - - - false - LogEntry__c.LimitsHeapSizeMax__c - false - - - false - LogEntry__c.LimitsHeapSizeUsed__c - false - - - false - LogEntry__c.LimitsHeapSize__c - false - - - false - LogEntry__c.LimitsMobilePushApexCallsMax__c - false - - - false - LogEntry__c.LimitsMobilePushApexCallsUsed__c - false - - - false - LogEntry__c.LimitsMobilePushApexCalls__c - false - - - false - LogEntry__c.LimitsPublishImmediateDmlStatementsMax__c - false - - - false - LogEntry__c.LimitsPublishImmediateDmlStatementsUsed__c - false - - - false - LogEntry__c.LimitsPublishImmediateDmlStatements__c - false - - - false - LogEntry__c.LimitsQueueableJobsMax__c - false - - - false - LogEntry__c.LimitsQueueableJobsUsed__c - false - - - false - LogEntry__c.LimitsQueueableJobs__c - false - - - false - LogEntry__c.LimitsSoqlQueriesMax__c - false - - - false - LogEntry__c.LimitsSoqlQueriesUsed__c - false - - - false - LogEntry__c.LimitsSoqlQueries__c - false - - - false - LogEntry__c.LimitsSoqlQueryLocatorRowsMax__c - false - - - false - LogEntry__c.LimitsSoqlQueryLocatorRowsUsed__c - false - - - false - LogEntry__c.LimitsSoqlQueryLocatorRows__c - false - - - false - LogEntry__c.LimitsSoqlQueryRowsMax__c - false - - - false - LogEntry__c.LimitsSoqlQueryRowsUsed__c - false - - - false - LogEntry__c.LimitsSoqlQueryRows__c - false - - - false - LogEntry__c.LimitsSoslSearchesMax__c - false - - - false - LogEntry__c.LimitsSoslSearchesUsed__c - false - - - false - LogEntry__c.LimitsSoslSearches__c - false - - - false - LogEntry__c.LogTransactionId__c - false - - - false - LogEntry__c.LoggedByUsernameLink__c - false - - - false - LogEntry__c.LoggedByUsernameText__c - false - - - false - LogEntry__c.LoggingLevelOrdinal__c - false - - - false - LogEntry__c.LoggingLevelWithImage__c - false - - - false - LogEntry__c.LoggingLevel__c - false - - - false - LogEntry__c.MessageMasked__c - false - - - false - LogEntry__c.MessageTruncated__c - false - - - false - LogEntry__c.Message__c - false - - - false - LogEntry__c.OriginLocation__c - false - - - false - LogEntry__c.OriginSourceActionName__c - false - - - false - LogEntry__c.OriginSourceApiName__c - false - - - false - LogEntry__c.OriginSourceApiVersion__c - false - - - false - LogEntry__c.OriginSourceCreatedById__c - false - - - false - LogEntry__c.OriginSourceCreatedByLink__c - false - - - false - LogEntry__c.OriginSourceCreatedByUsername__c - false - - - false - LogEntry__c.OriginSourceCreatedDate__c - false - - - false - LogEntry__c.OriginSourceId__c - false - - - false - LogEntry__c.OriginSourceLastModifiedById__c - false - - - false - LogEntry__c.OriginSourceLastModifiedByLink__c - false - - - false - LogEntry__c.OriginSourceLastModifiedByUsername__c - false - - - false - LogEntry__c.OriginSourceLastModifiedDate__c - false - - - false - LogEntry__c.OriginSourceMetadataType__c - false - - - false - LogEntry__c.OriginSourceSnippet__c - false - - - false - LogEntry__c.OriginType__c - false - - - false - LogEntry__c.Origin__c - false - - - false - LogEntry__c.RecordCollectionSize__c - false - - - false - LogEntry__c.RecordCollectionType__c - false - - - false - LogEntry__c.RecordDetailedLink__c - false - - - false - LogEntry__c.RecordId__c - false - - - false - LogEntry__c.RecordJsonMasked__c - false - - - false - LogEntry__c.RecordJson__c - false - - - false - LogEntry__c.RecordLink__c - false - - - false - LogEntry__c.RecordName__c - false - - - false - LogEntry__c.RecordSObjectClassification__c - false - - - false - LogEntry__c.RecordSObjectTypeNamespace__c - false - - - false - LogEntry__c.RecordSObjectType__c - false - - - false - LogEntry__c.RestRequestBodyMasked__c - false - - - false - LogEntry__c.RestRequestBody__c - false - - - false - LogEntry__c.RestRequestHeaderKeys__c - false - - - false - LogEntry__c.RestRequestHeaders__c - false - - - false - LogEntry__c.RestRequestMethod__c - false - - - false - LogEntry__c.RestRequestParameters__c - false - - - false - LogEntry__c.RestRequestRemoteAddress__c - false - - - false - LogEntry__c.RestRequestResourcePath__c - false - - - false - LogEntry__c.RestRequestUri__c - false - - - false - LogEntry__c.RestResponseBodyMasked__c - false - - - false - LogEntry__c.RestResponseBody__c - false - - - false - LogEntry__c.RestResponseHeaderKeys__c - false - - - false - LogEntry__c.RestResponseHeaders__c - false - - - false - LogEntry__c.RestResponseStatusCode__c - false - - - false - LogEntry__c.SomeLogEntryField__c - false - - - false - LogEntry__c.StackTrace__c - false - - - false - LogEntry__c.Tags__c - false - - - false - LogEntry__c.Timestamp__c - false - - - false - LogEntry__c.TransactionEntryNumber__c - false - - - false - LogEntry__c.TriggerIsExecuting__c - false - - - false - LogEntry__c.TriggerOperationType__c - false - - - false - LogEntry__c.TriggerSObjectType__c - false - - - false - LogEntry__c.Trigger__c - false - - - false - LogEntry__c.UniqueId__c - false - - - false - LogRetentionRuleCondition__mdt.SortOrder__c - false - - - false - LogRetentionRuleCondition__mdt.Value__c - false - - - false - LogRetentionRule__mdt.CustomConditionLogic__c - false - - - false - LogRetentionRule__mdt.ExecutionOrder__c - false - - - false - LogRetentionRule__mdt.IsEnabled__c - false - - - false - LogRetentionRule__mdt.NumberOfDaysToRetainLogs__c - false - - - false - LogStatus__mdt.IsActive__c - false - - - false - LogStatus__mdt.IsClosed__c - false - - - false - LogStatus__mdt.IsResolved__c - false - - - false - Log__c.ApiReleaseNumber__c - false - - - false - Log__c.ApiReleaseVersion__c - false - - - false - Log__c.ApiVersion__c - false - - - false - Log__c.AsyncContextChildJobId__c - false - - - false - Log__c.AsyncContextParentJobId__c - false - - - false - Log__c.AsyncContextTriggerId__c - false - - - false - Log__c.AsyncContextType__c - false - - - false - Log__c.ClosedBy__c - false - - - false - Log__c.ClosedDate__c - false - - - false - Log__c.Comments__c - false - - - false - Log__c.EndTime__c - false - - - false - Log__c.HasComments__c - false - - - false - Log__c.HasLoggedByFederationIdentifier__c - false - - - false - Log__c.HasOrganizationLimits__c - false - - - false - Log__c.ImpersonatedByUsernameLink__c - false - - - false - Log__c.ImpersonatedBy__c - false - - - false - Log__c.IsClosed__c - false - - - false - Log__c.IsResolved__c - false - - - false - Log__c.Issue__c - false - - - false - Log__c.Locale__c - false - - - false - Log__c.LogEntriesSummary__c - false - - - false - Log__c.LogPurgeAction__c - false - - - false - Log__c.LogRetentionDate__c - false - - - false - Log__c.LoggedByFederationIdentifier__c - false - - - false - Log__c.LoggedByUsernameLink__c - false - - - false - Log__c.LoggedByUsernameText__c - false - - - false - Log__c.LoggedByUsername__c - false - - - false - Log__c.LoggedBy__c - false - - - false - Log__c.LoggerVersionNumber__c - false - - - false - Log__c.LoginApplication__c - false - - - false - Log__c.LoginBrowser__c - false - - - false - Log__c.LoginDomain__c - false - - - false - Log__c.LoginHistoryId__c - false - - - false - Log__c.LoginPlatform__c - false - - - false - Log__c.LoginType__c - false - - - false - Log__c.LogoutUrl__c - false - - - false - Log__c.MaxLogEntryLoggingLevelOrdinal__c - false - - - false - Log__c.NetworkId__c - false - - - false - Log__c.NetworkLoginUrl__c - false - - - false - Log__c.NetworkLogoutUrl__c - false - - - false - Log__c.NetworkName__c - false - - - false - Log__c.NetworkSelfRegistrationUrl__c - false - - - false - Log__c.NetworkUrlPathPrefix__c - false - - - false - Log__c.OrganizationApiVersion__c - false - - - false - Log__c.OrganizationDomainUrl__c - false - - - false - Log__c.OrganizationEnvironmentType__c - false - - - false - Log__c.OrganizationId__c - false - - - false - Log__c.OrganizationInstanceName__c - false - - - false - Log__c.OrganizationInstanceReleaseCycle__c - false - - - false - Log__c.OrganizationLimits__c - false - - - false - Log__c.OrganizationLocation__c - false - - - false - Log__c.OrganizationName__c - false - - - false - Log__c.OrganizationNamespacePrefix__c - false - - - false - Log__c.OrganizationReleaseNumber__c - false - - - false - Log__c.OrganizationReleaseVersion__c - false - - - false - Log__c.OrganizationType__c - false - - - false - Log__c.ParentLogLink__c - false - - - false - Log__c.ParentLogTransactionId__c - false - - - false - Log__c.ParentLog__c - false - - - false - Log__c.ParentSessionId__c - false - - - false - Log__c.Priority__c - false - - - false - Log__c.ProfileId__c - false - - - false - Log__c.ProfileLink__c - false - - - false - Log__c.ProfileName__c - false - - - false - Log__c.RequestId__c - false - - - false - Log__c.Scenario__c - false - - - false - Log__c.SendSlackNotification__c - false - - - false - Log__c.SessionId__c - false - - - false - Log__c.SessionSecurityLevel__c - false - - - false - Log__c.SessionType__c - false - - - false - Log__c.SlackNotificationDate__c - false - - - false - Log__c.SomeLogField__c - false - - - false - Log__c.SourceIp__c - false - - - false - Log__c.StartTime__c - false - - - false - Log__c.Status__c - false - - - false - Log__c.SystemModeSummary__c - false - - - false - Log__c.SystemMode__c - false - - - false - Log__c.ThemeDisplayed__c - false - - - false - Log__c.TimeZoneId__c - false - - - false - Log__c.TimeZoneName__c - false - - - false - Log__c.TotalDEBUGLogEntries__c - false - - - false - Log__c.TotalERRORLogEntries__c - false - - - false - Log__c.TotalFINELogEntries__c - false - - - false - Log__c.TotalFINERLogEntries__c - false - - - false - Log__c.TotalFINESTLogEntries__c - false - - - false - Log__c.TotalINFOLogEntries__c - false - - - false - Log__c.TotalLimitsCpuTimeUsed__c - false - - - false - Log__c.TotalLogEntries__c - false - - - false - Log__c.TotalWARNLogEntries__c - false - - - false - Log__c.TransactionId__c - false - - - false - Log__c.TransactionScenarioLink__c - false - - - false - Log__c.TransactionScenarioName__c - false - - - false - Log__c.TransactionScenarioText__c - false - - - false - Log__c.TransactionScenario__c - false - - - false - Log__c.UserLicenseDefinitionKey__c - false - - - false - Log__c.UserLicenseId__c - false - - - false - Log__c.UserLicenseName__c - false - - - false - Log__c.UserLoggingLevelOrdinal__c - false - - - false - Log__c.UserLoggingLevel__c - false - - - false - Log__c.UserRoleId__c - false - - - false - Log__c.UserRoleLink__c - false - - - false - Log__c.UserRoleName__c - false - - - false - Log__c.UserType__c - false - - - false - Log__c.WasLoggedByCurrentUser__c - false - - - false - LoggerFieldMapping__mdt.IsEnabled__c - false - - - false - LoggerParameter__mdt.Comments__c - false - - - false - LoggerParameter__mdt.Description__c - false - - - false - LoggerParameter__mdt.Value__c - false - - - false - LoggerPlugin__mdt.BatchPurgerApexClass__c - false - - - false - LoggerPlugin__mdt.BatchPurgerExecutionOrder__c - false - - - false - LoggerPlugin__mdt.BatchPurgerFlowName__c - false - - - false - LoggerPlugin__mdt.Description__c - false - - - false - LoggerPlugin__mdt.ExecutionOrder__c - false - - - false - LoggerPlugin__mdt.IsEnabled__c - false - - - false - LoggerPlugin__mdt.Link__c - false - - - false - LoggerPlugin__mdt.PluginApiName__c - false - - - false - LoggerPlugin__mdt.PluginType__c - false - - - false - LoggerPlugin__mdt.SObjectHandlerApexClass__c - false - - - false - LoggerPlugin__mdt.SObjectHandlerExecutionOrder__c - false - - - false - LoggerPlugin__mdt.SObjectHandlerFlowName__c - false - - - false - LoggerPlugin__mdt.SObjectType__c - false - - - false - LoggerPlugin__mdt.VersionNumber__c - false - - - false - LoggerSObjectHandler__mdt.IsEnabled__c - false - - - false - LoggerSObjectHandler__mdt.SObjectHandlerApexClass__c - false - - - false - LoggerSObjectHandler__mdt.SObjectTypeOverride__c - false - - - false - LoggerSObjectHandler__mdt.SObjectType__c - false - - - false - LoggerScenarioRule__mdt.EndTime__c - false - - - false - LoggerScenarioRule__mdt.IsAnonymousModeEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsApexSystemDebugLoggingEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsDataMaskingEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsJavaScriptConsoleLoggingEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsLogAssignmentEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsLogRetentionOverrideEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsLoggerEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsPlatformEventStorageLocationEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsRecordFieldStrippingEnabled__c - false - - - false - LoggerScenarioRule__mdt.IsSavingEnabled__c - false - - - false - LoggerScenarioRule__mdt.NumberOfDaysToRetainLogs__c - false - - - false - LoggerScenarioRule__mdt.PlatformEventStorageLocation__c - false - - - false - LoggerScenarioRule__mdt.SaveMethod__c - false - - - false - LoggerScenarioRule__mdt.StartTime__c - false - - - false - LoggerScenarioRule__mdt.UserLoggingLevel__c - false - - - false - LoggerScenario__c.SomeLoggerScenarioField__c - false - - - false - LoggerTag__c.TotalLogEntries__c - false - - - false - LoggerTag__c.UniqueId__c - false - - - LogEntryDataMaskRule__mdt-Log Entry Data Mask Rule Layout - - - LogEntryTagRule__mdt-Logger Tag Rule Layout - - - LogEntryTag__c-Log Entry Tag Layout - - - LogEntry__c-Log Entry Layout - - - LogRetentionRuleCondition__mdt-Log Retention Rule Condition Layout - - - LogRetentionRule__mdt-Log Retention Rule Layout - - - LogStatus__mdt-Log Status Layout - - - Log__c-Log Layout - - - LoggerFieldMapping__mdt-Logger Field Mapping Layout - - - LoggerParameter__mdt-Logger Parameter Layout - - - LoggerPlugin__mdt-Logger Plugin Layout - - - LoggerSObjectHandler__mdt-Logger SObject Handler Layout - - - LoggerScenarioRule__mdt-Logger Scenario Rule Layout - - - LoggerScenario__c-Logger Scenario Layout - - - LoggerTag__c-Logger Tag Layout - - - true - true - true - true - true - Account - true - - - true - true - true - true - true - LogEntryArchive__b - true - - - true - true - true - true - true - LogEntryEvent__e - true - - - true - true - true - true - true - LogEntryTag__c - true - - - true - true - true - true - true - LogEntry__c - true - - - true - true - true - true - true - Log__c - true - - - true - true - true - true - true - LoggerScenario__c - true - - - true - true - true - true - true - LoggerTag__c - true - - - LogMassDelete - true - - - LogBatchPurge - Hidden - - - LogEntryArchives - Hidden - - - LogEntryEventStream - Hidden - - - LogEntryTag__c - Hidden - - - LogEntry__c - Hidden - - - Log__c - Hidden - - - LoggerScenario__c - Hidden - - - LoggerSettings - Hidden - - - LoggerTag__c - Hidden - - - Logger_App_Page_Demo - Hidden - - - Logger_Aura_Embed_Demo - Hidden - - - Logger_LWC_Embed_Demo - Hidden - - - Logger_LWC_Import_Demo - Hidden - - - standard-Account - DefaultOn - - Salesforce - - true - AIViewInsightObjects - - - true - ActivateContract - - - true - ActivateOrder - - - true - ActivitiesAccess - - - true - AllowUniversalSearch - - - true - AllowViewKnowledge - - - true - ApexRestServices - - - true - ApiEnabled - - - true - ApproveContract - - - true - AssignPermissionSets - - - true - AssignTopics - - - true - AuthorApex - - - true - BulkMacrosAllowed - - - true - CanUseNewDashboardBuilder - - - true - ChangeDashboardColors - - - true - ChatterFileLink - - - true - ClientSecretRotation - - - true - ConnectOrgToEnvironmentHub - - - true - ConsentApiUpdate - - - true - ContentAdministrator - - - true - ContentWorkspaces - - - true - ConvertLeads - - - true - CreateCustomizeDashboards - - - true - CreateCustomizeFilters - - - true - CreateCustomizeReports - - - true - CreateDashboardFolders - - - true - CreateLtngTempFolder - - - true - CreateReportFolders - - - true - CreateTopics - - - true - CreateWorkspaces - - - true - CustomizeApplication - - - true - DataExport - - - true - DelegatedTwoFactor - - - true - DeleteActivatedContract - - - true - DeleteTopics - - - true - DistributeFromPersWksp - - - true - EditActivatedOrders - - - true - EditBillingInfo - - - true - EditBrandTemplates - - - true - EditCaseComments - - - true - EditEvent - - - true - EditHtmlTemplates - - - true - EditKnowledge - - - true - EditMyDashboards - - - true - EditMyReports - - - true - EditOppLineItemUnitPrice - - - true - EditPublicDocuments - - - true - EditPublicFilters - - - true - EditPublicTemplates - - - true - EditReadonlyFields - - - true - EditTask - - - true - EditTopics - - - true - EmailMass - - - true - EmailSingle - - - true - EnableCommunityAppLauncher - - - true - EnableNotifications - - - true - ExportReport - - - true - FreezeUsers - - - true - GiveRecognitionBadge - - - true - ImportCustomObjects - - - true - ImportLeads - - - true - ImportPersonal - - - true - InboundMigrationToolsUser - - - true - InstallPackaging - - - true - LightningConsoleAllowedForUser - - - true - LightningExperienceUser - - - true - ListEmailSend - - - true - ManageAnalyticSnapshots - - - true - ManageAuthProviders - - - true - ManageBusinessHourHolidays - - - true - ManageC360AConnections - - - true - ManageCMS - - - true - ManageCallCenters - - - true - ManageCases - - - true - ManageCategories - - - true - ManageCertificates - - - true - ManageContentPermissions - - - true - ManageContentProperties - - - true - ManageContentTypes - - - true - ManageCustomDomains - - - true - ManageCustomPermissions - - - true - ManageCustomReportTypes - - - true - ManageDashbdsInPubFolders - - - true - ManageDataCategories - - - true - ManageDataIntegrations - - - true - ManageDynamicDashboards - - - true - ManageEmailClientConfig - - - true - ManageEntitlements - - - true - ManageExchangeConfig - - - true - ManageHealthCheck - - - true - ManageHubConnections - - - true - ManageInteraction - - - true - ManageInternalUsers - - - true - ManageIpAddresses - - - true - ManageKnowledge - - - true - ManageKnowledgeImportExport - - - true - ManageLeads - - - true - ManageLoginAccessPolicies - - - true - ManageMobile - - - true - ManageNetworks - - - true - ManageOrchInstsAndWorkItems - - - true - ManagePackageLicenses - - - true - ManagePasswordPolicies - - - true - ManageProfilesPermissionsets - - - true - ManagePropositions - - - true - ManagePvtRptsAndDashbds - - - true - ManageRecommendationStrategies - - - true - ManageReleaseUpdates - - - true - ManageRemoteAccess - - - true - ManageReportsInPubFolders - - - true - ManageRoles - - - true - ManageSearchPromotionRules - - - true - ManageSharing - - - true - ManageSolutions - - - true - ManageSubscriptions - - - true - ManageSynonyms - - - true - ManageTrustMeasures - - - true - ManageUserAccessPolicies - - - true - ManageUsers - - - true - MassInlineEdit - - - true - MergeTopics - - - true - ModifyAllData - - - true - ModifyDataClassification - - - true - ModifyMetadata - - - true - MonitorLoginHistory - - - true - NewReportBuilder - - - true - OutboundMigrationToolsUser - - - true - Packaging2 - - - true - Packaging2Delete - - - true - PrivacyDataAccess - - - true - ResetPasswords - - - true - RunReports - - - true - ScheduleJob - - - true - ScheduleReports - - - true - SelectFilesFromSalesforce - - - true - SendCustomNotifications - - - true - SendSitRequests - - - true - ShareInternalArticles - - - true - ShowCompanyNameAsUserBadge - - - true - SolutionImport - - - true - SubmitMacrosAllowed - - - true - SubscribeDashboardRolesGrps - - - true - SubscribeDashboardToOtherUsers - - - true - SubscribeReportRolesGrps - - - true - SubscribeReportToOtherUsers - - - true - SubscribeReportsRunAsUser - - - true - SubscribeToLightningDashboards - - - true - SubscribeToLightningReports - - - true - TransactionalEmailSend - - - true - TransferAnyCase - - - true - TransferAnyEntity - - - true - TransferAnyLead - - - true - UseTeamReassignWizards - - - true - UseWebLink - - - true - ViewAllData - - - true - ViewAllProfiles - - - true - ViewAllUsers - - - true - ViewDataAssessment - - - true - ViewDataCategories - - - true - ViewDeveloperName - - - true - ViewEventLogFiles - - - true - ViewFlowUsageAndFlowEventData - - - true - ViewHealthCheck - - - true - ViewHelpLink - - - true - ViewMLModels - - - true - ViewMyTeamsDashboards - - - true - ViewPublicDashboards - - - true - ViewPublicReports - - - true - ViewRoles - - - true - ViewSetup - - - true - ViewTrustMeasures - - - true - ViewUserPII - diff --git a/nebula-logger/recipes/applications/LoggerRecipes.app-meta.xml b/nebula-logger/recipes/applications/LoggerRecipes.app-meta.xml index 55bef32d5..eccedb16f 100644 --- a/nebula-logger/recipes/applications/LoggerRecipes.app-meta.xml +++ b/nebula-logger/recipes/applications/LoggerRecipes.app-meta.xml @@ -24,7 +24,8 @@ standard-home standard-Account Logger_App_Page_Demo - Logger_LWC_Import_Demo + Logger_LWC_Get_Logger_Import_Demo + Logger_LWC_Create_Logger_Import_Demo Logger_LWC_Embed_Demo Logger_Aura_Embed_Demo Log__c diff --git a/nebula-logger/recipes/flexipages/Logger_Recipes_Account_Record_Page.flexipage-meta.xml b/nebula-logger/recipes/flexipages/Logger_Recipes_Account_Record_Page.flexipage-meta.xml index 51936e78a..8af1399b6 100644 --- a/nebula-logger/recipes/flexipages/Logger_Recipes_Account_Record_Page.flexipage-meta.xml +++ b/nebula-logger/recipes/flexipages/Logger_Recipes_Account_Record_Page.flexipage-meta.xml @@ -7,7 +7,10 @@ actionNames - Account.loggerLWCImportDemo + Account.loggerLWCGetLoggerImportDemo + + + Account.loggerLWCCreateLoggerImportDemo Account.loggerLWCEmbedDemo @@ -72,8 +75,14 @@ - loggerLWCImportDemo - c_loggerLWCImportDemo + loggerLWCGetLoggerImportDemo + c_loggerLWCGetLoggerImportDemo + + + + + loggerLWCCreateLoggerImportDemo + c_loggerLWCCreateLoggerImportDemo diff --git a/nebula-logger/recipes/flexipages/Logger_Recipes_Home_Page.flexipage-meta.xml b/nebula-logger/recipes/flexipages/Logger_Recipes_Home_Page.flexipage-meta.xml index adf86af5d..40913d5c1 100644 --- a/nebula-logger/recipes/flexipages/Logger_Recipes_Home_Page.flexipage-meta.xml +++ b/nebula-logger/recipes/flexipages/Logger_Recipes_Home_Page.flexipage-meta.xml @@ -17,8 +17,14 @@ - loggerLWCImportDemo - c_loggerLWCImportDemo + loggerLWCGetLoggerImportDemo + c_loggerLWCGetLoggerImportDemo + + + + + loggerLWCCreateLoggerImportDemo + c_loggerLWCCreateLoggerImportDemo diff --git a/nebula-logger/recipes/flexipages/Logger_Recipes_Utility_Bar.flexipage-meta.xml b/nebula-logger/recipes/flexipages/Logger_Recipes_Utility_Bar.flexipage-meta.xml index 6e19f8967..18ff32731 100644 --- a/nebula-logger/recipes/flexipages/Logger_Recipes_Utility_Bar.flexipage-meta.xml +++ b/nebula-logger/recipes/flexipages/Logger_Recipes_Utility_Bar.flexipage-meta.xml @@ -21,7 +21,7 @@ label decorator - loggerLWCImportDemo + loggerLWCGetLoggerImportDemo scrollable @@ -33,8 +33,44 @@ decorator 680 - loggerLWCImportDemo - loggerLWCImportDemo + loggerLWCGetLoggerImportDemo + loggerLWCGetLoggerImportDemo + + + + + + eager + decorator + false + + + height + decorator + 480 + + + icon + decorator + fallback + + + label + decorator + loggerLWCCreateLoggerImportDemo + + + scrollable + decorator + true + + + width + decorator + 680 + + loggerLWCCreateLoggerImportDemo + loggerLWCCreateLoggerImportDemo diff --git a/nebula-logger/recipes/flows/Logger_Recipes_Flow_Screen_Demos.flow-meta.xml b/nebula-logger/recipes/flows/Logger_Recipes_Flow_Screen_Demos.flow-meta.xml index 06f8b3567..aacf434e2 100644 --- a/nebula-logger/recipes/flows/Logger_Recipes_Flow_Screen_Demos.flow-meta.xml +++ b/nebula-logger/recipes/flows/Logger_Recipes_Flow_Screen_Demos.flow-meta.xml @@ -32,15 +32,41 @@ true true - loggerLWCImportDemoHeader - loggerLWCImportDemo + loggerLWCGetLoggerImportDemoHeader + loggerLWCGetLoggerImportDemo RegionContainer - loggerLWCImportDemoHeader_Column1 + loggerLWCGetLoggerImportDemoHeader_Column1 Region - loggerLWCImportDemo - c:loggerLWCImportDemo + loggerLWCGetLoggerImportDemo + c:loggerLWCGetLoggerImportDemo + ComponentInstance + UseStoredValues + true + true + + + width + + 12 + + + false + + false + SectionWithHeader + + + loggerLWCCreateLoggerImportDemoHeader + loggerLWCCreateLoggerImportDemo + RegionContainer + + loggerLWCCreateLoggerImportDemoHeader_Column1 + Region + + loggerLWCCreateLoggerImportDemo + c:loggerLWCCreateLoggerImportDemo ComponentInstance UseStoredValues true diff --git a/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/__tests__/loggerLWCCreateLoggerImportDemo.test.js b/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/__tests__/loggerLWCCreateLoggerImportDemo.test.js new file mode 100644 index 000000000..59fab0024 --- /dev/null +++ b/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/__tests__/loggerLWCCreateLoggerImportDemo.test.js @@ -0,0 +1,45 @@ +import { createElement } from 'lwc'; +import loggerLWCCreateLoggerImportDemo from 'c/loggerLWCCreateLoggerImportDemo'; + +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: loggerLWCCreateLoggerImportDemo }); + document.body.appendChild(demo); + + await flushPromises(); + + expect(demo.logger?.getBufferSize()).toBe(0); + }); +}); diff --git a/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.html b/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.html new file mode 100644 index 000000000..97da6014a --- /dev/null +++ b/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.html @@ -0,0 +1,40 @@ + + + diff --git a/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js b/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js similarity index 71% rename from nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js rename to nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js index 21ca10a30..201cd9d3e 100644 --- a/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js +++ b/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js @@ -4,38 +4,36 @@ //------------------------------------------------------------------------------------------------// /* eslint-disable no-console */ -import { api, LightningElement, wire } from 'lwc'; +import { LightningElement, wire } from 'lwc'; import returnSomeString from '@salesforce/apex/LoggerLWCDemoController.returnSomeString'; import throwSomeError from '@salesforce/apex/LoggerLWCDemoController.throwSomeError'; import { createLogger } from 'c/logger'; -export default class LoggerLWCImportDemo extends LightningElement { +export default class LoggerLWCCreateLoggerImportDemo extends LightningElement { someBoolean = false; message = 'Hello, world!'; scenario = 'Some demo scenario'; tagsString = 'Tag-one, Another tag here'; - /* eslint-disable @lwc/lwc/no-api-reassignments */ - @api logger; async connectedCallback() { this.logger = await createLogger(); console.log('>>> start of connectedCallback()'); try { - 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' }); + this.logger.error('test error entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger.warn('test warn entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger.info('test info entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger.debug('test debug entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger.fine('test fine entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger.finer('test finer entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger.finest('test finest entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); throw new Error('A bad thing happened here'); } catch (error) { this.logger .error('>>> connectedCallback error: ' + error.message) - .setError(error) - .setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + .setExceptionDetails(error) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); this.logger.saveLog().then(() => { console.log('done with async save'); }); @@ -44,25 +42,29 @@ export default class LoggerLWCImportDemo extends LightningElement { disconnectedCallback() { console.log('>>> start of disconnectedCallback()'); - 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 + .info('>>> running disconnectedCallback(), using createLogger()') + .setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger.info('>>> adding an extra log entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); this.logger.saveLog(); console.log('>>> done with disconnectedCallback()'); } renderedCallback() { console.log('>>> start of renderedCallback()'); - 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 + ?.info('>>> running renderedCallback(), using createLogger()') + .setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); + this.logger?.info('>>> adding an extra log entry').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); this.logger?.saveLog(); console.log('>>> done with renderedCallback()'); } @wire(returnSomeString) wiredReturnSomeString({ error, data }) { - this.logger?.info('>>> logging inside a wire function').setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger?.info('>>> logging inside a wire function').setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); if (data) { - this.logger?.info('>>> wire function return value: ' + data).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }); + this.logger?.info('>>> wire function return value: ' + data).setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }); } if (error) { this.logger?.error('>>> wire function error: ' + JSON.stringify(error)); @@ -97,8 +99,8 @@ export default class LoggerLWCImportDemo extends LightningElement { console.log('and a stack trace', new Error().stack); const entry = this.logger .error(this.message) - .setError(error) - .setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }) + .setExceptionDetails(error) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }) .addTags(this.tagsString.split(',')); console.log('entry==', JSON.parse(JSON.stringify(entry))); }); @@ -110,39 +112,39 @@ export default class LoggerLWCImportDemo extends LightningElement { const someError = new TypeError('oops'); this.logger .error(this.message) - .setError(someError) - .setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }) + .setExceptionDetails(someError) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }) .addTags(this.tagsString.split(',')); } logWarnExample() { console.log('running logWarn for btn'); console.log(this.logger); - this.logger.warn(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); + this.logger.warn(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }).addTags(this.tagsString.split(',')); } logInfoExample() { console.log('running logInfo for btn'); console.log(this.logger); - this.logger.info(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); + this.logger.info(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }).addTags(this.tagsString.split(',')); } logDebugExample() { console.log('running logDebug for btn'); console.log(this.logger); - this.logger.debug(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); + this.logger.debug(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }).addTags(this.tagsString.split(',')); } logFineExample() { console.log('running logFine for btn'); console.log(this.logger); - this.logger.fine(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); + this.logger.fine(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }).addTags(this.tagsString.split(',')); } logFinerExample() { console.log('running logFiner for btn'); console.log(this.logger); - this.logger.finer(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCImportDemo' }).addTags(this.tagsString.split(',')); + this.logger.finer(this.message).setField({ SomeLogEntryField__c: 'some text from loggerLWCCreateLoggerImportDemo' }).addTags(this.tagsString.split(',')); } logFinestExample() { diff --git a/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js-meta.xml b/nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js-meta.xml similarity index 100% rename from nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.js-meta.xml rename to nebula-logger/recipes/lwc/loggerLWCCreateLoggerImportDemo/loggerLWCCreateLoggerImportDemo.js-meta.xml diff --git a/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.html b/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.html index 7f75dab65..d0ed2fe76 100644 --- a/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.html +++ b/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.html @@ -12,7 +12,7 @@
⚠ This component demonstrates how to use Nebula Logger by adding <c-logger> to your component's markup. This approach is still - supported, but is considered deprecate as of v4.10.2. The new recommended approach is import the logger component in your component's JavaScript file. + supported, but is considered deprecated as of v4.10.2. The new recommended approach is to import the logger component in your component's JavaScript file.
diff --git a/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js b/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js index c7a8e1013..fa34458db 100644 --- a/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js +++ b/nebula-logger/recipes/lwc/loggerLWCEmbedDemo/loggerLWCEmbedDemo.js @@ -45,7 +45,7 @@ export default class LoggerLWCEmbedDemo extends LightningElement { console.log('and a stack trace', new Error().stack); const entry = logger .error(this.message) - .setError(error) + .setExceptionDetails(error) .setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }) .addTags(this.tagsString.split(',')); console.log('entry==', JSON.parse(JSON.stringify(entry))); @@ -57,7 +57,11 @@ 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).setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }).addTags(this.tagsString.split(',')); + logger + .error(this.message) + .setExceptionDetails(someError) + .setField({ SomeLogEntryField__c: 'some text from loggerLWCEmbedDemo' }) + .addTags(this.tagsString.split(',')); } logWarnExample() { diff --git a/nebula-logger/recipes/lwc/loggerLWCImportDemo/__tests__/loggerLWCImportDemo.test.js b/nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/__tests__/loggerLWCGetLoggerImportDemo.test.js similarity index 86% rename from nebula-logger/recipes/lwc/loggerLWCImportDemo/__tests__/loggerLWCImportDemo.test.js rename to nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/__tests__/loggerLWCGetLoggerImportDemo.test.js index c19307054..1497c856e 100644 --- a/nebula-logger/recipes/lwc/loggerLWCImportDemo/__tests__/loggerLWCImportDemo.test.js +++ b/nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/__tests__/loggerLWCGetLoggerImportDemo.test.js @@ -1,5 +1,5 @@ import { createElement } from 'lwc'; -import loggerLWCImportDemo from 'c/loggerLWCImportDemo'; +import loggerLWCGetLoggerImportDemo from 'c/loggerLWCGetLoggerImportDemo'; import getSettings from '@salesforce/apex/ComponentLogger.getSettings'; @@ -35,7 +35,7 @@ describe('logger demo tests', () => { it('mounts and saves log correctly in one go', async () => { getSettings.mockResolvedValue({ ...MOCK_GET_SETTINGS }); - const demo = createElement('c-logger-demo', { is: loggerLWCImportDemo }); + const demo = createElement('c-logger-demo', { is: loggerLWCGetLoggerImportDemo }); document.body.appendChild(demo); await flushPromises(); diff --git a/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.html b/nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/loggerLWCGetLoggerImportDemo.html similarity index 91% rename from nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.html rename to nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/loggerLWCGetLoggerImportDemo.html index 52118995d..edffc497b 100644 --- a/nebula-logger/recipes/lwc/loggerLWCImportDemo/loggerLWCImportDemo.html +++ b/nebula-logger/recipes/lwc/loggerLWCGetLoggerImportDemo/loggerLWCGetLoggerImportDemo.html @@ -6,10 +6,10 @@ -->