To support your wish list, I would create the following objects:
Pluginlog
- Contains the information you would like to save for successful plugin completions
- Associated with the Plugin exception class. So you can see what happened before an exception occurred
PluginException
- Contains any special exception information (user, context, stack trace)
Now open your wish list:
- Save the last n lines of the log in memory.
- Not sure if you want to register a specific plugin class or all plugin classes defined in a DLL, I will take a specific plugin class:
- Create a static ConcurrentQueue for the plugin
- Calling one function to add a line to the log.
- Create a single function that creates a PluginLog object in memory (without creating it in the CRM database) and adds it to the queue.
- If length> n, dequeue.
- Calling one function to trigger an error record.
- Again, this is what you need to create. Basically, I create a PLuginException object in CRM, and then remove all the elements from the queue, populating the plugin exception identifier and saving it in CRM.
- When there is a mistake, write to me saying that there is a problem. This contains summary information.
- As long as the domain application context for the executable plugin has the necessary rights (not sure if it works in CRM Online), this should be trivial.
- The email contains a link to a web page with full error information. Stack trace, error message, date, time, user, etc., Last n lines of the log.
- You can create a link to the created PluginException object and include it in the email along with all other relevant information.
- It would be nice if there was a web page where all errors were shown, with filtering, sorting, etc.
Edit
To help you get started, this is what I am currently using to extract all the information from the context of the plugin and convert it to text that is inserted into the exception:
#region GetPluginInfo private Exception GetPluginExecutionInfoForLog(IServiceProvider serviceProvider, Exception ex) { if(ex.GetType() == typeof(InvalidPluginExecutionException)){ return ex; } try { var context = serviceProvider.GetContext(); ex = new InvalidPluginExecutionException( String.Format("Error During Plugin Execution: {0}**** Context Values ****{0}{1}", Environment.NewLine, GetPluginExecutionInfo(context)), ex); } catch (Exception childEx) { OnError(childEx); } return ex; } protected String GetPluginExecutionInfo(IPluginExecutionContext context) { var lines = new List<String>(); var target = GetTarget<Entity>(context); lines.Add("MessageName: " + context.MessageName); lines.Add("PrimaryEntityName: " + context.PrimaryEntityName); lines.Add("PrimaryEntityId: " + context.PrimaryEntityId); lines.Add("BusinessUnitId: " + context.BusinessUnitId); lines.Add("CorrelationId: " + context.CorrelationId); lines.Add("Depth: " + context.Depth); lines.Add("Has Parent Context: " + (context.ParentContext != null)); lines.Add("InitiatingUserId: " + context.InitiatingUserId); AddParameters(lines, context.InputParameters, "Input Parameters"); lines.Add("IsInTransaction: " + context.IsInTransaction); lines.Add("IsolationMode: " + context.IsolationMode); lines.Add("Mode: " + context.Mode); lines.Add("OperationCreatedOn: " + context.OperationCreatedOn); lines.Add("OperationId: " + context.OperationId); lines.Add("Organization: " + context.OrganizationName + "(" + context.OrganizationId + ")"); AddParameters(lines, context.OutputParameters, "Output Parameters"); AddEntityReference(lines, context.OwningExtension, "OwningExtension"); AddEntityImages(lines, context.PostEntityImages, "Post Entity Images"); AddEntityImages(lines, context.PreEntityImages, "Pre Entity Images"); lines.Add("SecondaryEntityName: " + context.SecondaryEntityName); AddParameters(lines, context.SharedVariables, "Shared Variables"); lines.Add("Stage: " + context.Stage); lines.Add("UserId: " + context.UserId); if (target == null || target.Attributes.Count == 0) { lines.Add("Target: Empty "); } else { lines.Add("* Target " + target.ToEntityReference().GetNameId() + " *"); foreach (var att in target.Attributes) { lines.Add(" Entity[" + att.Key + "]: " + GetAttributeValue(att.Value)); } } lines.Add("* App Config Values *"); foreach (var key in ConfigurationManager.AppSettings.AllKeys) { lines.Add(" [" + key + "]: " + ConfigurationManager.AppSettings[key]); } return String.Join(Environment.NewLine, lines); } private static string GetAttributeValue(object value) { if(value == null){ return "Null"; } var type = value.GetType(); if (type == typeof(OptionSetValue)) { return ((OptionSetValue)value).Value.ToString(); } else if (type == typeof(EntityReference)) { return ((EntityReference)value).GetNameId(); } else { return value.ToString(); } } private static void AddEntityReference(List<string> nameValuePairs, EntityReference entity, string name) { if (entity != null) { nameValuePairs.Add(name + ": " + entity.GetNameId()); } } private static void AddEntityImages(List<string> nameValuePairs, EntityImageCollection images, string name) { if (images != null && images.Count > 0) { nameValuePairs.Add("** " + name + " **"); foreach (var image in images) { if (image.Value == null || image.Value.Attributes.Count == 0) { nameValuePairs.Add(" Image[" + image.Key + "] " + image.Value.ToEntityReference().GetNameId() + ": Empty"); } else { nameValuePairs.Add("* Image[" + image.Key + "] " + image.Value.ToEntityReference().GetNameId() + " *"); foreach (var att in image.Value.Attributes) { nameValuePairs.Add(" Entity[" + att.Key + "]: " + GetAttributeValue(att.Value)); } } } } else { nameValuePairs.Add(name + ": Empty"); } } private static void AddParameters(List<string> nameValuePairs, ParameterCollection parameters, string name) { if (parameters != null && parameters.Count > 0) { nameValuePairs.Add("* " + name + " *"); foreach (var param in parameters) { nameValuePairs.Add(" Param[" + param.Key + "]: " + param.Value); } } else { nameValuePairs.Add(name + ": Empty"); } } #endregion // GetPluginInfo
Daryl source share