Sunday, August 28, 2011

Error Logging Plug-in exceptions in CRM Online

(Cross-posted from the Avanade Xrm Blog, where I've been posting more recently)

For a while now we've been trying to find some way of logging plug-in exceptions on CRM Online (and I've also seen a number of posts on the CRM Developer forums asking if this is possible).

Logging exceptions that originate from CRM plug-ins presents a number of problems, especially in CRM online.  First of all, plug-ins in CRM online are sand-boxed, so you do not have access to the file system or event log.  The CRM SDK offers a tracing service that can be used, but this only displays tracing information to the end user.  No permanent log of the exception is recorded.
 
The logical solution is to just write all exception messages to a custom CRM entity (The SDK even mentions this idea).  But this is also problematic because synchronous plug-ins are run inside of a database transaction. So if your plug-in runs into an exception the transaction will get rolled back, including rolling back the creation of any Error Log record you created while handling the exception.

Ideally, we want all of the operations of the plug-in to be inside the database transaction, but the creation of the errorlog record to NOT be part of the transaction (so that any changes made by the plug-in are rolled back, but an admin can still read the error message afterwards). This can be achieved in an on-premise CRM installation by just creating a new OrganizationService proxy from scratch, instead of using the one provided by the plugin context. But again, this doesn't work in CRM Online because of the sandbox restrictions (when running in partial trust you cannot make WCF calls or use the libraries included in the CRM SDK for connecting to CRM online).

Fortunately, there is a different way to connect to CRM online while in partial-trust, and the answer comes from this post on Dimaz Pramudya's blog.
"There is a sample on how to achieve this using Passport authentication in MSCRM SDK. ... it failed miserably in shared hosting environment which runs Medium or High trust level.

What I wanted to share today is an alternative way to get the Windows Live Id ticket that works virtually on any environment.  This method uses the RPS (Relying Party Suites) authentication option for Windows Live."
This post is over a year old, and only describes connecting to the CRM 4.0 webservicce, but it will still work with CRM 2011 online.

Dimaz has created two c# classes; LiveIdTicketAcquirer and CrmOnlineQueryManager, which together can be used to connect to CRM online from within a CRM plug-in.  And this new connection will not be part of the database transaction, which means it can be used to write our error log messages back to a CRM entity.

In the following code sample I am using the CrmOnlineQueryManager to log an exception to a custom entity called "new_error":

public class TestPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        try
        {
            throw new Exception("Testing Error logging");
        }
        catch( Exception ex)
        {
            CrmOnlineErrorLogger.LogError(ex);
            throw;
        }
    }
}

class CrmOnlineErrorLogger
{
        const string username = "myLiveId";
        const string password = "myPassword";
        const string organization = "myOrgName";

        public static void LogError(Exception ex)
        {
               CrmOnlineQueryManager queryManager = new CrmOnlineQueryManager();
               var crmService = queryManager.Connect(username, password, organization);
               DynamicEntity entity = new DynamicEntity();
               entity.Name = "new_error";
               StringProperty exceptionType = new StringProperty() { Name = "new_exceptiontype", Value = ex.GetType().FullName };
               StringProperty details = new StringProperty() { Name = "new_details", Value = ex.Message + ": " + ex.StackTrace };
               entity.Properties = new Property[] { exceptionType, details };
               crmService.Create(entity);                 
        }
}

When I run this plug-in, the thrown exception rolls back the update I was making, and I see the expected error message:


But I also see a log of the exception in my custom "Errors" table:


So now we can do error logging on CRM Online.  Of course, this is just a proof of concept. There's a few more steps we would need to take before using this in a production environment..
  1. This code still connects to the old CRM 4.0 webservice. Presumably a similar connector could be made that uses the 2011 endpoint.
  2. There may be performance issues.  I've noticed its a bit slow to log errors, but a lot of the slowdown can be reduced by trimming the crm4 webservice proxy class down to just the minimum components needed.  Also, the slowdown only occurs when the plug-in actually throws an error, which may be acceptable.  Bottom line, this technique still needs to be fully tested.
The source-code for this proof-of-concept can be found here. (this code is provided "as is" and provides no warranties or guarantees).

~Erik Pool