Logger Error Emails eat up Single Email limit
Package Edition of Nebula Logger
Unlocked Package
Package Version of Nebula Logger
v5
New Bug Summary
In a scenario where many errors are generated, the Email functionality will very quickly eat up the SingleEmail limit for the org. This can have severe consequences to other processes in the org.
I ran into this issue when the following error was being thrown in quick succession: "Logger failed to save 1 LogEntryTag__c records for : Required fields are missing: [Logger Tag]"
Preventing the error above from happening is one thing but we can also improve the way emails are sent to prevent them from having an impact on the SingleEmail limit.
In LoggerEmailUtils.queryApexErrrorRecipients a query is run to retrieve UserId/Email for the target receivers.
for (ApexEmailNotification notification : [SELECT Email, UserId FROM ApexEmailNotification WHERE Email != NULL OR User.IsActive = TRUE]) {
if (notification.UserId != null) {
apexErrrorRecipients.add(notification.UserId);
} else {
apexErrrorRecipients.addAll(notification.Email.split(';'));
}
}
return apexErrrorRecipients;
Then in sendErrorEmail the Messaging.SingleEmailMessage.setToAddresses method is used. The list passed to this method could contain UserIds or Email Addresses. The issue is, regardless of Email or User Id passed to this method it will eat up a SingleEmail limit.
private static void sendErrorEmail(Schema.SObjectType sobjectType, List<String> errorMessages) {
if (errorMessages.isEmpty() == true) {
return;
}
if (CACHED_APEX_ERROR_RECIPIENTS.isEmpty() == true) {
System.debug(LoggingLevel.INFO, 'Logger - no Apex email recipients configured, skipping sending email');
return;
}
Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
message.setToAddresses(CACHED_APEX_ERROR_RECIPIENTS); //Issue is here
message.setSubject(buildSubject(errorMessages));
message.setHtmlBody(buildHtmlBody(sobjectType, errorMessages));
sendEmail(message);
}
To avoid eating up the limit we can leverage the setTargetObjectId method. This only works if we target User Ids but I'd be surprised if many people do not have a User in the ApexEmailNotification setting. If this was the case a forwarding rule could be leveraged to send to non Salesforce users.
The code ends up looking something like this:
List<Messaging.SingleEmailMessage> emailMessages = new List<Messaging.SingleEmailMessage>();
for(ApexEmailNotification apexEmailNotification : [SELECT UserId FROM ApexEmailNotification WHERE User.IsActive = TRUE AND UserId != NULL]){
Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
message.setTargetObjectId(apexEmailNotification.UserId);
message.setSubject('Test subject');
message.setHtmlBody('testico');
message.setSaveAsActivity(false);
emailMessages.add(message);
}
System.debug(Messaging.sendEmail(emailMessages));
If you still wanted to target the 'External Email Addresses' then you could split the sending into two. One for UserIds, one for Email Addresses. This way only each External Email Address eats up a limit. The limit is per Email address, not per invocation of Messaging.sendEmail.
As an FYI to confirm all this I've been running testing against the Limits API to confirm my findings.
Hope this helps out. Would also love to see a Custom Setting for disabling Emails.
Thanks again for all your hard work.
@EoghanMcMullen thanks so much for providing all of this information, this is incredibly helpful! I have another open issue #348 for some other enhancements related to sending emails, so I'm hoping to make a few email-related enhancements in the near future - I'll include your suggestions at part of that work.
I also like your idea of having a custom setting field on LoggerSettings__c to disable sending emails - that would let you configure it at the org/profile/user levels. Another alternative would be to store it as custom metadata record in LoggerParemeter__mdt - with that approach it would be an org-wide change of enabling/disabling emails from Logger. Let me know what you think about the option of using a custom metadata record (instead of LoggerSettings__c), I think both options have pros & cons.
@EoghanMcMullen this is finally fixed in the latest release, v4.11.11