Skip to content

Commit

Permalink
Added a new Apex static method Logger.setField() so custom fields can…
Browse files Browse the repository at this point in the history
… be set once per transaction --> auto-populated on all LogEntryEvent__e records
  • Loading branch information
jongpie committed Sep 27, 2024
1 parent cfdae6b commit 8356672
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, Process Builder & integrations.

## Unlocked Package - v4.14.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)
Expand Down
2 changes: 1 addition & 1 deletion docs/apex/Logger-Engine/LogEntryEventBuilder.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ The same instance of `LogEntryEventBuilder`, useful for chaining methods

#### `setField(Schema.SObjectField field, Object fieldValue)``LogEntryEventBuilder`

Sets a field values on the builder's `LogEntryEvent__e` record
Sets a field value on the builder's `LogEntryEvent__e` record

##### Parameters

Expand Down
21 changes: 21 additions & 0 deletions docs/apex/Logger-Engine/Logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -4948,6 +4948,27 @@ Stores additional details about the current transacation's async context
| -------------------- | ------------------------------------------------------ |
| `schedulableContext` | - The instance of `System.SchedulableContext` to track |

#### `setField(Schema.SObjectField field, Object fieldValue)``void`

Sets a field value on every generated `LogEntryEvent__e` record

##### Parameters

| Param | Description |
| ------------ | -------------------------------------------------------- |
| `field` | The `Schema.SObjectField` token of the field to populate |
| `fieldValue` | The `Object` value to populate in the provided field |

#### `setField(Map<Schema.SObjectField, Object> fieldToValue)``void`

Sets multiple field values oon every generated `LogEntryEvent__e` record

##### Parameters

| Param | Description |
| -------------- | ---------------------------------------------------------------------- |
| `fieldToValue` | An instance of `Map&lt;Schema.SObjectField, Object&gt;` containing the |

#### `setParentLogTransactionId(String parentTransactionId)``void`

Relates the current transaction&apos;s log to a parent log via the field Log**c.ParentLog**c This is useful for relating multiple asynchronous operations together, such as batch &amp; queueable jobs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ global with sharing class LogEntryEventBuilder {
}

/**
* @description Sets a field values on the builder's `LogEntryEvent__e` record
* @description Sets a field value on the builder's `LogEntryEvent__e` record
* @param field The `Schema.SObjectField` token of the field to populate
* on the builder's `LogEntryEvent__e` record
* @param fieldValue The `Object` value to populate in the provided field
Expand All @@ -646,7 +646,7 @@ global with sharing class LogEntryEventBuilder {
/**
* @description Sets multiple field values on the builder's `LogEntryEvent__e` record
* @param fieldToValue An instance of `Map<Schema.SObjectField, Object>` containing the
* the fields & values to populate the builder's `LogEntryEvent__e` record
* the fields & values to populate on the builder's `LogEntryEvent__e` record
* @return The same instance of `LogEntryEventBuilder`, useful for chaining methods
*/
@SuppressWarnings('PMD.AvoidDebugStatements')
Expand Down
49 changes: 47 additions & 2 deletions nebula-logger/core/main/logger-engine/classes/Logger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
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<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.';
private static final String ORGANIZATION_DOMAIN_URL = System.URL.getOrgDomainUrl()?.toExternalForm();
private static final String REQUEST_ID = System.Request.getCurrent().getRequestId();
private static final Map<String, SaveMethod> SAVE_METHOD_NAME_TO_SAVE_METHOD = new Map<String, SaveMethod>();
private static final Map<Schema.SObjectField, Object> TRANSACTION_FIELD_TO_VALUE = new Map<Schema.SObjectField, Object>();
private static final String TRANSACTION_ID = System.UUID.randomUUID().toString();

private static AsyncContext currentAsyncContext;
Expand Down Expand Up @@ -250,6 +251,30 @@ global with sharing class Logger {
return parentLogTransactionId;
}

/**
* @description Sets a field value on every generated `LogEntryEvent__e` record
* @param field The `Schema.SObjectField` token of the field to populate
* on each `LogEntryEvent__e` record in the current transaction
* @param fieldValue The `Object` value to populate in the provided field
*/
global static void setField(Schema.SObjectField field, Object fieldValue) {
setField(new Map<Schema.SObjectField, Object>{ field => fieldValue });
}

/**
* @description Sets multiple field values oon every generated `LogEntryEvent__e` record
* @param fieldToValue An instance of `Map<Schema.SObjectField, Object>` containing the
* the fields & values to populate on each `LogEntryEvent__e` record in the current transaction
*/
global static void setField(Map<Schema.SObjectField, Object> fieldToValue) {
if (getUserSettings().IsEnabled__c == false) {
return;
}

TRANSACTION_FIELD_TO_VALUE.putAll(fieldToValue);
TRANSACTION_FIELD_TO_VALUE.remove(null);
}

/**
* @description Indicates if logging has been enabled for the current user, based on the custom setting LoggerSettings__c
* @return Boolean
Expand Down Expand Up @@ -3390,8 +3415,9 @@ global with sharing class Logger {

return logEntryEventBuilder;
}

private static void finalizeEntry(LogEntryEvent__e logEntryEvent) {
setTransactionFields(logEntryEvent);

logEntryEvent.ParentLogTransactionId__c = getParentLogTransactionId();
logEntryEvent.TransactionScenario__c = transactionScenario;

Expand All @@ -3403,6 +3429,25 @@ global with sharing class Logger {
}
}

@SuppressWarnings('PMD.AvoidDebugStatements')
private static void setTransactionFields(LogEntryEvent__e logEntryEvent) {
for (Schema.SObjectField field : TRANSACTION_FIELD_TO_VALUE.keySet()) {
Object value = TRANSACTION_FIELD_TO_VALUE.get(field);

try {
Schema.DescribeFieldResult fieldDescribe = field.getDescribe();
if (fieldDescribe.getSoapType() == Schema.SoapType.STRING) {
value = LoggerDataStore.truncateFieldValue(field, (String) value);
}

logEntryEvent.put(field, value);
} catch (System.Exception ex) {
LogMessage logMessage = new LogMessage('Could not set field {0} with value {1}', field, value);
System.debug(System.LoggingLevel.WARN, logMessage.getMessage());
}
}
}

private static Boolean hasValidStartAndEndTimes(LoggerSettings__c settings) {
Datetime nowish = System.now();
Boolean isStartTimeValid = settings.StartTime__c == null || settings.StartTime__c <= nowish;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import FORM_FACTOR from '@salesforce/client/formFactor';
import { log as lightningLog } from 'lightning/logger';
import { LoggerStackTrace } from './loggerStackTrace';

const CURRENT_VERSION_NUMBER = 'v4.14.12';
const CURRENT_VERSION_NUMBER = 'v4.14.13';

const LOGGING_LEVEL_EMOJIS = {
ERROR: '⛔',
Expand Down
60 changes: 60 additions & 0 deletions nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,66 @@ private class Logger_Tests {
System.Assert.isNull(Logger.getParentLogTransactionId());
}

// Start setField() test methods
@IsTest
static void it_should_set_single_transaction_field_on_all_entries_when_events_published() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
// HttpRequestBody__c is used as an example here since it's provided out of the box, and doesn't
// have a default value set.
// But realisticially, this functionality is intended to be used with custom fields that are added to
// Nebula Logger's data model.
Schema.SObjectField field = Schema.LogEntryEvent__e.HttpRequestBody__c;
String fieldValue = 'Some_value';
Integer countOfEntriesToAdd = 3;

for (Integer i = 0; i < countOfEntriesToAdd; i++) {
LogEntryEventBuilder builder = Logger.info('hello, world');
System.Assert.isNull(builder.getLogEntryEvent().get(field), 'Field ' + field + ' should be null until the event is published');
// Call setField() after some entries have been added (and before other entries are added)
// to ensure that all entries have the fields populated on publish
if (i == 1) {
Logger.setField(field, fieldValue);
}
}
Logger.saveLog();

System.Assert.areEqual(countOfEntriesToAdd, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
for (LogEntryEvent__e publishedLogEntryEvent : (List<LogEntryEvent__e>) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents()) {
System.Assert.areEqual(fieldValue, publishedLogEntryEvent.get(field));
}
}

@IsTest
static void it_should_set_map_of_transaction_fields_on_all_entries_when_events_published() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
// HttpRequestBody__c is used as an example here since it's provided out of the box, and doesn't
// have a default value set.
// But realisticially, this functionality is intended to be used with custom fields that are added to
// Nebula Logger's data model.
Schema.SObjectField field = Schema.LogEntryEvent__e.HttpRequestBody__c;
String fieldValue = 'Some_value';
Integer countOfEntriesToAdd = 3;

for (Integer i = 0; i < countOfEntriesToAdd; i++) {
LogEntryEventBuilder builder = Logger.info('hello, world');
System.Assert.isNull(builder.getLogEntryEvent().get(field), 'Field ' + field + ' should be null until the event is published');
// Call setField() after some entries have been added (and before other entries are added)
// to ensure that all entries have the fields populated on publish
if (i == 1) {
Logger.setField(new Map<Schema.SObjectField, Object>{ field => fieldValue });
}
}
Logger.saveLog();

System.Assert.areEqual(countOfEntriesToAdd, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
for (LogEntryEvent__e publishedLogEntryEvent : (List<LogEntryEvent__e>) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents()) {
System.Assert.areEqual(fieldValue, publishedLogEntryEvent.get(field));
}
}
// End setField() test methods

@IsTest
static void it_should_return_quiddity_level() {
List<System.Quiddity> acceptableDefaultQuidditiesForTests = new List<System.Quiddity>{
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nebula-logger",
"version": "4.14.12",
"version": "4.14.13",
"description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.",
"author": "Jonathan Gillespie",
"license": "MIT",
Expand Down
6 changes: 3 additions & 3 deletions sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"path": "./nebula-logger/core",
"definitionFile": "./config/scratch-orgs/base-scratch-def.json",
"scopeProfiles": true,
"versionNumber": "4.14.12.NEXT",
"versionName": "Replaced HttpRequestEndpoint__c with HttpRequestEndpointAddress__c",
"versionDescription": "Deprecated the HttpRequestEndpoint__c fields on LogEntryEvent__e and LogEntry__c, and replaced them with new HttpRequestEndpointAddress__c fields that have a much longer max length (2,000 vs 255)",
"versionNumber": "4.14.13.NEXT",
"versionName": "New Apex Static Method Logger.setField()",
"versionDescription": "Added a new Apex static method Logger.setField() so custom fields can be set once per transaction --> auto-populated on all LogEntryEvent__e records",
"releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases",
"unpackagedMetadata": {
"path": "./nebula-logger/extra-tests"
Expand Down

0 comments on commit 8356672

Please sign in to comment.