Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcDBehr committed Jan 23, 2020
0 parents commit ab24759
Show file tree
Hide file tree
Showing 33 changed files with 875 additions and 0 deletions.
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# debug_logger

This package provides the objects and methods to store logs generated by other Apex
classes so they are logged for later analysis. Log data is stored in a custom object and can also be published as Platform Events for live monitoring.

Also included is a scheduable cleanup process that should be configured to run
on a nightly basis to delete old logs, keeping the number of records down. The number of days that records should be kept is configurable
via a custom metadata setting.

If you want to monitor the Platform Events that can be generated, a package that I like can be found at https://github.com/pozil/streaming-monitor, but others are also available

# Methods

* DebugLog(String CallerIdentification)
* CallerIdentification is a string that identifies the process that created the entry
* saveLog(String LogEntry)
* LogEntry is a string that provides the data to be logged
can be called multiple times to append to the log
(but it does not commit the data)
* commitLog(String LogEntry)
* LogEntry is a string that provides data to be logged
* Calling this method saves the data in the custom object and clears the log string
(but leaves the CallerIdentification intact)

# Variables

* endPoint
* used to define the REST endpoint or method [optional]

# Configuration

You can control part of the operation of the package via several Custom Metadata (CMD) settings. The CMD is named 'Debug Logger Setting' and there are 2 fields that you can create in a record called 'Local'

* 'Days To Keep'
* defines the number of days of logs that should be kept during the cleanup processing. Logs older that the specified number of days will be deleted if you schedule the DebugLoggerCleanupScheduler to run as describe on the 'Post Install Steps' below.
* 'Publish Events'
* If this is set to true, will publish a event on the 'Debug_Log' platform event bus. This can be received by other processes for additional handling of the errors if desired.

# Example usage

***

static void myMethod() {
private static string commentString = 'random string';

DebugLog log = new DebugLog('className'); // inits the class with the caller info
log.endPoint = 'myMethod'; // define the calling method
log.saveLog('some comment '+ commentString); // saves some string (but does not commit)

[...]

log.commitLog('Exception caught '); // commits the log entry
}

***

# Post Install Steps

Once the package is installed, you need to schedule the DebugCleanupScheduler job.
Under the Setup menu, look for Custom Code -> Apex Classes. There you will see
a button 'Schedule Apex'. After clicking that button, create a job name and
select the Apex Class "DebugLoggerCleanupScheduler". Specify that it should run
weekly, on everyday of the week, and specify a start and end date as well as a
time (suggested 3:00 AM).

You can also change how many days of logs are kept during the cleanup job
by modifying the Custom Metadata 'Local' settings called 'Debug Logger Settings'.
If this value is changed, it will be used the next time the cleanup job runs.

You should also assign the 'Debug Log Viewer' permission set to any user that will need
to be able to view the logs.


# Single line sfdx set up

```sfdx force:org:create -a logger -n -f config/project-scratch-def.json && sleep 2 ; sfdx force:source:push -u logger && sfdx force:user:permset:assign -u logger -n Debug_Log_Viewer ; sfdx force:org:open -u logger ```


# Credit

Written by Marc D Behr in 2015 with numerous updates over the years
16 changes: 16 additions & 0 deletions config/project-scratch-def.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"orgName": "Emerson",
"edition": "Enterprise",
"hasSampleData": false,
"features": "API;AuthorApex;ServiceCloud;DebugApex",
"settings": {
"orgPreferenceSettings": {
"s1DesktopEnabled": true
},
"securitySettings": {
"sessionSettings": {
"sessionTimeout": "TwelveHours"
}
}
}
}
39 changes: 39 additions & 0 deletions data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# TODO: data

### What type of files should be kept in this directory?

This directory should contain only the following files or file types.

```
README.md -- This file
*-meta.xml -- Specific metadata type files
.gitignore -- Configured to exclude all other files
```

### How are these files used?

The files in this directory are used in the following ways.

* Used like this
* Used like that
* Used like this

### Why are these files located here within the SFDX-Falcon project framework?

This directory and the files it contains are located here within the SFDX-Falcon project framework because...

* Reason one
* Reason two
* Reason three

## Relevant Documentation

* [FlexiPage Type][1] - Specific Metadata Type Reference
* [Metadata Types][2] - General Metadata Type Reference
* [Metadata Components and Types][3] - Supplemental Reference
* [Unsupported Metadata Types][4] - Supplemental Reference

[1]: https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_flexipage.htm
[2]: https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_types_list.htm
[3]: https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_objects_intro.htm
[4]: https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_unsupported_types.htm
40 changes: 40 additions & 0 deletions force-app/main/default/classes/DebugCleanup.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
debug_logger cleanup processor
Cleans old records from the system
Marc D Behr
*/

global class DebugCleanup implements Database.batchable<sObject> {

global Database.QueryLocator start(Database.BatchableContext BC) {
Integer daysToKeep;
if(Test.isRunningTest()) {
daysToKeep = 2; // in the future to ensure that all logs will be deleted when running tests
} else {
// look for the local settings
Debug_Logger_Setting__mdt[] localSettings = [Select Days_To_Keep__c from Debug_Logger_Setting__mdt where DeveloperName = 'Local'];
if(null != localSettings) {
daysToKeep = Integer.valueOf(-(localSettings[0].Days_To_Keep__c));
} else {
daysToKeep = -2; // default to 2 days ago
}
}

String dateString = String.valueof(Date.today().adddays(daysToKeep)).substringBefore(' ')+'T00:00:00Z';
system.debug('Selecting records before ' + dateString);

String query = 'select id from debuginfo__c where Created_Time__c < '+dateString ;
return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, List<debuginfo__c> scope){
delete scope;
DataBase.emptyRecycleBin(scope);
}

global void finish(Database.BatchableContext BC)
{
}
}
5 changes: 5 additions & 0 deletions force-app/main/default/classes/DebugCleanup.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>47.0</apiVersion>
<status>Active</status>
</ApexClass>
32 changes: 32 additions & 0 deletions force-app/main/default/classes/DebugCleanupTest.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
debug_logger cleanup processor
Tests the class that cleans old records from the system
Marc D Behr
*/

@isTest
private class DebugCleanupTest {
static list<DebugInfo__c> debugInfoList ;

@TestSetup static void createRecords() {
debugInfoList = new list<DebugInfo__c> ();

for (integer i = 0; i < 200; i++) {
debugInfoList.add(new DebugInfo__c(DebugData__c = 'Test ' + i));
}
insert debugInfoList;
}

static testMethod void DebugLogsAreDeletedWhenBatchIsRun() {
debugInfoList = [Select Id from DebugInfo__c];
system.AssertEquals(200,debugInfoList.size());
Test.startTest();
Database.executeBatch(new DebugCleanup());
Test.stopTest();
debugInfoList = [Select Id from DebugInfo__c];
system.AssertEquals(0,debugInfoList.size());
}

}
5 changes: 5 additions & 0 deletions force-app/main/default/classes/DebugCleanupTest.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>47.0</apiVersion>
<status>Active</status>
</ApexClass>
76 changes: 76 additions & 0 deletions force-app/main/default/classes/DebugLog.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
DebugLog class
provides a method of saving logs in a custom object
Methods:
DebugLog(String CallerIdentification)
CallerIdentification is a string that identifies the process that created the entry
saveLog(String LogEntry)
LogEntry is a string that provides the data to be logged
can be called multiple times to append to the log
commitLog(String LogEntry)
LogEntry is a string that provides data to be logged
Calling this method saves the data in the custom object and clears the log string
(but leaves the CallerIdentification intact)
Marc D Behr
*/

public with sharing class DebugLog {

private String logData { set; get { if (logData == null) { logData = ''; } return logData; } }
private string caller { set; get; }
private Boolean publishEvent { set; get; }
public string endPoint { public set; private get { if (endPoint == null) { endPoint = 'undefined'; } return endPoint; } }


public DebugLog(String procedure) {
caller = procedure;
Debug_Logger_Setting__mdt[] localSettings = [Select Publish_Events__c from Debug_Logger_Setting__mdt where DeveloperName = 'Local'];
if(null != localSettings) {
publishEvent = true; // publish by default
} else {
publishEvent = localSettings[0].Publish_Events__c;
}
}

public void saveLog(String entry) {
logData += entry + '\n';
}

public void commitLog(String entry) {
logData += entry + '\n';
try {
DebugInfo__c debugInfo = new DebugInfo__c(
Caller__c = caller.abbreviate(SObjectType.DebugInfo__c.Fields.Caller__c.Length),
DebugData__c = obscurePasswords(logData).abbreviate(SObjectType.DebugInfo__c.Fields.DebugData__c.Length),
EndPoint__c = endPoint.abbreviate(SObjectType.DebugInfo__c.Fields.EndPoint__c.Length)
);
insert debugInfo;

if (publishEvent) {
// publish the platform event
Debug_Log__e debugEvent = new Debug_Log__e(
Caller__c = caller.abbreviate(SObjectType.Debug_Log__e.Fields.Caller__c.Length),
DebugData__c = obscurePasswords(logData).abbreviate(SObjectType.Debug_Log__e.Fields.DebugData__c.Length),
EndPoint__c = endPoint.abbreviate(SObjectType.Debug_Log__e.Fields.EndPoint__c.Length)
);
system.debug(debugEvent);
EventBus.publish(debugEvent);
}

logData = '';

}

catch(Exception e) {
//silently ignore
system.debug('Exception occurred: ' + e.getMessage());
}
}

private string obscurePasswords(string input) {
// replace any passwords with *'s in the logged messages
return input.replaceAll('([pP]assword)=([^,\\s]+)\\s?', '$1=******');
}
}
5 changes: 5 additions & 0 deletions force-app/main/default/classes/DebugLog.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>47.0</apiVersion>
<status>Active</status>
</ApexClass>
13 changes: 13 additions & 0 deletions force-app/main/default/classes/DebugLoggerCleanupScheduler.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
debug_log cleanup scheduler
Schedules the job that cleans old records from the system
Marc D Behr
*/

public with sharing class DebugLoggerCleanupScheduler implements Schedulable {
public void execute(SchedulableContext sc) {
database.executebatch(new DebugCleanup());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="urn:metadata.tooling.soap.sforce.com" fqn="DebugLoggerCleanupScheduler">
<apiVersion>47.0</apiVersion>
<status>Active</status>
</ApexClass>
50 changes: 50 additions & 0 deletions force-app/main/default/classes/DebugTest.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
debug_logger test classes
Test the functionality of the main logger
Marc D Behr
*/

@isTest
private class DebugTest {
static list<DebugInfo__c> debugInfoList = new list<DebugInfo__c> ();
static DebugLog debugLogs;
static String realyBigString = '01234567890 error '.repeat(2200); // oversized string
static String debugLogQuery = 'Select Id,DebugData__c from DebugInfo__c';

static testMethod void LoggedPasswordsAreObscured() {
Test.startTest();
debugLogs = new DebugLog('DebugTest');
debugLogs.commitLog('Test0,Password=ABc123,');
Test.stopTest();
debugInfoList = (list<DebugInfo__c>) database.query(debugLogQuery);
system.assertequals(1, debugInfoList.size());
system.assert(debugInfoList[0].DebugData__c.contains('Test0'));
system.assert(debugInfoList[0].DebugData__c.contains('Password=*****'));
}

static testMethod void LongStringsAreTruncated() {
Test.startTest();
debugLogs = new DebugLog('DebugTest');
debugLogs.saveLog('Test');
debugLogs.commitLog(realyBigString);
Test.stopTest();
debugInfoList = (list<DebugInfo__c>) database.query(debugLogQuery);
system.assertequals(1, debugInfoList.size());
system.assertequals('...', debugInfoList[0].DebugData__c.right(3));
system.assertequals(SObjectType.DebugInfo__c.Fields.DebugData__c.Length, debugInfoList[0].DebugData__c.length());
}

static testMethod void LogsAreNotWrittenDuringReadOnlyMode() {
// Set the application read only mode.
Test.setReadOnlyApplicationMode(true);
Test.startTest();
debugLogs = new DebugLog('DebugTest');
debugLogs.commitLog('ReadOnlyModeTest');
Test.stopTest();
debugInfoList = (list<DebugInfo__c>) database.query(debugLogQuery);
system.assertequals(0, debugInfoList.size());
}

}
5 changes: 5 additions & 0 deletions force-app/main/default/classes/DebugTest.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>47.0</apiVersion>
<status>Active</status>
</ApexClass>
Loading

0 comments on commit ab24759

Please sign in to comment.