As a Solution Architect I often review Microsoft Dynamics 365 custom server and client side code.
One of the most common rejects regards tracing and exception handling mechanisms, or their absence. Some code constructs may have empty Try/Catch blocks or none at all, other catch exceptions and just re-throw. As for tracing, code often contains debugging ‘aids’ such as alerts and debugger statements or no tracing notifications at all.
Why is this a reject?
- Unhandled raw exceptions float to UI, confusing and frustrating users while exposing internal implementation details to potential attackers
- System Administrator is unaware of custom code exceptions unless users decide to report an error message or a bug
- Tracing mechanism help diagnose faults and sometimes allow resolving without debugging or recompiling code
Microsoft Dynamics 365 contains a Plug-in Trace repository which contains trace and exception entries originating from Plug-in and Custom Workflow Activity components.
While this is a good built in solution, it has some drawbacks:
- It does not support common types of custom code such as client side JavaScript and external components consuming Microsoft Dynamics API directly
- There is no straight forward method to filter Plug-in Trace notification to search for specific text in trace/exception messages
- No option to trigger a notification to system administrator once an exception is logged
- No option to accurately correlate a user reported error message to a specific exception entry
In this post I would like to suggest an updated implementation of a more wholesome infrastructure for tracing and exceptions logging. Using this infrastructure, any developer can easily log trace entries and exceptions from any custom code interacting with Microsoft Dynamics 365 application, while admins can easily monitor the application health in that aspect.
WIIFM
Why using this infrastructure?
- Easily log trace entries and exceptions from any client and server side custom code interacting with Microsoft Dynamics 365 application in a unified manner
- You can add processes triggered by exception Log record creation to alert system administrator regarding a fault as it occurs
- Easily search and filter specific exceptions/trace records according to unique Reference Token provided by users/clients using Quick/Advanced Find
In the following How To Use section you can download a un/managed solution and follow setup and usage instructions. You can view the GitHub project here.
How to use
- Download and import solution
You can download a managed solution here.
If you want to make any changes to this solution, download the un-managed version. In that case, open the solution, navigate to the Log entity and check ‘Settings’ to display the entity in the settings area. Finally, publish the solution.

- Using in an entity form JavaScript code
Add the dyn_utils.js Web Resource to the target entity form libraries collection.
Call the LogTrace and LogException functions in your custom code.

//perform some business logic including tracing and exception handling
ns.DoSomeBusinessLogic = function () {
//define verbose trace entry
var traceEntry = {
//title
"title": "loggingSample.DoSomeBusinessLogic",
//description
"description": "Start execution",
//related business record id
"relatedBusinessRecordId": "2514FC63-9E58-4D1E-8226-69256D0197E3",
//related business record URL
"relatedBusinessRecordURL": "https://xxx.crm.dynamics.com/main.aspx?etc=8&extraqs=&histKey=520872127&id=%7b2514FC63-9E58-4D1E-8226-69256D0197E3%7d&newWindow=true&pagetype=entityrecord#923042",
//set current user id as related user id
"relatedUserId": getContext().userSettings.userId
};
//trace function execution start asynchronously
Utils.LogTrace(traceEntry, true, SuccessHandler, FailureHandler);
try {
//some business logic including some exception
NonExistingFunctionCall();
}
catch (err) {
//define verbose exception entry
var exceptionEntry = {
//title
"title": "loggingSample.DoSomeBusinessLogic",
//description
"description": Constants.MSG_GENERAL_FAILURE,
//related business record id
"errorMessage": err.message,
//related business record URL
"stackTrace": err.stack
};
//log exception and return reference token
var referenceToken = Utils.LogException(exceptionEntry, false);
//notify user with a reference token to report back to admin
alert(Constants.MSG_GENERAL_FAILURE + referenceToken);
}
finally {
//define thin trace entry
var traceEntry = {
//title
"title": "loggingSample.DoSomeBusinessLogic",
//description
"description": "Start execution",
};
//trace function exectuion end asynchronously
Utils.LogTrace(traceEntry, true, SuccessHandler, FailureHandler);
}
}
- Using in Web Resource JavaScript code
You can view the code in action by launching the dyn_loggingSample.htm Web Resource (replace organization base address)
https://YOUR_ORG_NAME.crm.dynamics.com//WebResources/dyn_loggingSample.htm

Make sure you include the dyn_utils.js as a referenced resource:
The sample code in the dyn_loggingSample.js demonstrates tracing and exception handling, same as in the code sample above.
- Using in Plug-in/Custom Workflow Activity/external SDK client
The following code sample represents usage in Plug-in and is similar to CWA and external SDK client usage:
public class LogTraceAndException : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// Get a reference to the organization service
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService organizationService = factory.CreateOrganizationService(context.UserId);
//define a trace entry, all attributes are optional except title
Log traceEntry1 = new Log
{
title = "ServerSideLogging: Execute",
description = "Execution end",
//replace user id with an valid one
relatedUser = new EntityReference("systemuser",
Guid.Parse("37ACCC73-0FCE-474B-A206-19C08163438B")),
relatedBusinessRecordURL = "https://YOURORGNAME.crm.dynamics.com/main.aspx?etc=1&extraqs=&histKey=87055256&id=%7b381FA757-60D7-E811-A97E-000D3AB20035%7d",
};
//log trace entry, get reference token back
string refToken1 = GenericLogging.Log(traceEntry1,
GenericLogging.LogType.Trace, organizationService);
try
{
//some exception raising code.
//If transaction is rolled back, Log entries will persist
throw new Exception("An error occurred, please notify the System Administrator");
}
catch (Exception ex)
{
//define an exception entry
Log excptionEntry = new Log
{
title = "ServerSideLogging: Execute",
errorMessage = ex.Message,
stackTrace = ex.StackTrace
};
//log exception entry, get reference token back
string refToken2 = GenericLogging.Log(excptionEntry,
GenericLogging.LogType.Exception,
organizationService);
throw new InvalidPluginExecutionException(
string.Format("An error occurred, please notify the System Administrator. Reference token: {0}",
refToken2));
}
}
}
A resulting exception raised from the Plug-in code includes the reference token. Once reported to an Admin, he can easily find the relevant Log entry using Quick Find



Implementation Notes
I have written about the subject in the past and the described approach has not changed much. The implementation has been updated to leverage version 9 features (currently available on for Online implementations)
What has changed since last version:
- Using Action definition to activate, deactivate and control logging level
- Using built-in auto-numbering attribute to generate reference token, so a designated CWA component is no longer required
- Optional usage of client side asynchronous logging operation to keep UX uninterrupted
In order to support logging from transactional components (such as Plugin registered to pre/post operation stages), the Log method in GenericLogging.cs file is using the ExecuteMultipleRequest class to execute the dyn_Log request.
As the ExecuteMultipleRequest instance is external to the Plugin transaction, it manages to create a Log record without the Plugin transaction rolling it back.
Using the Execute method directly with the dyn_LogRequest request instead, would have executed the Log Action, but the created Exception record would have been deleted by the Plugin transaction rollback, leaving no trace.
