Skip to content

Commit

Permalink
Release v4.2.0 (#100)
Browse files Browse the repository at this point in the history
* Released new managed package v4.2.0

* Added 'view documentation' button in README + topics details

* Added & updated javadoc comments in all classes

* DmlResultErrors__c field is now DatabaseResultJson__c, updated naming convention to use 'Database' instead of 'DML'
  • Loading branch information
jongpie authored Mar 9, 2021
1 parent 4672216 commit 7944b0d
Show file tree
Hide file tree
Showing 31 changed files with 2,634 additions and 986 deletions.
83 changes: 45 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@

Designed for Salesforce admins, developers & architects. A robust logger for Apex, Flow, Process Builder & Integrations.

[![Install Managed Package](./content/btn-install-managed-package.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000000XJZ7QAO)
[![Deploy Unpackaged Metadata](./content/btn-deploy-unmanaged-metadata.png)](https://githubsfdeploy.herokuapp.com)
[![Install Managed Package](./content/btn-install-managed-package.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000000Xg4wQAC)
[![View Documentation](./content/btn-view-documentation.png)](https://jongpie.github.io/NebulaLogger/)

## Features
1. Easily add log entries via Apex, Flow & Process Builder to generate 1 consolidate log
2. Manage & report on logging data using the `Nebula__Log__c` and `Nebula__LogEntry__c` objects
3. Leverage `Nebula__LogEntryEvent__e` platform events for real-time monitoring & integrations
4. Enable logging and set the logging level for different users & profiles using `Nebula__LoggerSettings__c` custom hierarchy setting
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
5. View related log entries on any record page by adding the 'Related Log Entries' component in App Builder
6. Dynamically assign Topics to `Log__c` and `LogEntry__c` records for tagging/labeling your logs (not currently available in the managed package)

## Installing

In general, using the managed package is recommended - but you can choose to either deploy the metadata from this repo to your org, or install the managed package. The metadata is the same, but there are some differences in using the 2 versions. All examples in README include the `Nebula` namespace - simply remove the namespace from the examples if you are using the unpackaged metadata.
You can choose to either deploy the metadata from this repo to your org, or install the managed package. The metadata is the same, but there are some differences in using the 2 versions. All examples in README are for the unmanaged metadata - simply add the `Nebula` namespace from the examples if you are using the managed package.

| | Unpackaged Metadata | 2nd Gen Managed Package |
| ----------- | ------------------- | ----------------------- |
| Namespace | none | `Nebula` |
| Future Releases | New enhancements & bugfixes will be immediately available in GitHub | Slower release cycle: new package versions will only be released once new enhancements & bugfixes have been tested and code is stabilized |
| Public Apex Methods | Any `public` Apex methods are subject to change in the future - they can be used, but you may encounter deployment issues if future changes to `public` methods are not backwards-compatible | Only `global` methods are available in managed packages - any `global` Apex methods available in the managed package will be supported for the foreseeable future |
| Apex Debug Statements | `System.debug()` is automatically called | Requires adding your own calls for `System.debug()` due to Salesforce limitations with managed packages |
| Apex Stack Traces | Automatically stored in `LogEntry__c.StackTrace__c` when calling methods like `Logger.debug('my message');` | Requires calling `parseStackTrace()` due to Salesforce limitations with managed packages. For example:<br />`Nebula.Logger.debug('my message').parseStackTrace(new DmlException().getStackTrace());` |
| Apex Stack Traces | Automatically stored in `LogEntry__c.StackTrace__c` when calling methods like `Logger.debug('my message');` | Requires calling `parseStackTrace()` due to Salesforce limitations with managed packages. For example:<br />`Logger.debug('my message').parseStackTrace(new DmlException().getStackTrace());` |
| Assign Topics (Tagging/Labeling System) | Provide `List<String> topics` in Apex or Flow to dynamically assign Salesforce Topics to `Log__c` and `LogEntry__c` records | This functionality is not currently available in the managed package |

## Getting Started
After deploying Nebula Logger to your org, there are a few additional configuration changes needed...
Expand All @@ -32,28 +35,32 @@ After deploying Nebula Logger to your org, there are a few additional configurat
* `LoggerEndUser` provides access to generate logs, as well as read-only access to any log records shared with the user.
* `LoggerLogViewer` provides view-all access (read-only) to all log records. This does **not** provide access to generate logs.
* `LoggerLogAdmin` provides view-all and modify-all access to all log records.
* Customize the default settings in `Nebula__LoggerSettings__c`
* Customize the default settings in `LoggerSettings__c`
* You can customize settings at the org, profile and user levels
* Unmanaged Metadata Only: Enable Salesforce Topics for the `Log__c` and `LogEntry__c` objects for tagging/labeling. See [Salesforce Help](https://help.salesforce.com/articleView?id=sf.collab_topics_records_admin.htm) for more details.
* Currently, enabling Topics for objects must still be done using the Salesforce Classic UI. Once enabled, Topics can then be used from withing Lightning Experience.
* Once enabled, Topics can be added via Apex and Flow and then used as list view filters (and more) for the object `Log__c`.


## Logger for Apex: Quick Start
For Apex developers, the `Nebula.Logger` class has several methods that can be used to add entries with different logging levels. Each logging level's method has several overloads to support multiple parameters.
For Apex developers, the `Logger` class has several methods that can be used to add entries with different logging levels. Each logging level's method has several overloads to support multiple parameters.

```java
// This will generate a debug statement within developer console
System.debug('Debug statement using native Apex');

// This will create a new `Nebula__Log__c` record with multiple related `Nebula__LogEntry__c` records
Nebula.Logger.error('Add log entry using Nebula Logger with logging level == ERROR');
Nebula.Logger.warn('Add log entry using Nebula Logger with logging level == WARN');
Nebula.Logger.info('Add log entry using Nebula Logger with logging level == INFO');
Nebula.Logger.debug('Add log entry using Nebula Logger with logging level == DEBUG');
Nebula.Logger.fine('Add log entry using Nebula Logger with logging level == FINE');
Nebula.Logger.finer('Add log entry using Nebula Logger with logging level == FINER');
Nebula.Logger.finest('Add log entry using Nebula Logger with logging level == FINEST');
Nebula.Logger.saveLog();
// This will create a new `Log__c` record with multiple related `LogEntry__c` records
Logger.error('Add log entry using Nebula Logger with logging level == ERROR');
Logger.warn('Add log entry using Nebula Logger with logging level == WARN');
Logger.info('Add log entry using Nebula Logger with logging level == INFO');
Logger.debug('Add log entry using Nebula Logger with logging level == DEBUG');
Logger.fine('Add log entry using Nebula Logger with logging level == FINE');
Logger.finer('Add log entry using Nebula Logger with logging level == FINER');
Logger.finest('Add log entry using Nebula Logger with logging level == FINEST');
Logger.saveLog();
```

This results in 1 `Nebula__Log__c` record with several related `Nebula__LogEntry__c` records.
This results in 1 `Log__c` record with several related `LogEntry__c` records.

![Apex Log Results](./content/apex-log.png)

Expand All @@ -66,7 +73,7 @@ In this simple example, a Flow is configured after-insert and after-update to lo

![Flow Builder: Log Case](./content/flow-builder-log-case.png)

This results in a `Nebula__Log__c` record with related `Nebula__LogEntry__c` records.
This results in a `Log__c` record with related `LogEntry__c` records.

![Flow Log Results](./content/flow-log.png)

Expand All @@ -76,37 +83,37 @@ After incorporating Logger into your Flows & Apex code (including controllers, t
```java
Case currentCase = [SELECT Id, CaseNumber, Type, Status, IsClosed FROM Case LIMIT 1];

Nebula.Logger.info('First, log the case through Apex', currentCase);
Logger.info('First, log the case through Apex', currentCase);

Nebula.Logger.debug('Now, we update the case in Apex to cause our record-triggered Flow to run');
Logger.debug('Now, we update the case in Apex to cause our record-triggered Flow to run');
update currentCase;

Nebula.Logger.info('Last, save our log');
Nebula.Logger.saveLog();
Logger.info('Last, save our log');
Logger.saveLog();
```

This generates 1 consolidated `Nebula__Log__c`, containing `Nebula__LogEntry__c` records from both Apex and Flow
This generates 1 consolidated `Log__c`, containing `LogEntry__c` records from both Apex and Flow

![Flow Log Results](./content/combined-apex-flow-log.png)

## Event-Driven Integrations with Platform Events
Logger is built using Salesforce's [Platform Events](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_intro.htm), an event-driven messaging architecture. External integrations can subscribe to log events using the `Nebula__LogEntryEvent__e` object - see more details at [the Platform Events Developer Guide site](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_subscribe_cometd.htm)
Logger is built using Salesforce's [Platform Events](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_intro.htm), an event-driven messaging architecture. External integrations can subscribe to log events using the `LogEntryEvent__e` object - see more details at [the Platform Events Developer Guide site](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_subscribe_cometd.htm)

## Managing Logs
To help development and support teams better manage logs (and any underlying code or config issues), some fields on `Nebula__Log__c` are provided to track the owner, priority and status of a log. These fields are optional, but are helpful in critical environments (production, QA sandboxes, UAT sandboxes, etc.) for monitoring ongoing user activities.
* All editable fields on `Nebula__Log__c` can be updated via the 'Manage Log' quick action (shown below)
To help development and support teams better manage logs (and any underlying code or config issues), some fields on `Log__c` are provided to track the owner, priority and status of a log. These fields are optional, but are helpful in critical environments (production, QA sandboxes, UAT sandboxes, etc.) for monitoring ongoing user activities.
* All editable fields on `Log__c` can be updated via the 'Manage Log' quick action (shown below)

![Manage Log QuickAction](./content/manage-log-quickaction.png)
* Additional fields are automatically set based on changes to `Nebula__Log__c.Nebula__Status__c`
* `Nebula__Log__c.Nebula__ClosedBy__c` - The user who closed the log
* `Nebula__Log__c.Nebula__ClosedDate__c` - The datetime that the log was closed
* `Nebula__Log__c.Nebula__IsClosed__c` - Indicates if the log is closed, based on the selected status (and associated config in the 'Log Status' custom metadata type)
* `Nebula__Log__c.Nebula__IsResolved__c` - Indicates if the log is resolved (meaning that it required analaysis/work, which has been completed). Only closed statuses can be considered resolved. This is also driven based on the selected status (and associated config in the 'Log Status' custom metadata type)
* To customize the statuses provided, simply update the picklist values for `Nebula__Log__c.Nebula__Status__c` and create/update corresponding records in the custom metadata type `Nebula__LogStatus__mdt`. This custom metadata type controls which statuses are considerd closed and resolved.
* Additional fields are automatically set based on changes to `Log__c.Status__c`
* `Log__c.ClosedBy__c` - The user who closed the log
* `Log__c.ClosedDate__c` - The datetime that the log was closed
* `Log__c.IsClosed__c` - Indicates if the log is closed, based on the selected status (and associated config in the 'Log Status' custom metadata type)
* `Log__c.IsResolved__c` - Indicates if the log is resolved (meaning that it required analaysis/work, which has been completed). Only closed statuses can be considered resolved. This is also driven based on the selected status (and associated config in the 'Log Status' custom metadata type)
* To customize the statuses provided, simply update the picklist values for `Log__c.Status__c` and create/update corresponding records in the custom metadata type `LogStatus__mdt`. This custom metadata type controls which statuses are considerd closed and resolved.

## View Related Log Entries
Within App Builder, admins can add the 'Related Log Entries' lightning web component to any record page. Admins can also control which columns are displayed be creating & selecting a field set on `Nebula__LogEntry__c` with the desired fields.
* The component automatically shows any related log entries, based on `Nebula__LogEntry__c.Nebula__RecordId__c == :recordId`
Within App Builder, admins can add the 'Related Log Entries' lightning web component to any record page. Admins can also control which columns are displayed be creating & selecting a field set on `LogEntry__c` with the desired fields.
* The component automatically shows any related log entries, based on `LogEntry__c.RecordId__c == :recordId`
* Users can search the list of log entries for a particular record using the component's built-insearch box. The component dynamically searches all related log entries using SOSL.
* Component automatically enforces Salesforce's security model
* Object-Level Security - Users without read access to LogEntry__c will not see the component
Expand All @@ -116,7 +123,7 @@ Within App Builder, admins can add the 'Related Log Entries' lightning web compo
![Related Log Entries](./content/relate-log-entries-lwc.png)
## Deleting Old Logs
Two Apex classes are provided out-of-the-box to handle automatically deleting old logs
1. `Nebula.LogBatchPurger` - this batch Apex class will delete any `Nebula__Log__c` records with `Nebula__Log__c.Nebula__LogRetentionDate__c <= System.today()`.
* By default, this field is populated with "TODAY + 14 DAYS" - the number of days to retain a log can be customized in `Nebula__LoggerSettings__c`.
1. `LogBatchPurger` - this batch Apex class will delete any `Log__c` records with `Log__c.LogRetentionDate__c <= System.today()`.
* By default, this field is populated with "TODAY + 14 DAYS" - the number of days to retain a log can be customized in `LoggerSettings__c`.
* Users can also manually edit this field to change the retention date - or set it to null to prevent the log from being automatically deleted
2. `Nebula.LogBatchPurgeScheduler` - this schedulable Apex class can be schedule to run `Nebula.LogBatchPurger` on a daily or weekly basis
2. `LogBatchPurgeScheduler` - this schedulable Apex class can be schedule to run `LogBatchPurger` on a daily or weekly basis
4 changes: 2 additions & 2 deletions config/apexdocs.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"root": "nebula-logger-docs",
"root": "",
"sourceLanguage": "java",
"home": {
"header": "./config/apexdocs-header.md"
Expand All @@ -9,4 +9,4 @@
"includeAuthor": false,
"includeDate": false
}
}
}
Binary file added content/btn-view-documentation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

/**
* @group log-management
* @description Schedulable class used to schedule the batch job LogBatchPurger
* @description Schedulable class used to schedule the batch job `LogBatchPurger`
* @see LogBatchPurger
*/
global with sharing class LogBatchPurgeScheduler implements System.Schedulable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

/**
* @group log-management
* @description Batch class used to delete old logs, based on Log__c.LogRetentionDate__c <= :System.today()
* @description Batch class used to delete old logs, based on `Log__c.LogRetentionDate__c <= :System.today()`
* @see LogBatchPurgeScheduler
*/
global with sharing class LogBatchPurger implements Database.Batchable<SObject>, Database.Stateful {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

/**
* @group log-management
* @description Subscribes to LogEntryEvent__e platform events and normalizes the data into Log__c and LogEntry__c records
* @description Subscribes to `LogEntryEvent__e` platform events and normalizes the data into `Log__c` and `LogEntry__c` records
*/
public without sharing class LogEntryEventHandler {
private static final Log__c LOG = new Log__c();
Expand Down Expand Up @@ -104,8 +104,8 @@ public without sharing class LogEntryEventHandler {
: logEntryEvent.Timestamp__c;

LogEntry__c logEntry = new LogEntry__c(
DmlResultErrors__c = logEntryEvent.DmlResultErrors__c,
DmlResultType__c = logEntryEvent.DmlResultType__c,
DatabaseResultJson__c = logEntryEvent.DatabaseResultJson__c,
DatabaseResultType__c = logEntryEvent.DatabaseResultType__c,
ExceptionMessage__c = logEntryEvent.ExceptionMessage__c,
ExceptionStackTrace__c = logEntryEvent.ExceptionStackTrace__c,
ExceptionType__c = logEntryEvent.ExceptionType__c,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@

/**
* @group log-management
* @description Dynamically returns LogEntry__c field sets in App Builder when configuring the component RelatedLogEntries
* @description Dynamically returns `LogEntry__c` field sets in App Builder when configuring the component RelatedLogEntries
*/
public without sharing class LogEntryFieldSetPicklist extends VisualEditor.DynamicPickList {
/**
* Returns a default value of null - admins must select a field set within App Builder
* @return return null (no default value)
* @return null (no default value)
*/
public override VisualEditor.DataRow getDefaultValue() {
return null;
}

/**
* Returns the list of fields sets on LogEntry__c, allowing admins to specify any
* field set for each instance of the RelatedLogEntries component
* @description Returns the list of fields sets on `LogEntry__c`, allowing admins to specify any
* field set for each instance of the `RelatedLogEntries` component
* @return The list of field sets on the LogEntry__c object
*/
public override VisualEditor.DynamicPickListRows getValues() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

/**
* @group log-management
* @description Manages setting fields on LogEntry__c before insert & update
* @description Manages setting fields on `LogEntry__c` before insert & before update
*/
public without sharing class LogEntryHandler {
/**
* @description Runs the trigger handler's logic
*/
public void execute() {
List<LogEntry__c> logEntries = (List<LogEntry__c>) Trigger.new;

Expand Down
5 changes: 4 additions & 1 deletion nebula-logger/main/log-management/classes/LogHandler.cls
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

/**
* @group log-management
* @description Manages setting fields on Log__c before insert & update
* @description Manages setting fields on `Log__c` before insert & before update
*/
public without sharing class LogHandler {
@testVisible
Expand All @@ -20,6 +20,9 @@ public without sharing class LogHandler {
}
}

/**
* @description Runs the trigger handler's logic
*/
public void execute() {
List<Log__c> logs = (List<Log__c>) Trigger.new;
Map<Id, Log__c> oldLogsById = (Map<Id, Log__c>) Trigger.oldMap;
Expand Down
Loading

0 comments on commit 7944b0d

Please sign in to comment.