Waiting for Winter'22 update to use enum.valueOf

This commit is contained in:
Wim Velzeboer
2021-09-15 10:08:01 +01:00
parent 0c259e4e25
commit 898ca0b0f4
61 changed files with 2335 additions and 2 deletions
+9 -1
View File
@@ -34,9 +34,14 @@ Please have read the [contributing guide](./CONTRIBUTING.md) that will through t
You can also have a look at the open items in the [project(s)](./projects), feel free to pick an item from the kanban board! You can also have a look at the open items in the [project(s)](./projects), feel free to pick an item from the kanban board!
## Change Log ----
# Change Log
Some of the changes that have major impact are listed here; Some of the changes that have major impact are listed here;
### September 2021
**Logger functionality**
A replacement of System.Debug that is using Platform events to store log messages in a Custom Object for you to monitor
### August 2021 ### August 2021
**Added criteria based on formula; (1 OR 2) AND 3)** **Added criteria based on formula; (1 OR 2) AND 3)**
@@ -50,6 +55,9 @@ new fflib_Criteria()
Evaluates: Evaluates:
(AccountNumber = '0001' OR AccountNumber = '0001') AND ShippingCountry = 'USA' (AccountNumber = '0001' OR AccountNumber = '0001') AND ShippingCountry = 'USA'
``` ```
---
## Donations ## Donations
We are pleased to announce that the fflib-apex-extensions Project is accepting donations in the privacy-oriented cryptocurrency Monero (XMR) at the following address: We are pleased to announce that the fflib-apex-extensions Project is accepting donations in the privacy-oriented cryptocurrency Monero (XMR) at the following address:
+1 -1
View File
@@ -8,5 +8,5 @@
"name": "fflib-apex-extensions", "name": "fflib-apex-extensions",
"namespace": "", "namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com", "sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "51.0" "sourceApiVersion": "53.0"
} }
@@ -0,0 +1,197 @@
/**
* File Name: fflib_ALogAppenderFilter
* Description: Log Appender that includes a filter for logginglevel
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public abstract with sharing class fflib_ALogAppenderFilter implements fflib_ILogAppender
{
// The default logging level that must be met before log messages are processed
public static final System.LoggingLevel DEFAULT_LEVEL = System.LoggingLevel.ERROR;
// The current used logging level which can be modified during runtime
protected System.LoggingLevel minLogLevel = DEFAULT_LEVEL;
/**
* setLoggingLevel
*
* @description Change the current minimum logging level
* @param logLevel
*/
public void setLoggingLevel(System.LoggingLevel logLevel)
{
this.minLogLevel = logLevel;
}
/**
* isValidLogLevel
*
* @description Compares the Organisation, User and Apex logging level settings
* @param logLevel
*
* @return Returns true if one of the settings is equal or higher than the given loglevel
*/
protected Boolean isValidLogLevel(System.LoggingLevel logLevel)
{
return (
isValidOrganisationLogLevel(logLevel) ||
isValidUserLogLevel(logLevel) ||
isValidApexClassLogLevel(logLevel)
);
}
/**
* isValidLogLevel
*
* @description Compares the given log level valueToCompare against the compareTo
* @param compareTo The log level to compare against
* @param valueToCompare The log level to compare
*
* @return Returns true if the valueToCompare is equal of higher then the compareTo
*/
private Boolean isValidLogLevel(System.LoggingLevel compareTo, System.LoggingLevel valueToCompare)
{
return compareTo != System.LoggingLevel.NONE
&&
valueToCompare != System.LoggingLevel.NONE
&&
valueToCompare.ordinal() >= compareTo.ordinal();
}
/**
* isValidApexClassLogLevel
*
* @description Compare the given loglevel against the current Apex setting stored in this.minLogLevel
* @param logLevel The log level to compare
*
* @return Returns true if the given log level is equal of higher then the Apex setting
*/
private Boolean isValidApexClassLogLevel(System.LoggingLevel logLevel)
{
return isValidLogLevel(this.minLogLevel, logLevel);
}
/**
* isValidOrganisationLogLevel
*
* @description Compare the given loglevel against the Organisation setting stored in a Custom Setting
* @param logLevel The log level to compare
*
* @return Returns true if the given log level is equal of higher then the Organisation setting
*/
private Boolean isValidOrganisationLogLevel(System.LoggingLevel logLevel)
{
return isValidLogLevel(
getDefaultOrganisationLogLevel(),
logLevel
);
}
/**
* isValidUserLogLevel
*
* @description Compare the given loglevel against the User setting stored in a Custom Setting
* @param logLevel The log level to compare
*
* @return Returns true if the given log level is equal of higher then the User setting
*/
private Boolean isValidUserLogLevel(System.LoggingLevel logLevel)
{
return isValidLogLevel(
getUserLogLevel(),
logLevel
);
}
/**
* getDefaultOrganisationLogLevel
*
* @return Returns the default Organisation log level setting stored in fflib_LogLevelFilter__c
*/
private System.LoggingLevel getDefaultOrganisationLogLevel()
{
fflib_LogLevelFilter__c orgSettings = fflib_LogLevelFilter__c.getOrgDefaults();
if (String.isBlank(orgSettings.LogLevel__c))
{
return System.LoggingLevel.NONE;
}
return stringToLoggingLevel(orgSettings.LogLevel__c);
}
/**
* getUserLogLevel
*
* @return Returns the default User log level setting stored in user or profile setting in fflib_LogLevelFilter__c
*/
private System.LoggingLevel getUserLogLevel()
{
fflib_LogLevelFilter__c logLevelFilter = fflib_LogLevelFilter__c.getInstance(UserInfo.getUserId());
if (!hasSettingsValue(logLevelFilter, fflib_LogLevelFilter__c.LogLevel__c))
{
return System.LoggingLevel.NONE;
}
return stringToLoggingLevel(logLevelFilter.LogLevel__c);
}
/**
* hasSettingsValue
*
* @description Validates if the Custom Setting has a value stored in the given field
* @param setting The Custom Setting to validate
* @param sObjectField The field to check
*
* @return Returns true is the given field is set in the Custom Setting
*/
private Boolean hasSettingsValue(fflib_LogLevelFilter__c setting, SObjectField sObjectField)
{
return (
null != setting
&&
setting.getPopulatedFieldsAsMap().containsKey(sObjectField.getDescribe().getName())
);
}
/**
* stringToLoggingLevel
*
* @param logLevelToConvert
*
* @return Returns the Log level for a given string, and System.LoggingLevel.None is an invalid string is given
*/
private System.LoggingLevel stringToLoggingLevel(String logLevelToConvert)
{
String logLevelString = logLevelToConvert.toUpperCase();
for (System.LoggingLevel logLevel : System.LoggingLevel.values())
{
if (logLevelString == logLevel.name())
{
return logLevel;
}
}
return System.LoggingLevel.NONE;
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,34 @@
/**
* File Name: fflib_ILogAppender
* Description: Interface for appending log entries to and endpoint
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public interface fflib_ILogAppender
{
void append(fflib_LogEntry logEntry);
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,36 @@
/**
* File Name: fflib_ILogMessageFormatter
* Description: Interface for formatting log messages
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public interface fflib_ILogMessageFormatter
{
String format(String message);
String format(String message, List<String> args);
String format(Exception e);
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,48 @@
/**
* File Name: fflib_ILogger
* Description: Interface for the Logger utility
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public interface fflib_ILogger
{
void add(String message);
void debug(Exception e);
void debug(String message);
void debug(String message, List<String> args);
void error(Exception e);
void error(String message);
void error(String message, List<String> args);
void info(String message);
void info(String message, List<String> args);
void warning(String message);
void warning(String message, List<String> args);
fflib_ILogger addAppender(fflib_ILogAppender appender);
fflib_ILogger setAppender(fflib_ILogAppender appender);
fflib_ILogger setMessageFormatter(fflib_ILogMessageFormatter formatter);
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,75 @@
/**
* File Name: fflib_LogEntry
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public with sharing class fflib_LogEntry
{
public String className { get; set; }
public String executionContext { get; set; }
public Decimal lineNumber { get; set; }
public Id logEntryId { get; set; }
public System.LoggingLevel logLevel { get; set; }
public String message { get; set; }
public String methodName { get; set; }
public Id userId { get; set; }
public Datetime createdDatetime { get; set; }
public fflib_LogEntry()
{
this(System.LoggingLevel.INFO, null);
}
public fflib_LogEntry(System.LoggingLevel loggingLevel, String message)
{
this.executionContext = Request.getCurrent().getRequestId() + '/' + Request.getCurrent().getQuiddity();
this.logLevel = loggingLevel;
this.message = message;
this.userId = System.UserInfo.getUserId();
}
public fflib_LogEntry(fflib_LogEvent__e logEvent)
{
this.className = logEvent.ClassName__c;
this.executionContext = logEvent.ExecutionContext__c;
this.lineNumber = logEvent.LineNumber__c;
this.logEntryId = logEvent.Id;
this.logLevel = System.LoggingLevel.valueOf(logEvent.LogLevel__c);
}
public fflib_LogEvent__e toPlatformEvent()
{
return new fflib_LogEvent__e(
ClassName__c = this.className,
ExecutionContext__c = this.executionContext,
LineNumber__c = this.lineNumber,
LogLevel__c = this.logLevel.name(),
Message__c = this.message,
MethodName__c = this.methodName
);
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>51.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,107 @@
/**
* File Name: fflib_LogEventAppender
* Description: Log Appender for publishing Platform Events
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public virtual without sharing class fflib_LogEventAppender extends fflib_ALogAppenderFilter
{
/**
* Publish a platform event fflib_LogEvent__e with the given message and logging level if it meets the minimum logging level
*
* @param logEntry The new log entry to append
*/
public virtual void append(fflib_LogEntry logEntry)
{
if (!isValidLogLevel(logEntry.logLevel)) return;
publishEvents(
new List<fflib_LogEvent__e>
{
logEntry.toPlatformEvent()
});
}
/**
* publishEvents
*
* @description Publish the given fflib_LogEvent__e events and report if there are any errors
* @param events The Platform events to publish
*/
protected virtual void publishEvents(List<fflib_LogEvent__e> events)
{
List<Database.SaveResult> results = EventBus.publish(events);
checkAndReportErrors(results);
}
/**
* checkAndReportErrors
*
* @description Iterate over the given Database.SaveResults and report any errors
* @param saveResults
*/
protected void checkAndReportErrors(List<Database.SaveResult> saveResults)
{
// Inspect publishing result for each event
for (Database.SaveResult saveResult : saveResults)
{
if (!saveResult.isSuccess())
{
reportError(saveResult);
}
}
}
/**
* reportError
*
* @description Construct the error message for the failing Database.SaveResult and throws an exception
* @param saveResult
*
* @throws fflib_LogEventAppender.fflib_LogEventAppenderException
*/
protected virtual void reportError(Database.SaveResult saveResult)
{
String errorMessage = 'Error while publishing Log event:';
for (Database.Error databaseError : saveResult.getErrors())
{
errorMessage += String.format(
' {0} - {1}, ',
new List<String>
{
String.valueOf(databaseError.getStatusCode()),
databaseError.getMessage()
}
);
}
throw new fflib_LogEventAppenderException(errorMessage);
}
public class fflib_LogEventAppenderException extends Exception {}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,71 @@
/**
* File Name: fflib_LogEventDomain
* Description: Log Platform Events domain abstract class for the Log Entry events
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public abstract with sharing class fflib_LogEventDomain extends fflib_PlatformEventDomain
{
protected List<fflib_LogEntry> logEntries = new List<fflib_LogEntry>();
/**
* Class constructor
*/
public fflib_LogEventDomain(List<SObject> records)
{
super(records);
recordsToLogEntries();
}
protected void recordsToLogEntries()
{
for (SObject record : Records)
{
this.logEntries.add(
new LogEntry((fflib_LogEvent__e) record)
);
}
}
public class LogEntry
{
public Id createdById;
public Datetime createdDatetime;
public System.LoggingLevel loggingLevel;
public String message;
public LogEntry(fflib_LogEvent__e event)
{
this.loggingLevel = System.LoggingLevel.values().get((Integer) event.LogLevel__c);
this.message = event.message__c;
this.createdById = event.CreatedById;
this.createdDatetime = event.CreatedDate;
}
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,57 @@
/**
* File Name: fflib_LogEventStoreListener
* Description: Log Platform Events listener to store messages into a custom object
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public with sharing class fflib_LogEventStoreListener extends fflib_LogEventDomain
{
/**
* Class constructor
*/
public fflib_LogEventStoreListener(List<SObject> records)
{
super(records);
}
public void run()
{
List<fflib_LogEntry__c> logEntryRecords = new List<fflib_LogEntry__c>();
for (fflib_LogEventDomain.LogEntry logEntry : logEntries)
{
logEntryRecords.add(
new fflib_LogEntry__c(
LogLevel__c = logEntry.loggingLevel.name(),
Message__c = logEntry.message,
User__c = logEntry.createdById
)
);
}
insert logEntryRecords;
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,233 @@
/**
* File Name: fflib_Logger
* Description: Logging utility with standard system.debug log appender and log message formatter
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public with sharing class fflib_Logger implements fflib_ILogger
{
public static final String LF = '\n';
public static final String CR = '\r';
private static final String ORG_NAMESPACE = [SELECT NamespacePrefix FROM Organization].NamespacePrefix;
private static final String CLASSNAME = fflib_Logger.class.getName();
private static final Pattern STACK_LINE =
Pattern.compile('^(?:Class\\.)?([^.]+)\\.?([^\\.\\:]+)?[\\.\\:]?([^\\.\\:]*): line (\\d+), column (\\d+)$');
protected List<fflib_ILogAppender> logAppenders = new List<fflib_ILogAppender>
{
new SystemAppender()
};
protected fflib_ILogMessageFormatter messageFormatter = new LogMessageFormatter();
public void add(String message)
{
add(System.LoggingLevel.INFO, message);
}
public void add(LoggingLevel loggingLevel, Exception e)
{
add(new fflib_LogEntry(loggingLevel, messageFormatter.format(e)));
}
public void add(LoggingLevel loggingLevel, String message)
{
add(new fflib_LogEntry(loggingLevel, messageFormatter.format(message)));
}
public void add(LoggingLevel loggingLevel, String message, List<String> args)
{
add(new fflib_LogEntry(loggingLevel, messageFormatter.format(message, args)));
}
private void add(fflib_LogEntry logEntry)
{
populateStackTrace(logEntry);
addLogEntryToAppenders(logEntry);
}
public fflib_ILogger addAppender(fflib_ILogAppender appender)
{
this.logAppenders.add(appender);
return this;
}
public void debug(Exception e)
{
add(System.LoggingLevel.DEBUG, e);
}
public void debug(String message)
{
add(System.LoggingLevel.DEBUG, message);
}
public void debug(String message, List<String> args)
{
add(System.LoggingLevel.DEBUG, message, args);
}
public void error(Exception e)
{
add(System.LoggingLevel.ERROR, e);
}
public void error(String message)
{
add(System.LoggingLevel.ERROR, message);
}
public void error(String message, List<String> args)
{
add(System.LoggingLevel.ERROR, message, args);
}
public void info(Exception e)
{
add(System.LoggingLevel.INFO, e);
}
public void info(String message)
{
add(System.LoggingLevel.INFO, message);
}
public void info(String message, List<String> args)
{
add(System.LoggingLevel.INFO, message, args);
}
public fflib_ILogger setAppender(fflib_ILogAppender appender)
{
this.logAppenders.clear();
this.logAppenders.add(appender);
return this;
}
public fflib_ILogger setMessageFormatter(fflib_ILogMessageFormatter formatter)
{
this.messageFormatter = formatter;
return this;
}
public void warning(String message)
{
add(System.LoggingLevel.WARN, message);
}
public void warning(String message, List<String> args)
{
add(System.LoggingLevel.WARN, message, args);
}
private void populateStackTrace(fflib_LogEntry logEntry)
{
List<String> stacktrace = new DmlException().getStackTraceString().split('\n');
for (String line : stacktrace)
{
Matcher matcher = STACK_LINE.matcher(line);
if (matcher.find() && !line.startsWith('Class.' + CLASSNAME + '.'))
{
Boolean hasNamespace = String.isNotBlank(matcher.group(3));
if (hasNamespace)
{
logEntry.className =
(matcher.group(1).equals(ORG_NAMESPACE)) ?
matcher.group(2)
:
matcher.group(1) + '.' + matcher.group(2);
logEntry.methodName = formatMethodName(matcher.group(3));
}
else
{
logEntry.className = matcher.group(1);
logEntry.methodName = formatMethodName(matcher.group(2));
}
logEntry.lineNumber = Integer.valueOf(matcher.group(4));
return;
}
}
}
private String formatMethodName(String method)
{
String result = (method == null) ? 'anonymous' : method;
return (result.contains('init')) ? 'ctor' : result;
}
private void addLogEntryToAppenders(fflib_LogEntry logEntry)
{
for (fflib_ILogAppender logAppender : logAppenders)
{
logAppender.append(logEntry);
}
}
public virtual class SystemAppender implements fflib_ILogAppender
{
public void append(fflib_LogEntry logEntry)
{
System.debug(logEntry.className + ' #' + logEntry.lineNumber + ' @'+ logEntry.methodName + ' ' + logEntry.message);
}
}
public virtual class LogMessageFormatter implements fflib_ILogMessageFormatter
{
public virtual String format(String message)
{
return message;
}
public virtual String format(String message, List<String> args)
{
return String.format(message, args);
}
public virtual String format(Exception e)
{
String result = 'Exception: ' + e;
if (null != e.getCause())
{
result += LF + ' Cause: ' + e.getCause();
}
result += LF + ' Stack trace: ' + e.getStackTraceString();
return result;
}
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,41 @@
/**
* File Name: fflib_PlatformEventDomain
* Description: Platform Events Domain abstract class
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public abstract with sharing class fflib_PlatformEventDomain implements ffllib_IPlatformEventDomain
{
protected List<SObject> Records { get; private set; }
public fflib_PlatformEventDomain(List<SObject> records)
{
this.Records = records;
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>46.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,34 @@
/**
* File Name: ffllib_IPlatformEventDomain
* Description: Platform events domain interface
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public interface ffllib_IPlatformEventDomain
{
void run();
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>46.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<Layout xmlns="http://soap.sforce.com/2006/04/metadata">
<excludeButtons>Submit</excludeButtons>
<layoutSections>
<customLabel>false</customLabel>
<detailHeading>false</detailHeading>
<editHeading>true</editHeading>
<label>Information</label>
<layoutColumns>
<layoutItems>
<behavior>Required</behavior>
<field>Name</field>
</layoutItems>
</layoutColumns>
<layoutColumns>
<layoutItems>
<behavior>Edit</behavior>
<field>User__c</field>
</layoutItems>
<layoutItems>
<behavior>Required</behavior>
<field>LogLevel__c</field>
</layoutItems>
</layoutColumns>
<style>TwoColumnsTopToBottom</style>
</layoutSections>
<layoutSections>
<customLabel>true</customLabel>
<detailHeading>true</detailHeading>
<editHeading>true</editHeading>
<label>Log Event Details</label>
<layoutColumns>
<layoutItems>
<behavior>Edit</behavior>
<field>Message__c</field>
</layoutItems>
</layoutColumns>
<style>OneColumn</style>
</layoutSections>
<layoutSections>
<customLabel>false</customLabel>
<detailHeading>false</detailHeading>
<editHeading>true</editHeading>
<label>System Information</label>
<layoutColumns>
<layoutItems>
<behavior>Readonly</behavior>
<field>CreatedById</field>
</layoutItems>
</layoutColumns>
<layoutColumns>
<layoutItems>
<behavior>Readonly</behavior>
<field>LastModifiedById</field>
</layoutItems>
<layoutItems>
<behavior>Edit</behavior>
<field>OwnerId</field>
</layoutItems>
</layoutColumns>
<style>TwoColumnsTopToBottom</style>
</layoutSections>
<layoutSections>
<customLabel>true</customLabel>
<detailHeading>false</detailHeading>
<editHeading>true</editHeading>
<label>Custom Links</label>
<layoutColumns/>
<layoutColumns/>
<layoutColumns/>
<style>CustomLinks</style>
</layoutSections>
<showEmailCheckbox>false</showEmailCheckbox>
<showHighlightsPanel>false</showHighlightsPanel>
<showInteractionLogPanel>false</showInteractionLogPanel>
<showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
<showSubmitAndAttachButton>false</showSubmitAndAttachButton>
<summaryLayout>
<masterLabel>00h3D000000xiYW</masterLabel>
<sizeX>4</sizeX>
<sizeY>0</sizeY>
<summaryLayoutStyle>Default</summaryLayoutStyle>
</summaryLayout>
</Layout>
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<actionOverrides>
<actionName>Accept</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>CancelEdit</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Clone</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Delete</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Edit</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>List</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>New</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>SaveEdit</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>Tab</actionName>
<type>Default</type>
</actionOverrides>
<actionOverrides>
<actionName>View</actionName>
<type>Default</type>
</actionOverrides>
<allowInChatterGroups>false</allowInChatterGroups>
<compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
<deploymentStatus>Deployed</deploymentStatus>
<description>Object to store log entries after they a caught from a Platform event</description>
<enableActivities>false</enableActivities>
<enableBulkApi>true</enableBulkApi>
<enableFeeds>false</enableFeeds>
<enableHistory>false</enableHistory>
<enableReports>true</enableReports>
<enableSearch>true</enableSearch>
<enableSharing>true</enableSharing>
<enableStreamingApi>true</enableStreamingApi>
<label>Log Entry</label>
<nameField>
<label>LogEntry Name</label>
<type>Text</type>
</nameField>
<pluralLabel>LogEntries</pluralLabel>
<searchLayouts/>
<sharingModel>ReadWrite</sharingModel>
<visibility>Public</visibility>
</CustomObject>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>ClassName__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Class Name</label>
<length>80</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>ExecutionContext__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Execution Context</label>
<length>80</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>LineNumber__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Line Number</label>
<precision>18</precision>
<required>false</required>
<scale>0</scale>
<type>Number</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>LogLevel__c</fullName>
<description>The logging level</description>
<externalId>false</externalId>
<label>Log Level</label>
<required>true</required>
<trackTrending>false</trackTrending>
<type>Picklist</type>
<valueSet>
<restricted>true</restricted>
<valueSetDefinition>
<sorted>false</sorted>
<value>
<fullName>ERROR</fullName>
<default>false</default>
<label>ERROR</label>
</value>
<value>
<fullName>WARN</fullName>
<default>false</default>
<label>WARN</label>
</value>
<value>
<fullName>INFO</fullName>
<default>false</default>
<label>INFO</label>
</value>
<value>
<fullName>DEBUG</fullName>
<default>false</default>
<label>DEBUG</label>
</value>
</valueSetDefinition>
</valueSet>
</CustomField>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Message__c</fullName>
<description>The Message of the log entry</description>
<externalId>false</externalId>
<label>Message</label>
<length>32768</length>
<trackTrending>false</trackTrending>
<type>LongTextArea</type>
<visibleLines>5</visibleLines>
</CustomField>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>MethodName__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Method Name</label>
<length>80</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>User__c</fullName>
<description>The User</description>
<deleteConstraint>SetNull</deleteConstraint>
<externalId>false</externalId>
<label>User</label>
<referenceTo>User</referenceTo>
<relationshipName>LogEntries</relationshipName>
<required>false</required>
<trackTrending>false</trackTrending>
<type>Lookup</type>
</CustomField>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<ListView xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>All</fullName>
<filterScope>Everything</filterScope>
<label>All</label>
<columns>NAME</columns>
<columns>CREATED_DATE</columns>
<columns>LogLevel__c</columns>
<columns>User__c</columns>
</ListView>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<ListView xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>All_Debug_Last_7_days</fullName>
<columns>NAME</columns>
<columns>CREATED_DATE</columns>
<columns>User__c</columns>
<columns>Message__c</columns>
<filterScope>Everything</filterScope>
<filters>
<field>LogLevel__c</field>
<operation>equals</operation>
<value>ERROR</value>
</filters>
<filters>
<field>CREATED_DATE</field>
<operation>equals</operation>
<value>LAST_N_DAYS:7</value>
</filters>
<label>All debug entries last 7 days</label>
</ListView>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<ListView xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>All_Error_Last_7_days</fullName>
<columns>NAME</columns>
<columns>CREATED_DATE</columns>
<columns>User__c</columns>
<columns>Message__c</columns>
<filterScope>Mine</filterScope>
<filters>
<field>LogLevel__c</field>
<operation>equals</operation>
<value>ERROR</value>
</filters>
<filters>
<field>CREATED_DATE</field>
<operation>equals</operation>
<value>LAST_N_DAYS:7</value>
</filters>
<label>All error entries last 7 days</label>
</ListView>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<ListView xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>All_Last_day</fullName>
<columns>NAME</columns>
<columns>CREATED_DATE</columns>
<columns>LogLevel__c</columns>
<columns>User__c</columns>
<columns>Message__c</columns>
<filterScope>Everything</filterScope>
<filters>
<field>CREATED_DATE</field>
<operation>equals</operation>
<value>LAST_N_DAYS:1</value>
</filters>
<label>All last day</label>
</ListView>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<ListView xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>All_Warning_Last_7_days</fullName>
<columns>NAME</columns>
<columns>CREATED_DATE</columns>
<columns>LogLevel__c</columns>
<columns>User__c</columns>
<columns>Message__c</columns>
<filterScope>Mine</filterScope>
<filters>
<field>LogLevel__c</field>
<operation>equals</operation>
<value>WARN</value>
</filters>
<filters>
<field>CREATED_DATE</field>
<operation>equals</operation>
<value>LAST_N_DAYS:7</value>
</filters>
<label>All warning entries last 7 days</label>
</ListView>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<deploymentStatus>Deployed</deploymentStatus>
<description>FFLIB Logger Events</description>
<eventType>StandardVolume</eventType>
<label>Log Event</label>
<pluralLabel>Log Events</pluralLabel>
</CustomObject>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>ClassName__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Class Name</label>
<length>80</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>ExecutionContext__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Execution Context</label>
<length>80</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>LineNumber__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Line Number</label>
<precision>18</precision>
<required>false</required>
<scale>0</scale>
<type>Number</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>LogLevel__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Log Level</label>
<length>5</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Message__c</fullName>
<description>The message thrown by the Logger</description>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Message</label>
<length>32768</length>
<type>LongTextArea</type>
<visibleLines>5</visibleLines>
</CustomField>
fflib_LogEventDomain
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>MethodName__c</fullName>
<externalId>false</externalId>
<isFilteringDisabled>false</isFilteringDisabled>
<isNameField>false</isNameField>
<isSortingDisabled>false</isSortingDisabled>
<label>Method Name</label>
<length>80</length>
<required>false</required>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<customSettingsType>Hierarchy</customSettingsType>
<description>Filter to add customised logging for particular users</description>
<enableFeeds>false</enableFeeds>
<label>Log Level Filter</label>
<visibility>Public</visibility>
</CustomObject>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>LogLevel__c</fullName>
<description>The minimum log level to report for the tracked User or Profile</description>
<externalId>false</externalId>
<inlineHelpText>Allowed values none, error, warn, info, debug</inlineHelpText>
<label>Log Level</label>
<length>5</length>
<required>false</required>
<trackTrending>false</trackTrending>
<type>Text</type>
<unique>false</unique>
</CustomField>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<ReportFolder xmlns="http://soap.sforce.com/2006/04/metadata">
<name>Log Entries</name>
</ReportFolder>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Report xmlns="http://soap.sforce.com/2006/04/metadata">
<columns>
<field>CUST_NAME</field>
</columns>
<format>Summary</format>
<groupingsDown>
<field>CUST_CREATED_DATE</field>
<sortOrder>Asc</sortOrder>
</groupingsDown>
<groupingsDown>
<dateGranularity>Day</dateGranularity>
<field>fflib_LogEntry__c.LogLevel__c</field>
<sortOrder>Asc</sortOrder>
</groupingsDown>
<name>LogEntries Report - Day count per level</name>
<params>
<name>co</name>
<value>1</value>
</params>
<reportType>CustomEntity$fflib_LogEntry__c</reportType>
<scope>organization</scope>
<showDetails>false</showDetails>
<timeFrameFilter>
<dateColumn>CUST_CREATED_DATE</dateColumn>
<interval>INTERVAL_CUSTOM</interval>
</timeFrameFilter>
</Report>
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<Report xmlns="http://soap.sforce.com/2006/04/metadata">
<columns>
<field>CUST_NAME</field>
</columns>
<columns>
<field>CUST_CREATED_DATE</field>
</columns>
<columns>
<field>fflib_LogEntry__c.LogLevel__c</field>
</columns>
<columns>
<field>fflib_LogEntry__c.User__c</field>
</columns>
<columns>
<field>fflib_LogEntry__c.Message__c</field>
</columns>
<description>Shows all ERROR and WARNING log entires from yesterday</description>
<filter>
<criteriaItems>
<column>CUST_CREATED_DATE</column>
<isUnlocked>true</isUnlocked>
<operator>equals</operator>
<value>YESTERDAY</value>
</criteriaItems>
<criteriaItems>
<column>fflib_LogEntry__c.LogLevel__c</column>
<isUnlocked>true</isUnlocked>
<operator>equals</operator>
<value>ERROR,WARN</value>
</criteriaItems>
</filter>
<format>Tabular</format>
<name>LogEntries Report - Yesterday</name>
<params>
<name>co</name>
<value>1</value>
</params>
<reportType>CustomEntity$fflib_LogEntry__c</reportType>
<scope>organization</scope>
<showDetails>true</showDetails>
<timeFrameFilter>
<dateColumn>CUST_CREATED_DATE</dateColumn>
<interval>INTERVAL_CUSTOM</interval>
</timeFrameFilter>
</Report>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
<customObject>true</customObject>
<description>Tab to show Log Entries</description>
<motif>Custom34: Insect</motif>
</CustomTab>
@@ -0,0 +1,10 @@
/**
* File Name: sflib_LogEventTrigger
* Description: Tigger to store Log events into a custom object
* Copyright (c) 2019 Johnson & Johnson
* @author: architect ir. Wilhelmus G.J. Velzeboer | wvelzebo@its.jnj.com
*/
trigger fflib_LogEventTrigger on fflib_LogEvent__e (after insert)
{
new fflib_LogEventStoreListener(Trigger.new).run();
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexTrigger xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexTrigger>
@@ -0,0 +1,228 @@
/**
* File Name: fflib_LogAppenderFilterTest
* Description: Unit test class for fflib_LogAppenderFilter.cls
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@IsTest
private class fflib_ALogAppenderFilterTest
{
private static final String TEST_USER_PROFILE = 'Standard User';
@IsTest
static void itShouldValidateErrorLevelWithDefaultSetToError()
{
// GIVEN - We create an AppenderFilter instance and set the filter to ERRROR
FilteredAppender appender = new FilteredAppender();
// WHEN - we check if ERROR is valid
Boolean isValid = appender.checkValidLogLevel(System.LoggingLevel.ERROR);
// THEN - It should be valid
System.assert(isValid);
}
@IsTest
static void itShouldNotValidateErrorLevelWithDefaultSetToNone()
{
// GIVEN - We create an AppenderFilter instance and set the filter to NONE
FilteredAppender appender = new FilteredAppender();
appender.setLoggingLevel(System.LoggingLevel.NONE);
// WHEN - we check if ERROR is valid
Boolean isValid = appender.checkValidLogLevel(System.LoggingLevel.ERROR);
// THEN - It should not be valid
System.assert(!isValid);
}
@IsTest
static void itShouldNotValidateWarningLevelWithDefaultSetToError()
{
// GIVEN - We create an AppenderFilter instance and set the filter to NONE
FilteredAppender appender = new FilteredAppender();
// WHEN - we check if ERROR is valid
Boolean isValid = appender.checkValidLogLevel(System.LoggingLevel.WARN);
// THEN - It should not be valid
System.assert(!isValid);
}
/**
* THIS UNIT TEST IS DISABLED BY DEFAULT SINCE THERE IS NO KNOWN STANDARD USER FACTORY METHOD FOR THE TARGET ORG
*/
@IsTest
static void itShouldValidateWarningLevelWithDefaultSetToErrorForSpecifiedUser()
{
// Given - A default AppenderFilter instance and a specific user config set to Warning
FilteredAppender appender = new FilteredAppender();
User testUser = generateUser();
insert testUser;
insert new fflib_LogLevelFilter__c(
SetupOwnerId = testUser.Id,
LogLevel__c = System.LoggingLevel.WARN.name()
);
// When - we check if WARNING is valid
Boolean isValid = false;
System.runAs(testUser)
{
isValid = appender.checkValidLogLevel(System.LoggingLevel.WARN);
}
// Then - It should be valid
System.assert(isValid);
}
/**
* THIS UNIT TEST IS DISABLED BY DEFAULT SINCE THERE IS NO KNOWN STANDARD USER FACTORY METHOD FOR THE TARGET ORG
*/
@IsTest
static void itShouldValidateWarningLevelWithDefaultSetToErrorForSpecifiedProfile()
{
// Given - A default AppenderFilter instance and a specific user config set to Warning
FilteredAppender appender = new FilteredAppender();
User testUser = generateUser();
insert testUser;
insert new fflib_LogLevelFilter__c(
SetupOwnerId = testUser.ProfileId,
LogLevel__c = System.LoggingLevel.WARN.name()
);
// When - we check if WARNING is valid
Boolean isValid = false;
System.runAs(testUser)
{
isValid = appender.checkValidLogLevel(System.LoggingLevel.WARN);
}
// Then - It should be valid
System.assert(isValid);
}
@IsTest
static void itShouldValidateWarningLevelWithDefaultOrganisationLogLevelValueSetToWarning()
{
// GIVEN - A default AppenderFilter instance with log level set to ERROR
// and a custom setting at Organisation Level to WARNING
FilteredAppender appender = new FilteredAppender();
fflib_LogLevelFilter__c settings = fflib_LogLevelFilter__c.getOrgDefaults();
settings.LogLevel__c = 'WARN';
upsert settings fflib_LogLevelFilter__c.Id;
// WHEN - We check if WARNING is valid
Boolean isValid = appender.checkValidLogLevel(System.LoggingLevel.WARN);
// Then - It should be valid
System.assert(isValid);
}
@IsTest
static void itShouldValidateWarningLevelWithDefaultOrganisationLogLevelValueSetToLowerCaseWarning()
{
// GIVEN - A default AppenderFilter instance with log level set to ERROR
// and a custom setting at Organisation Level to WARNING
FilteredAppender appender = new FilteredAppender();
fflib_LogLevelFilter__c settings = fflib_LogLevelFilter__c.getOrgDefaults();
settings.LogLevel__c = 'warn';
upsert settings fflib_LogLevelFilter__c.Id;
// WHEN - We check if WARNING is valid
Boolean isValid = appender.checkValidLogLevel(System.LoggingLevel.WARN);
// Then - It should be valid
System.assert(isValid);
}
@IsTest
static void itShouldNotFailWithIncorrectValueSetForDefaultOrganisationLogLevel()
{
// GIVEN - A default AppenderFilter instance with log level set to ERROR
// and a custom setting at Organisation Level to FAIL
FilteredAppender appender = new FilteredAppender();
fflib_LogLevelFilter__c settings = fflib_LogLevelFilter__c.getOrgDefaults();
settings.LogLevel__c = 'Fail';
upsert settings fflib_LogLevelFilter__c.Id;
// WHEN - We check if WARNING is valid
Boolean isValid = appender.checkValidLogLevel(System.LoggingLevel.WARN);
// Then - It should be valid
System.assert(!isValid);
}
@IsTest
static void itShouldNotLogWhenSetToNone()
{
// GIVEN - A default AppenderFilter instance with log level set to NONE
FilteredAppender appender = new FilteredAppender();
appender.setLoggingLevel(System.LoggingLevel.NONE);
// WHEN - We check if WARNING is valid
Boolean isValid = appender.checkValidLogLevel(System.LoggingLevel.WARN);
// Then - It should not be valid
System.assert(!isValid);
}
private static User generateUser()
{
Profile profile = [SELECT Id FROM Profile WHERE Name = :TEST_USER_PROFILE];
return new User(
Alias = 'jnjfflib',
Email = 'TestUser@example.com',
EmailEncodingKey = 'UTF-8',
FirstName = 'Test',
LanguageLocaleKey = 'en_US',
LastName = 'User',
LocaleSidKey = 'en_US',
ProfileId = profile.Id,
TimeZoneSidKey = 'America/Los_Angeles',
Username = 'TestUser@example.com.jnjfflib'
);
}
private class FilteredAppender extends fflib_ALogAppenderFilter
{
public Boolean checkValidLogLevel(System.LoggingLevel logLevel)
{
return isValidLogLevel(logLevel);
}
public void append(fflib_LogEntry logEntry) {}
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,103 @@
/**
* File Name: fflib_LogEventAppenderTest
* Description: Unit test class for fflib_LogEventAppender.cls
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@IsTest
private class fflib_LogEventAppenderTest
{
private static final String TEST_ERROR_MESSAGE = 'My Error Message';
@IsTest
static void itShouldThrowEventOnAppendedLogEntry()
{
// GIVEN - An instance of the LogEventAppender
fflib_LogEventAppender eventAppender = new fflib_LogEventAppender();
// WHEN - We append an entry
Boolean caughtException = false;
try
{
eventAppender.append(System.LoggingLevel.ERROR, TEST_ERROR_MESSAGE);
}
catch (Exception e)
{
caughtException = true;
}
// THEN - An Log Event should be thrown
System.assert(!caughtException);
}
static testMethod void itShouldNotAppendInfoMessageWhileDefaultLevelIsSetToError()
{
// GIVEN - And appender with a default level set to Error
fflib_LogEventAppender eventAppender = new fflib_LogEventAppender();
// WHEN - We append an Info Entry
eventAppender.append(System.LoggingLevel.INFO, TEST_ERROR_MESSAGE);
// THEN - It should be ignored
System.assertEquals(0, Limits.getDmlStatements(), 'Oops, the log entry should have been ignored');
}
static testMethod void itShouldAppendInfoMessageWhenTheApexLevelIsSetToInfo()
{
// GIVEN - And appender with a Apex level set to Info
fflib_LogEventAppender eventAppender = new fflib_LogEventAppender();
eventAppender.setLoggingLevel(System.LoggingLevel.INFO);
// WHEN - We append an Info Entry
System.Test.startTest();
eventAppender.append(System.LoggingLevel.INFO, TEST_ERROR_MESSAGE);
System.Test.stopTest();
// THEN - It should be ignored
System.assertEquals(1, [SELECT COUNT() FROM fflib_LogEntry__c]);
}
static testMethod void itShouldAppendInfoMessageWhenTheOrgSettingsLevelIsSetToInfo()
{
// GIVEN - And appender with a Org level set to Info
fflib_LogEventAppender eventAppender = new fflib_LogEventAppender();
fflib_LogLevelFilter__c orgSettings = fflib_LogLevelFilter__c.getOrgDefaults();
System.debug(orgSettings);
orgSettings.LogLevel__c = 'INFO';
insert orgSettings;
// WHEN - We append an Info Entry
System.Test.startTest();
eventAppender.append(System.LoggingLevel.INFO, TEST_ERROR_MESSAGE);
System.Test.stopTest();
// THEN - It should be ignored
System.assertEquals(1, [SELECT COUNT() FROM fflib_LogEntry__c]);
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,103 @@
/**
* File Name: fflib_LogEventStoreListenerTest
* Description: Unit tests for fflib_LogEventStoreListener.cls
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@IsTest
private class fflib_LogEventStoreListenerTest
{
private static final String ERROR_MESSAGE = 'My Error Message';
private static final String TEST_USER_PROFILE = 'Standard User';
@IsTest
static void itShouldStoreRecordOnLogEvent()
{
// GIVEN - A Log Event
fflib_LogEvent__e logEvent = generateLoggingLevelEvent(System.LoggingLevel.ERROR, ERROR_MESSAGE);
// WHEN - The log event is published
System.Test.startTest();
EventBus.publish(logEvent);
System.Test.stopTest();
// THEN - a record should be created in the Log Event Store
System.assertEquals(1, [SELECT COUNT() FROM fflib_LogEntry__c]);
}
/**
* THIS UNIT TEST IS DISABLED BY DEFAULT SINCE THERE IS NO KNOWN STANDARD USER FACTORY METHOD FOR THE TARGET ORG
*/
@IsTest
static void itShouldStoreRecordOnLogEventWithUserDetails()
{
// GIVEN - A Log Event thrown by a specific user
fflib_LogEvent__e logEvent = generateLoggingLevelEvent(System.LoggingLevel.ERROR, ERROR_MESSAGE);
User testUser = generateUser();
insert testUser;
// WHEN - The log event is published
System.Test.startTest();
System.runAs(testUser)
{
EventBus.publish(logEvent);
}
System.Test.stopTest();
// THEN - a record should be created in the Log Event Store
List<fflib_LogEntry__c> logEntries = [SELECT Id, LogLevel__c, Message__c, User__c FROM fflib_LogEntry__c];
System.assert(logEntries.size() == 1);
System.assertEquals(testUser.Id, logEntries.get(0).User__c, 'Log Entry stored with wrong CreatedById');
}
private static fflib_LogEvent__e generateLoggingLevelEvent(LoggingLevel loggingLevel, String message)
{
return new fflib_LogEvent__e(
LogLevel__c = loggingLevel.ordinal(),
message__c = message
);
}
private static User generateUser()
{
Profile profile = [SELECT Id FROM Profile WHERE Name = :TEST_USER_PROFILE];
return new User(
Alias = 'jnjfflib',
Email = 'TestUser@example.com',
EmailEncodingKey = 'UTF-8',
FirstName = 'Test',
LanguageLocaleKey = 'en_US',
LastName = 'User',
LocaleSidKey = 'en_US',
ProfileId = profile.Id,
TimeZoneSidKey = 'America/Los_Angeles',
Username = 'TestUser@example.com.jnjfflib'
);
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,326 @@
/**
* File Name: fflib_LoggerTest
* Description: Unit test for fflib_Logger.cls
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@IsTest
private class fflib_LoggerTest
{
/*
private static final List<String> ARGUMENTS = new List<String> {'A', 'B', 'C'};
private static final String ERROR_MESSAGE = 'Some Error';
private static final String ERROR_MESSAGE_WITH_ARGS = 'Some Error {0}, {1}, {2}';
private static final String ERROR_MESSAGE_FORMATTED_WITH_EXCEPTION = String.format(
'Exception: {0} {1}\n Stack trace: {2}',
new List<String>
{
'LogTestMockException:[]:',
ERROR_MESSAGE,
'()'
}
);
private static final String ERROR_MESSAGE_FORMATTED_WITH_ARGS =
String.format(
ERROR_MESSAGE_WITH_ARGS,
ARGUMENTS
);
private static final LogAppenderMock APPENDER_MOCK = new LogAppenderMock();
private static final SecondLogAppenderMock APPENDER_MOCK_B = new SecondLogAppenderMock();
@IsTest
static void itShouldAppendAMessage()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.add(ERROR_MESSAGE);
// THEN - It should be appended to the logAppender
System.assert(APPENDER_MOCK.getAppendedMessages(System.LoggingLevel.INFO).contains(ERROR_MESSAGE));
}
@IsTest
static void itShouldAppendDebugMessage()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.debug(ERROR_MESSAGE);
// THEN - It should be appended to the logAppender
System.assert(
APPENDER_MOCK
.getAppendedMessages(System.LoggingLevel.DEBUG)
.contains(ERROR_MESSAGE)
);
}
/**
* todo : For some reason this method fails when run by Jenkins deployment user during a build, need to investigate why and re-enable it
@IsTest
static void itShouldAppendDebugException()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
Exception e = new LogTestMockException(ERROR_MESSAGE);
// WHEN - When the error is logged
log.debug(e);
// THEN - It should be appended to the logAppender
System.assert(
APPENDER_MOCK
.getAppendedMessages(System.LoggingLevel.DEBUG)
.contains(ERROR_MESSAGE_FORMATTED_WITH_EXCEPTION)
);
}
*/
@IsTest
static void itShouldAppendDebugMessageWithArguments()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.debug(ERROR_MESSAGE_WITH_ARGS, ARGUMENTS);
// THEN - It should be appended to the logAppender
System.assert(
APPENDER_MOCK
.getAppendedMessages(System.LoggingLevel.DEBUG)
.contains(ERROR_MESSAGE_FORMATTED_WITH_ARGS)
);
}
@IsTest
static void itShouldAppendErrorMessage()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.error(ERROR_MESSAGE);
// THEN - It should be appended to the logAppender
System.assert(APPENDER_MOCK.getAppendedMessages(System.LoggingLevel.ERROR).contains(ERROR_MESSAGE));
}
@IsTest
static void itShouldAppendErrorMessageWithArguments()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.error(ERROR_MESSAGE_WITH_ARGS, ARGUMENTS);
// THEN - It should be appended to the logAppender
System.assert(
APPENDER_MOCK
.getAppendedMessages(System.LoggingLevel.ERROR)
.contains(ERROR_MESSAGE_FORMATTED_WITH_ARGS)
);
}
@IsTest
static void itShouldAppendInfoMessage()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.info(ERROR_MESSAGE);
// THEN - It should be appended to the logAppender
System.assert(APPENDER_MOCK.getAppendedMessages(System.LoggingLevel.INFO).contains(ERROR_MESSAGE));
}
@IsTest
static void itShouldAppendInfoMessageWithArguments()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.info(ERROR_MESSAGE_WITH_ARGS, ARGUMENTS);
// THEN - It should be appended to the logAppender
System.assert(
APPENDER_MOCK
.getAppendedMessages(System.LoggingLevel.INFO)
.contains(ERROR_MESSAGE_FORMATTED_WITH_ARGS)
);
}
@IsTest
static void itShouldAppendWarningMessage()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.warning(ERROR_MESSAGE);
// THEN - It should be appended to the logAppender
System.assert(APPENDER_MOCK.getAppendedMessages(System.LoggingLevel.WARN).contains(ERROR_MESSAGE));
}
@IsTest
static void itShouldAppendWarningMessageWithArguments()
{
// GIVEN - An error message and a configured logger
fflib_ILogger log = generatedLoggerWithMockAppender();
// WHEN - When the error is logged
log.warning(ERROR_MESSAGE_WITH_ARGS, ARGUMENTS);
// THEN - It should be appended to the logAppender
System.assert(
APPENDER_MOCK
.getAppendedMessages(System.LoggingLevel.WARN)
.contains(ERROR_MESSAGE_FORMATTED_WITH_ARGS)
);
}
@IsTest
static void itShouldHandleTwoAppenders()
{
// GIVEN an error message and a logger with two appenders configured
fflib_ILogger log = new fflib_Logger();
log.addAppender(APPENDER_MOCK);
log.addAppender(APPENDER_MOCK_B);
log.setMessageFormatter(new fflib_Logger.LogMessageFormatter());
// WHEN - When the error is logged
log.error(ERROR_MESSAGE);
// THEN - It Should be added to both appenders
System.assert(
APPENDER_MOCK
.getAppendedMessages(System.LoggingLevel.ERROR)
.contains(ERROR_MESSAGE)
);
System.assert(
APPENDER_MOCK_B
.getAppendedMessages(System.LoggingLevel.ERROR)
.contains(ERROR_MESSAGE)
);
}
private static fflib_ILogger generatedLoggerWithMockAppender()
{
fflib_ILogger result = new fflib_Logger();
result.setAppender(APPENDER_MOCK);
result.setMessageFormatter(new fflib_Logger.LogMessageFormatter());
return result;
}
private class LogAppenderMock implements fflib_ILogAppender
{
private Map<System.LoggingLevel, List<String>> appendedMessages =
new Map<System.LoggingLevel, List<String>>();
public void append(String message)
{
if (!this.appendedMessages.containsKey(System.LoggingLevel.INFO))
{
this.appendedMessages.put(System.LoggingLevel.INFO, new List<String>());
}
appendedMessages.get(System.LoggingLevel.INFO).add(message);
}
public void append(LoggingLevel loggingLevel, String message)
{
if (!this.appendedMessages.containsKey(loggingLevel))
{
this.appendedMessages.put(loggingLevel, new List<String>());
}
appendedMessages.get(loggingLevel).add(message);
}
public List<String> getAppendedMessages(System.LoggingLevel loggingLevel)
{
if (this.appendedMessages.containsKey(loggingLevel))
{
return this.appendedMessages.get(loggingLevel);
}
else
{
return new List<String>();
}
}
}
private class SecondLogAppenderMock implements fflib_ILogAppender
{
private Map<System.LoggingLevel, List<String>> appendedMessages =
new Map<System.LoggingLevel, List<String>>();
public void append(String message)
{
if (!this.appendedMessages.containsKey(System.LoggingLevel.INFO))
{
this.appendedMessages.put(System.LoggingLevel.INFO, new List<String>());
}
appendedMessages.get(System.LoggingLevel.INFO).add(message);
}
public void append(LoggingLevel loggingLevel, String message)
{
if (!this.appendedMessages.containsKey(loggingLevel))
{
this.appendedMessages.put(loggingLevel, new List<String>());
}
appendedMessages.get(loggingLevel).add(message);
}
public List<String> getAppendedMessages(System.LoggingLevel loggingLevel)
{
if (this.appendedMessages.containsKey(loggingLevel))
{
return this.appendedMessages.get(loggingLevel);
}
else
{
return new List<String>();
}
}
}
private class LogTestMockException extends Exception
{
}
*/
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>41.0</apiVersion>
<status>Active</status>
</ApexClass>