Skip to content

Commit

Permalink
Added support for custom field mappings (#664)
Browse files Browse the repository at this point in the history
* Added the ability to set & map custom fields, using a new CMDT LoggerFieldMapping__mdt and new LogEntryEventBuilder instance method overloads setField(Schema.SObjectField field, Object fieldValue) and 
setField(Map<Schema.SObjectField, Object> fieldToValue)

* Added some CMDT records to the extra-tests directory that map the included custom fields (also stored in the extra-tests directory). These CMDT records are just to help with functionally/manually testing in a scratch org - they won't be included in any of the packages

* Scope creep: Updated several config classes to consistently have test-visible private methods before non-test-visible private methods

* Updated README.md to add details about custom field mappings, and cleaned up/updated some other README contents

* Added .github/FUNDING.yml so the repo has a sponsor button
  • Loading branch information
jongpie authored Jul 12, 2024
1 parent 48ea5fe commit 2dbbf9f
Show file tree
Hide file tree
Showing 43 changed files with 1,093 additions and 76 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [jongpie]
145 changes: 101 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

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

## Unlocked Package - v4.13.13
## Unlocked Package - v4.13.14

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

`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oDsQAI`
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oE2QAI`

`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oDsQAI`
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oE2QAI`

---

Expand All @@ -31,7 +31,7 @@ The most robust logger for Salesforce. Works with Apex, Lightning Components, Fl

## Features

1. Easily add log entries via Apex, Lightning Components (lwc & aura), Flow & Process Builder to generate 1 consolidated, unified log
1. Easily add log entries via Apex, Lightning Components (lightning web components (LWCs) & aura components), Flow & Process Builder to generate 1 consolidated, unified log
2. Manage & report on logging data using the `Log__c` and `LogEntry__c` objects
3. Leverage `LogEntryEvent__e` platform events for real-time monitoring & integrations
4. Enable logging and set the logging level for different users & profiles using `LoggerSettings__c` custom hierarchy setting
Expand Down Expand Up @@ -86,11 +86,6 @@ Nebula Logger is available as both an unlocked package and a managed package. Th
<td><code>System.debug()</code> is automatically called - the output can be configured with <code>LoggerSettings__c.SystemLogMessageFormat__c</code> to use any field on <code>LogEntryEvent__e</code></td>
<td>Requires adding your own calls for <code>System.debug()</code> due to Salesforce limitations with managed packages</td>
</tr>
<tr>
<td>Apex Stack Traces</td>
<td>Automatically stored in <code>LogEntry__c.StackTrace__c</code> when calling methods like <code>Logger.debug('my message');</code></td>
<td>Requires calling <code>parseStackTrace()</code> due to Salesforce limitations with managed packages. For example:<br><code>Logger.debug('my message').parseStackTrace(new DmlException().getStackTrace());</code></td>
</tr>
<tr>
<td>Logger Plugin Framework</td>
<td>Leverage Apex or Flow to build your own "plugins" for Logger - easily add your own automation to the any of the included objects: <code>LogEntryEvent__e</code>, <code>Log__c</code>, <code>LogEntry__c</code>, <code>LogEntryTag__c</code> and <code>LoggerTag__c</code>. The logger system will then automatically run your plugins for each trigger event (BEFORE_INSERT, BEFORE_UPDATE, AFTER_INSERT, AFTER_UPDATE, and so on).</td>
Expand Down Expand Up @@ -142,20 +137,21 @@ This results in 1 `Log__c` record with several related `LogEntry__c` records.

### Logger for Lightning Components: Quick Start

For lightning component developers, the `logger` lwc provides very similar functionality that is offered in Apex. Simply embed 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 incorporate the `logger` LWC into your component, and call the desired logging methods within your code.

```javascript
// For lwc, retrieve logger from your component's template
const logger = this.template.querySelector('c-logger');
// For LWC, import logger's createLogger() function into your component
import { createLogger } from 'c/logger';

logger.error('Hello, world!').addTag('some important tag');
logger.warn('Hello, world!');
logger.info('Hello, world!');
logger.debug('Hello, world!');
logger.fine('Hello, world!');
logger.finer('Hello, world!');
logger.finest('Hello, world!');
logger.saveLog();
export default class LoggerLWCDemo extends LightningElement {
logger;

async connectedCallback() {
this.logger = await createLogger();
this.logger.info('Hello, world');
this.logger.saveLog();
}
}
```

```javascript
Expand Down Expand Up @@ -388,7 +384,7 @@ For more details, check out the `LogMessage` class [documentation](https://jongp

## Features for Lightning Component Developers

For lightning component developers, the included `logger` lwc can be used in other lwc & aura components for frontend logging. Similar to `Logger` and `LogEntryBuilder` Apex classes, the lwc has both `logger` and `logEntryBuilder` classes. This provides a fluent API for javascript developers so they can chain the method calls.
For lightning component developers, the included `logger` LWC can be used in other LWCs & aura components for frontend logging. Similar to `Logger` and `LogEntryBuilder` Apex classes, the LWC has both `logger` and `logEntryBuilder` classes. This provides a fluent API for JavaScript developers so they can chain the method calls.

Once you've incorporated `logger` into your lightning components, you can see your `LogEntry__c` records using the included list view "All Component Log Entries'.

Expand All @@ -400,34 +396,48 @@ Each `LogEntry__c` record automatically stores the component's type ('Aura' or '
#### Example LWC Usage
To use the logger component, it has to be added to your lwc's markup:
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.
```html
<template>
<c-logger></c-logger>
```javascript
// For LWC, import logger's createLogger() function into your component
import { createLogger } from 'c/logger';

<div>My component</div>
</template>
```
export default class LoggerLWCDemo extends LightningElement {
logger;

Once you've added logger to your markup, you can call it in your lwc's controller:
async connectedCallback() {
// Call createLogger() once per component
this.logger = await createLogger();

```javascript
import { LightningElement } from 'lwc';
this.logger.setScenario('some scenario');
this.logger.finer('initialized demo LWC');
}

export default class LoggerDemo extends LightningElement {
logSomeStuff() {
const logger = this.template.querySelector('c-logger');

logger.error('Hello, world!').addTag('some important tag');
logger.warn('Hello, world!');
logger.info('Hello, world!');
logger.debug('Hello, world!');
logger.fine('Hello, world!');
logger.finer('Hello, world!');
logger.finest('Hello, world!');
this.logger.error('Add log entry using Nebula Logger with logging level == ERROR').addTag('some important tag');
this.logger.warn('Add log entry using Nebula Logger with logging level == WARN');
this.logger.info('Add log entry using Nebula Logger with logging level == INFO');
this.logger.debug('Add log entry using Nebula Logger with logging level == DEBUG');
this.logger.fine('Add log entry using Nebula Logger with logging level == FINE');
this.logger.finer('Add log entry using Nebula Logger with logging level == FINER');
this.logger.finest('Add log entry using Nebula Logger with logging level == FINEST');

this.logger.saveLog();
}

logger.saveLog();
doSomething(event) {
this.logger.finest('Starting doSomething() with event: ' + JSON.stringify(event));
try {
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');
} finally {
this.logger.saveLog();
}
}
}
```
Expand Down Expand Up @@ -584,6 +594,53 @@ Once you've implementing log entry tagging within Apex or Flow, you can choose h
---
## Adding Custom Fields to Nebula Logger's Data Model

As of `v4.13.14`, Nebula Logger supports defining, setting, and mapping custom fields within Nebula Logger's data model. This is helpful in orgs that want to extend Nebula Logger's included data model by creating their own org/project-specific fields.

This feature requires that you populate your custom fields yourself, and is only available in Apex currently. The plan is to add in a future release the ability to also set custom fields via JavaScript & Flow.

### Adding Custom Fields to the Platform Event `LogEntryEvent__e`

The first step is to add a field to the platform event `LogEntryEvent__e`

- Create a custom field on `LogEntryEvent__e`. Any data type supported by platform events can be used.

- In this example, a custom text field called `SomeCustomField__c` has been added:

![Custom Field on LogEntryEvent__e](./images/custom-field-log-entry-event.png)

- Populate your field(s) in Apex by calling the instance method overloads `LogEntryEventBuilder.setField(Schema.SObjectField field, Object fieldValue)` or `LogEntryEventBuilder.setField(Map<Schema.SObjectField, Object> fieldToValue)`

```apex
Logger.info('hello, world')
// Set a single field
.setField(LogEntryEvent__e.SomeCustomTextField__c, 'some text value')
// Set multiple fields
.setFields(new Map<Schema.SObjectField, Object>{
LogEntryEvent__e.AnotherCustomTextField__c => 'another text value',
LogEntryEvent__e.SomeCustomDatetimeField__c => System.now()
});
```

### Adding Custom Fields to the Custom Objects `Log__c`, `LogEntry__c`, and `LoggerScenario__c`

If you want to store the data in one of Nebula Logger's custom objects, you can follow the above steps, and also...
- Create an equivalent custom field on one of Nebula Logger's custom objects - right now, only `Log__c`, `LogEntry__c`, and `LoggerScenario__c` are supported.

- In this example, a custom text field _also_ called `SomeCustomField__c` has been added to `Log__c` object - this will be used to store the value of the field `LogEntryEvent__e.SomeCustomField__c`:

![Custom Field on LogEntryEvent__e](./images/custom-field-log.png)

- Create a record in the new CMDT `LoggerFieldMapping__mdt` to map the `LogEntryEvent__e` custom field to the custom object's custom field, shown below. Nebula Logger will automatically populate the custom object's target field with the value of the source `LogEntryEvent__e` field.

- In this example, a custom text field called `SomeCustomField__c` has been added to both `LogEntryEvent__e` and `Log__c`.

![Custom Field on LogEntryEvent__e](./images/custom-field-mapping.png)

---

## Log Management

### Logger Console App
Expand Down
24 changes: 24 additions & 0 deletions docs/apex/Configuration/LoggerFieldMapper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
layout: default
---

## LoggerFieldMapper class

Maps fields values from custom fields on `LogEntryEvent__e` to equivalent fields on `Log__c`, `LogEntry__c`, and `LoggerScenario__c`

---

### Methods

#### `mapFieldValues(SObject sourceRecord, SObject targetRecord)``void`

Copies field values from the `sourceRecord` to the `targetRecord`, based on rules configured in `LoggerFieldMapping_t`

##### Parameters

| Param | Description |
| -------------- | ------------------------------------------------------------------------------- |
| `sourceRecord` | The source `SObject` record containing the data to copy |
| `targetRecord` | The target `SObject` record that should have fields &amp; field values appended |

---
41 changes: 41 additions & 0 deletions docs/apex/Logger-Engine/LogEntryEventBuilder.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,47 @@ LogEntryEventBuilder

The same instance of `LogEntryEventBuilder`, useful for chaining methods

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

Sets a field values on the builder&apos;s `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 |

##### Return

**Type**

LogEntryEventBuilder

**Description**

The same instance of `LogEntryEventBuilder`, useful for chaining methods

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

Sets multiple field values on the builder&apos;s `LogEntryEvent__e` record

##### Parameters

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

##### Return

**Type**

LogEntryEventBuilder

**Description**

The same instance of `LogEntryEventBuilder`, useful for chaining methods

#### `setHttpRequestDetails(System.HttpRequest request)``LogEntryEventBuilder`

Sets the log entry event&apos;s HTTP Request fields
Expand Down
4 changes: 4 additions & 0 deletions docs/apex/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ Class used by the logging system for batch contextual details

Class used to cache query results returned by the selector classes

### [LoggerFieldMapper](Configuration/LoggerFieldMapper)

Maps fields values from custom fields on `LogEntryEvent__e` to equivalent fields on `Log__c`, `LogEntry__c`, and `LoggerScenario__c`

### [LoggerParameter](Configuration/LoggerParameter)

Provides a centralized way to load parameters for SObject handlers &amp; plugins, and casts the parameters to common data types
Expand Down
Binary file added images/custom-field-log-entry-event.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/custom-field-log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/custom-field-mapping.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2dbbf9f

Please sign in to comment.