Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track OrgLimits data on Log__c #646

Merged
merged 12 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

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

## Unlocked Package - v4.13.1
## Unlocked Package - v4.13.2

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

`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001MkE3QAK`
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001MkEmQAK`

`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001MkE3QAK`
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001MkEmQAK`

---

Expand Down
8 changes: 8 additions & 0 deletions docs/apex/Configuration/LoggerParameter.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ Indicates if Nebula Logger will send an error email notification if any internal

Indicates if Nebula Logger will store the header values when logging an instance of `System.HttpResponse`. Controlled by the custom metadata record `LoggerParameter.StoreHttpResponseHeaderValues`, or `true` as the default. Regardless of how this parameter is configured, Nebula Logger will still log the header keys of any instance of `System.HttpResponse` that is logged - this parameter only controls if the header values are stored.

#### `STORE_ORGANIZATION_LIMITS` → `Boolean`

Indicates if Nebula Logger will store the organization limits on `Log__c`, retrieved from the class `System.OrgLimits`. Controlled by the custom metadata record `LoggerParameter.StoreOrganizationLimits`, or `true` as the default.

#### `STORE_REST_REQUEST_HEADER_VALUES` → `Boolean`

Indicates if Nebula Logger will store the header values when logging an instance of `System.RestRequest`. Controlled by the custom metadata record `LoggerParameter.StoreRestRequestHeaderValues`, or `true` as the default. Regardless of how this parameter is configured, Nebula Logger will still log the header keys of any instance of `System.RestRequest` that is logged - this parameter only controls if the header values are stored.
Expand All @@ -110,6 +114,10 @@ Indicates if Nebula Logger will store the header values when logging an instance

Indicates if Nebula Logger will store the header values when logging an instance of `System.RestResponse`. Controlled by the custom metadata record `LoggerParameter.StoreRestResponseHeaderValues`, or `true` as the default. Regardless of how this parameter is configured, Nebula Logger will still log the header keys of any instance of `System.RestResponse` that is logged - this parameter only controls if the header values are stored.

#### `STORE_TRANSACTION_LIMITS` → `Boolean`

Indicates if Nebula Logger will store the transaction limits on `LogEntry__c`, retrieved from the class `System.Limits`. Controlled by the custom metadata record `LoggerParameter.StoreTransactionLimits`, or `true` as the default.

#### `SYSTEM_DEBUG_MESSAGE_FORMAT` → `String`

The merge-field syntax to use when calling System.debug(). Controlled by the custom metadata record `LoggerParameter.SystebugMessageFormat`, or `{OriginLocation__c}\n{Message__c}` as the default
Expand Down
10 changes: 10 additions & 0 deletions docs/apex/Log-Management/LogHandler.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ Manages setting fields on `Log__c` before insert & before update

---

### Properties

#### `Max` → `Integer`

#### `Name` → `String`

#### `Used` → `Integer`

---

### Methods

#### `getSObjectType()` → `Schema.SObjectType`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,20 @@ public class LoggerParameter {
private set;
}

/**
* @description Indicates if Nebula Logger will store the organization limits on `Log__c`, retrieved from the class `System.OrgLimits`.
* Controlled by the custom metadata record `LoggerParameter.StoreOrganizationLimits`, or `true` as the default.
*/
public static final Boolean STORE_ORGANIZATION_LIMITS {
get {
if (STORE_ORGANIZATION_LIMITS == null) {
STORE_ORGANIZATION_LIMITS = getBoolean('StoreOrganizationLimits', true);
}
return STORE_ORGANIZATION_LIMITS;
}
private set;
}

/**
* @description Indicates if Nebula Logger will store the header values when logging an instance of `System.RestRequest`.
* Controlled by the custom metadata record `LoggerParameter.StoreRestRequestHeaderValues`, or `true` as the default.
Expand Down Expand Up @@ -397,6 +411,20 @@ public class LoggerParameter {
private set;
}

/**
* @description Indicates if Nebula Logger will store the transaction limits on `LogEntry__c`, retrieved from the class `System.Limits`.
* Controlled by the custom metadata record `LoggerParameter.StoreTransactionLimits`, or `true` as the default.
*/
public static final Boolean STORE_TRANSACTION_LIMITS {
get {
if (STORE_TRANSACTION_LIMITS == null) {
STORE_TRANSACTION_LIMITS = getBoolean('StoreTransactionLimits', true);
}
return STORE_TRANSACTION_LIMITS;
}
private set;
}

/**
* @description The merge-field syntax to use when calling System.debug().
* Controlled by the custom metadata record `LoggerParameter.SystemDebugMessageFormat`, or `{OriginLocation__c}\n{Message__c}` as the default
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<CustomMetadata
xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
>
<label>Store Organization Limits</label>
<protected>false</protected>
<values>
<field>Description__c</field>
<value
xsi:type="xsd:string"
>When set to &apos;true&apos; (default), organization limits are retrieved from the class System.OrgLimits and stored on Log__c.

When set to &apos;false&apos;, organization limits are not retrieved or stored. This helps reduce some CPU time &amp; heap size usage.</value>
</values>
<values>
<field>Value__c</field>
<value xsi:type="xsd:string">true</value>
</values>
</CustomMetadata>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<CustomMetadata
xmlns="http://soap.sforce.com/2006/04/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
>
<label>Store Transaction Limits</label>
<protected>false</protected>
<values>
<field>Description__c</field>
<value
xsi:type="xsd:string"
>When set to &apos;true&apos; (default), transaction limits are retrieved from the class System.Limits and stored on LogEntry__c.

When set to &apos;false&apos;, transaction limits are not retrieved or stored. This helps reduce some CPU time &amp; heap size usage.</value>
</values>
<values>
<field>Value__c</field>
<value xsi:type="xsd:string">true</value>
</values>
</CustomMetadata>
56 changes: 55 additions & 1 deletion nebula-logger/core/main/log-management/classes/LogHandler.cls
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@
* @group Log Management
* @description Manages setting fields on `Log__c` before insert & before update
*/
@SuppressWarnings('PMD.CognitiveComplexity, PMD.CyclomaticComplexity')
@SuppressWarnings('PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.FieldDeclarationsShouldBeAtStart, PMD.PropertyNamingConventions')
public without sharing class LogHandler extends LoggerSObjectHandler {
private static final Map<String, LogStatus__mdt> MOCK_LOG_STATUS_TO_STATUS = new Map<String, LogStatus__mdt>();

private static final List<OrganizationLimit> ORGANIZATION_LIMITS {
get {
if (ORGANIZATION_LIMITS == null) {
ORGANIZATION_LIMITS = loadOrganizationLimits();
}
return ORGANIZATION_LIMITS;
}
set;
}

@TestVisible
private List<Log__c> logs;
@TestVisible
Expand All @@ -31,11 +41,13 @@ public without sharing class LogHandler extends LoggerSObjectHandler {

this.setHasCommentsField();
this.setClosedStatusFields();
this.setOrganizationLimits();
// The log OwnerId field should support being manually changed, so only auto-set it on insert
this.setOwnerId();
this.setParentLog();
// The log retention date field should support being manually changed, so only auto-set it on insert
this.setLogRetentionDetails();
this.setCheckboxFields();
}

protected override void executeBeforeUpdate(Map<Id, SObject> triggerNewMap, Map<Id, SObject> triggerOldMap) {
Expand All @@ -46,6 +58,7 @@ public without sharing class LogHandler extends LoggerSObjectHandler {
this.setClosedStatusFields();
// Priority logic relies on roll-up fields, so only run on update (after log entries are inserted)
this.setPriority();
this.setCheckboxFields();
}

protected override void executeAfterInsert(Map<Id, SObject> triggerNewMap) {
Expand Down Expand Up @@ -82,6 +95,16 @@ public without sharing class LogHandler extends LoggerSObjectHandler {
}
}

private void setOrganizationLimits() {
if (LoggerParameter.STORE_ORGANIZATION_LIMITS == false) {
return;
}

for (Log__c log : this.logs) {
log.OrganizationLimits__c = JSON.serializePretty(ORGANIZATION_LIMITS);
}
}

private void setOwnerId() {
// Loop through the logs and figure out what value has been configured as the default owner (if any)
Map<Id, String> ownerNamesByLoggingUserId = new Map<Id, String>();
Expand Down Expand Up @@ -197,6 +220,12 @@ public without sharing class LogHandler extends LoggerSObjectHandler {
}
}

private void setCheckboxFields() {
for (Log__c log : this.logs) {
log.HasOrganizationLimits__c = log.OrganizationLimits__c != null;
}
}

private void updateUnlinkedChildLogs() {
Map<String, Log__c> transactionIdToPossibleParentLog = new Map<String, Log__c>();
for (Log__c log : this.logs) {
Expand Down Expand Up @@ -262,6 +291,23 @@ public without sharing class LogHandler extends LoggerSObjectHandler {
return logStatusNameToStatus;
}

private static List<OrganizationLimit> loadOrganizationLimits() {
List<OrganizationLimit> organizationLimits = new List<OrganizationLimit>();

List<String> systemOrgLimitNames = new List<String>(System.OrgLimits.getMap().keySet());
systemOrgLimitNames.sort();
for (String systemOrgLimitName : systemOrgLimitNames) {
System.OrgLimit systemOrgLimit = System.OrgLimits.getMap().get(systemOrgLimitName);
OrganizationLimit organizationLimit = new OrganizationLimit();
organizationLimit.Name = systemOrgLimit.getName();
organizationLimit.Used = systemOrgLimit.getValue();
organizationLimit.Max = systemOrgLimit.getLimit();
organizationLimits.add(organizationLimit);
}

return organizationLimits;
}

private static Map<Id, LoggerScenario__c> queryLoggerScenarios(List<Log__c> logs) {
List<Id> loggerScenarioIds = new List<Id>();
for (Log__c log : logs) {
Expand Down Expand Up @@ -303,4 +349,12 @@ public without sharing class LogHandler extends LoggerSObjectHandler {
private static void setMockLogStatus(LogStatus__mdt logStatus) {
MOCK_LOG_STATUS_TO_STATUS.put(logStatus.MasterLabel, logStatus);
}

@SuppressWarnings('PMD.ApexDoc')
@TestVisible
private class OrganizationLimit {
public String Name { get; set; }
public Integer Used { get; set; }
public Integer Max { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,45 @@
<name>Facet-82b9e667-1aa4-42ed-8a09-ac611351ece9</name>
<type>Facet</type>
</flexiPageRegions>
<flexiPageRegions>
<itemInstances>
<componentInstance>
<componentInstanceProperties>
<name>decorate</name>
<value>true</value>
</componentInstanceProperties>
<componentInstanceProperties>
<name>richTextValue</name>
<value
>&lt;p style=&quot;text-align: center;&quot;&gt;&lt;strong&gt;No Organization Limits Data Available for This Log&lt;/strong&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;br&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://ideas.salesforce.com/s/idea/a0B8W00000H6VzyUAF/allow-conditional-tabs-for-lightning-app-builder-tab-component&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Upvote this Idea&lt;/a&gt; to make this experience better&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;</value>
</componentInstanceProperties>
<componentName>flexipage:richText</componentName>
<identifier>flexipage_richText</identifier>
<visibilityRule>
<criteria>
<leftValue>{!Record.HasOrganizationLimits__c}</leftValue>
<operator>EQUAL</operator>
<rightValue>false</rightValue>
</criteria>
</visibilityRule>
</componentInstance>
</itemInstances>
<itemInstances>
<componentInstance>
<componentName>logOrganizationLimits</componentName>
<identifier>c_logOrganizationLimits</identifier>
<visibilityRule>
<criteria>
<leftValue>{!Record.HasOrganizationLimits__c}</leftValue>
<operator>EQUAL</operator>
<rightValue>true</rightValue>
</criteria>
</visibilityRule>
</componentInstance>
</itemInstances>
<name>Facet-fb2a30be-0705-474f-aaae-25d2ed03d33c</name>
<type>Facet</type>
</flexiPageRegions>
<flexiPageRegions>
<itemInstances>
<componentInstance>
Expand Down Expand Up @@ -1271,6 +1310,20 @@
<identifier>detailTab</identifier>
</componentInstance>
</itemInstances>
<itemInstances>
<componentInstance>
<componentInstanceProperties>
<name>body</name>
<value>Facet-fb2a30be-0705-474f-aaae-25d2ed03d33c</value>
</componentInstanceProperties>
<componentInstanceProperties>
<name>title</name>
<value>Organization Limits</value>
</componentInstanceProperties>
<componentName>flexipage:tab</componentName>
<identifier>flexipage_tab</identifier>
</componentInstance>
</itemInstances>
<itemInstances>
<componentInstance>
<componentInstanceProperties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"apiName": "Log__c",
"childRelationships": {},
"fields": {
"OrganizationLimits__c": {
"displayValue": null,
"value": "[{\"Used\":0,\"Name\":\"AnalyticsExternalDataSizeMB\",\"Max\":40960},{\"Used\":0,\"Name\":\"CdpAiInferenceApiMonthlyLimit\",\"Max\":500000000},{\"Used\":0,\"Name\":\"ConcurrentAsyncGetReportInstances\",\"Max\":200},{\"Used\":0,\"Name\":\"ConcurrentEinsteinDataInsightsStoryCreation\",\"Max\":5},{\"Used\":0,\"Name\":\"ConcurrentEinsteinDiscoveryStoryCreation\",\"Max\":2},{\"Used\":0,\"Name\":\"ConcurrentSyncReportRuns\",\"Max\":20},{\"Used\":0,\"Name\":\"DailyAnalyticsDataflowJobExecutions\",\"Max\":60},{\"Used\":0,\"Name\":\"DailyAnalyticsUploadedFilesSizeMB\",\"Max\":51200},{\"Used\":334,\"Name\":\"DailyApiRequests\",\"Max\":5000000},{\"Used\":1,\"Name\":\"DailyAsyncApexExecutions\",\"Max\":250000},{\"Used\":0,\"Name\":\"DailyAsyncApexTests\",\"Max\":1320},{\"Used\":0,\"Name\":\"DailyBulkApiBatches\",\"Max\":15000},{\"Used\":0,\"Name\":\"DailyBulkV2QueryFileStorageMB\",\"Max\":976562},{\"Used\":0,\"Name\":\"DailyBulkV2QueryJobs\",\"Max\":10000},{\"Used\":0,\"Name\":\"DailyDeliveredPlatformEvents\",\"Max\":25000},{\"Used\":0,\"Name\":\"DailyDurableGenericStreamingApiEvents\",\"Max\":200000},{\"Used\":0,\"Name\":\"DailyDurableStreamingApiEvents\",\"Max\":200000},{\"Used\":0,\"Name\":\"DailyEinsteinDataInsightsStoryCreation\",\"Max\":1000},{\"Used\":0,\"Name\":\"DailyEinsteinDiscoveryOptimizationJobRuns\",\"Max\":25},{\"Used\":0,\"Name\":\"DailyEinsteinDiscoveryPredictAPICalls\",\"Max\":50000},{\"Used\":0,\"Name\":\"DailyEinsteinDiscoveryPredictionsByCDC\",\"Max\":500000},{\"Used\":0,\"Name\":\"DailyEinsteinDiscoveryStoryCreation\",\"Max\":100},{\"Used\":0,\"Name\":\"DailyGenericStreamingApiEvents\",\"Max\":10000},{\"Used\":0,\"Name\":\"DailyStandardVolumePlatformEvents\",\"Max\":25000},{\"Used\":0,\"Name\":\"DailyStreamingApiEvents\",\"Max\":200000},{\"Used\":0,\"Name\":\"DailyWorkflowEmails\",\"Max\":1800},{\"Used\":3,\"Name\":\"DataStorageMB\",\"Max\":200},{\"Used\":1,\"Name\":\"DurableStreamingApiConcurrentClients\",\"Max\":1000},{\"Used\":4,\"Name\":\"FileStorageMB\",\"Max\":50},{\"Used\":0,\"Name\":\"HourlyAsyncReportRuns\",\"Max\":1200},{\"Used\":0,\"Name\":\"HourlyDashboardRefreshes\",\"Max\":200},{\"Used\":0,\"Name\":\"HourlyDashboardResults\",\"Max\":5000},{\"Used\":0,\"Name\":\"HourlyDashboardStatuses\",\"Max\":999999999},{\"Used\":0,\"Name\":\"HourlyElevateAsyncReportRuns\",\"Max\":1200},{\"Used\":0,\"Name\":\"HourlyElevateSyncReportRuns\",\"Max\":500},{\"Used\":0,\"Name\":\"HourlyLongTermIdMapping\",\"Max\":100000},{\"Used\":0,\"Name\":\"HourlyManagedContentPublicRequests\",\"Max\":50000},{\"Used\":0,\"Name\":\"HourlyODataCallout\",\"Max\":20000},{\"Used\":0,\"Name\":\"HourlyPublishedPlatformEvents\",\"Max\":250000},{\"Used\":0,\"Name\":\"HourlyPublishedStandardVolumePlatformEvents\",\"Max\":100000},{\"Used\":0,\"Name\":\"HourlyShortTermIdMapping\",\"Max\":100000},{\"Used\":0,\"Name\":\"HourlySyncReportRuns\",\"Max\":500},{\"Used\":0,\"Name\":\"HourlyTimeBasedWorkflow\",\"Max\":1000},{\"Used\":0,\"Name\":\"MassEmail\",\"Max\":10},{\"Used\":0,\"Name\":\"MonthlyEinsteinDiscoveryStoryCreation\",\"Max\":500},{\"Used\":0,\"Name\":\"Package2VersionCreates\",\"Max\":6},{\"Used\":0,\"Name\":\"Package2VersionCreatesWithoutValidation\",\"Max\":500},{\"Used\":8,\"Name\":\"PermissionSets\",\"Max\":1500},{\"Used\":0,\"Name\":\"PrivateConnectOutboundCalloutHourlyLimitMB\",\"Max\":0},{\"Used\":0,\"Name\":\"PublishCallbackUsageInApex\",\"Max\":5242880},{\"Used\":0,\"Name\":\"SingleEmail\",\"Max\":15},{\"Used\":0,\"Name\":\"StreamingApiConcurrentClients\",\"Max\":1000}]"
}
},
"id": "a027d00000IJmfRAAT",
"lastModifiedById": "0057d000008gZI9AAM",
"lastModifiedDate": "2024-03-10T04:51:54.000Z",
"recordTypeId": "012000000000000AAA",
"recordTypeInfo": null,
"systemModstamp": "2024-03-10T04:51:54.000Z"
}
Loading