[SalesForce] throw an exception in apex and still log the caught exception

I'm really wrestling with the best error handling strategy. My goal is to handle all exceptions that I can identify, log them to a table in the database, and surface a helpful message about what happened to the user.

The problem

I am using an auth provider for SSO OAuth from one Salesforce instance to another. It's generally working fine but the registration handler has some complex logic, including a callout to another org for verification of some data. Sometimes the callout fails for a few different reasons and there are a handful of other infrequent but notable registration errors that we wish to log. The problem is that without throwing a custom exception, there is no way to communicate to the error handler page for the auth provider what happened, not that I see at least. Handled errors fail silently. When I throw an exception in my reg handler, whatever logging I had attempted gets rolled back.

What I've tried

I tried logging and then throwing the exception in the catch block in a bunch of places in my registration handler class. As I mentioned, this was a no go since the logging got rolled back.

I've also tried not throwing the exception but then some of my logic in the reg handler gets subverted/ignored. I have yet to try rolling back to a savepoint in my catch blocks following known and handled errors, and this may solve this issue, although I wouldn't be able to surface anything helpful to the user at this point.

Questions

When would I use the Finally block? Can it be used for any purpose when throwing my own exception or would whatever logging I did get rolled back.

Is there any way to force custom logging and save to the database while also throwing an exception?

What other strategy should I try here?

Finally, how do you think this should work? It seems like there is some logging path that you should be able to continue down in apex, even after an exception and a rollback.

Best Answer

First please take a look at this answer for a partial solution / approach, Can I prevent an insert to be rolled back even if my class (later) runs into an exception?.

Basically as per the Apex Developers guide the final commit transaction management is baked into Apex, meaning it will always rollback if you let the platform handle your exceptions, if you don't you have to manage the rollback scope yourself or accept any DML that has been issued be committed.

If the entire request completes successfully, all changes are committed to the database. For example, suppose a Visualforce page called an Apex controller, which in turn called an additional Apex class. Only when all the Apex code has finished running and the Visualforce page has finished running, are the changes committed to the database. If the request does not complete successfully, all database changes are rolled back

The only way to get control of this is to use the try/catch/finally semantics, along with Savepoints, and critically not throwing any exception (note that as per the answer above some system exceptions cannot be caught sadly). Since your implementing the Auth.RegistrationHandler interface your stuck with its method signature and thus cannot code any alternative way to pass errors back, not does there appear to be any support for this on the interface itself.

Passing a null user back from createUser. What happens if you using try/catch/finally with a logging approach as above and then null back? I expect the system will handle this and present some general error message. Of course in this case your more detail errors will have been logged, since technically the call completed successfully. You would of course have to educate your users / admins to check your own log in this case. Given the frequency of the issues, perhaps this is tolerable? As regards the updateUser method, i don't see any way to infer errors with a successful execution of the method.

Summary and System.debug? Sorry this is not the answer you wanted, but i believe this is the situation. Your not the first to want to implement such a logging approach as you can see. Ultimately its a matter of how far your prepared to go against the transaction management semantics of the platform vs resorting to having your Admins enable Debug Logs and adding System.debug output to your code to output messages to the standard platform log that can then be reviewed for your errors.

Related Topic