[SalesForce] Error Logging Utility and Controllers

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');
        }           
}

Best Answer

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>