7

I am trying to build a generic logging utility that will store exceptions in a custom object, this in itself is fine. What I am struggling with is using it in Controllers or Getters/Setters due to any of the following:

Trying to use DML:

dml currently not allowed

Trying to use @future:

@future call currently not allowed

Trying to use Queable:

System.enqueueJob is not allowed in this context.

My main concern is public facing pages where we may never find out there is a problem, I want to be proactive about these errors. I feel like there isn't a way to do this in a nice neat one line way...any other options I've missed? I'd prefer to not use an action method.

Edit

Here's a very santized example for the future method approach. For the others just replace the @future with queuable or remove it...

//Utility class/method
@future
public static void createExceptionRecord(String className, String errorMessage){
        //Create record
} 

//VF Controller
public exampleController(){
        String myId = ApexPages.currentPage().getParameters().get('myid');
        try{
            customObject__c = [SELECT Id FROM customObject__c WHERE Id = :myId]
        }
        catch(System.QueryException ex){
            utilityClass.createExceptionRecord('myCtr', 'woe is me. A query exception has occurred');
        }           
}
3
  • Is that first error from an <apex:component>? Or a constructor?
    – Adrian Larson
    Dec 21, 2016 at 17:08
  • No just a VF page, it does have a component on it but after your message tried removing it. Still got the same message.
    – Girbot
    Dec 21, 2016 at 17:10
  • If DML currently isn't allowed (at all), it also may not be allowed for your logging utility. It all depends on the context for which it's not allowed since the call would be coming from the class that's generating that error or exception.
    – crmprogdev
    Dec 21, 2016 at 17:14

2 Answers 2

6

If you're looking for a mechanism that will allow you to save errors from within a constructor, you will have to enqueue them, and then later flush the queue. You might consider an abstract class that will define the behavior you need, along the lines of:

public abstract class LoggingController
{
    public List<Error_Log__c> errors { get; private set; }
    public LoggingController() { errors = new List<Error_Log__c>(); }
    public void logErrors()
    {
        try
        {
            insert errors;
        }
        catch (DmlException d)
        {
            ApexPages.addMessages(d);
        }
    }
}

Then your actual controller/extension will just extend this class.

public with sharing class MyController extends LoggingController
{
    public LoggingController()
    {
        super();
        try
        {
            // doStuff
        }
        catch (QueryException e)
        {
            errors.add(new Error_Log__c(/*data*/));
        }
    }
}

And the key to the magic is, your page can then log the errors as an action.

<apex:page controller="MyController" action="{!logErrors}">
    <!--markup-->
</apex:page>
4
  • I like it but I was hoping to avoid using an action but it doesn't look like there is another way at the moment.
    – Girbot
    Dec 21, 2016 at 17:31
  • @Girbot Yeah if you want to save data on page load, that's pretty much your only option. Or you could hook up some Javascript to basically do the same thing.
    – Adrian Larson
    Dec 21, 2016 at 18:13
  • just thinking here ... couldn't the constructor, if it gets an error, redirect to another page, pass the error message in the URL string, and that page, with a page action method, could then execute DML of the error message before rendering the page with an apex pages message?
    – cropredy
    Dec 22, 2016 at 2:53
  • @cropredy Sounds like a pretty similar idea that would also work.
    – Adrian Larson
    Dec 22, 2016 at 5:11
4

If you are going to do this, do capture all the available information from the exception including the stacktrace via getStackTraceString.

I also suggest that you should only add this sort of logic sparingly for code that you expect to go wrong: otherwise you are adding a lot of clutter to your code and that clutter in itself can cause or hide defects. The platform's default behaviour of showing the error on a white screen is pretty effective at alerting users who you can then contact you via a public support email/number. Logging the error into an error log table hides the error for the end user and perhaps leaves them confused as to what is happening. And if many (perhaps unimportant) errors are logged your willpower to identify the important ones and address them may run out.

Unfortunately as you probably know you can't log and then re-throw the error to also alert the end user because the transaction will be automatically rolled back so discarding your log. (But as Adrian and Girbot point out in the comments below you can use ApexPages.addMessages.)

3
  • 1
    As for the last bit, you can log the message and still surface it to the UI using ApexPages.addMessages.
    – Adrian Larson
    Dec 21, 2016 at 18:49
  • Just to echo @AdrianLarson I have been using ApexPages.addMessages before the logging but lots of valid points in there.
    – Girbot
    Dec 21, 2016 at 18:51
  • @AdrianLarson Good point. Loses you the dramatic white screen, but gives you the chance to insert extra advice and who to contact etc.
    – Keith C
    Dec 21, 2016 at 18:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .