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

Logger Error Emails eat up Single Email limit #369

Closed
EoghanMcMullen opened this issue Sep 19, 2022 · 2 comments · Fixed by #570
Closed

Logger Error Emails eat up Single Email limit #369

EoghanMcMullen opened this issue Sep 19, 2022 · 2 comments · Fixed by #570
Assignees
Labels
Feature: Email Notifications Layer: Log Management Items related to the custom objects & Logger Console app Layer: Logger Engine Items related to the core logging engine Type: Bug Something isn't working

Comments

@EoghanMcMullen
Copy link

EoghanMcMullen commented Sep 19, 2022

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 EoghanMcMullen added the Type: Bug Something isn't working label Sep 19, 2022
@jongpie
Copy link
Owner

jongpie commented Sep 22, 2022

@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.

@jongpie jongpie added the Layer: Logger Engine Items related to the core logging engine label Sep 22, 2022
@jongpie jongpie self-assigned this Sep 28, 2022
jongpie added a commit that referenced this issue Oct 5, 2023
…argetObjectId() when sending failure emails to internal users
jongpie added a commit that referenced this issue Oct 12, 2023
…argetObjectId() when sending failure emails to internal users
jongpie added a commit that referenced this issue Oct 13, 2023
…argetObjectId() when sending failure emails to internal users
jongpie added a commit that referenced this issue Oct 15, 2023
* Fixed #369 by using instance method Messaging.SingleEmailMessage.setTargetObjectId() when sending failure emails to internal users

* Updated build.yml to delete all profiles in the unsorted directory before generating package versions, because they sometimes upset the sf cli ᕕ( ᐛ )ᕗ
@jongpie
Copy link
Owner

jongpie commented Oct 15, 2023

@EoghanMcMullen this is finally fixed in the latest release, v4.11.11

@jongpie jongpie added the Layer: Log Management Items related to the custom objects & Logger Console app label Oct 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature: Email Notifications Layer: Log Management Items related to the custom objects & Logger Console app Layer: Logger Engine Items related to the core logging engine Type: Bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants