How to Log Kentico Events to Visual Studio Online

   —   

Every project has specific requirements that make it a unique development experience. From integrations to configurations, each customization requires developers to know how to achieve the desired functionality in the most efficient way. In some scenarios, system events need to log and track in an external system for external administration. In this blog, I’ll show you how to log all of your Kentico exceptions to Visual Studio Online for easy tracking and assignment.

Setup

For my environment, I am using Visual Studio Online for my source control and bug tracking. While Kentico does a great job at tracking errors and exceptions, I really wanted to have them logged in Visual Studio so they can be assigned to a developer. This would allow my site to conform automatically to my ALM processes, and I would be able to track issues easily.

For my demo, I have already created a new Visual Studio Team Project for tracking. Once I have that created, I have pretty much everything I need, from the VSO side of things. Now to write some custom code!

Create a Global Event Handler

The first step in coding is to create a Global Event Handler to capture all of my exceptions. I leverage the CMSLoaderAttribute type to register my class automatically when the application starts.

[CustomEventLogEvents]
public partial class CMSModuleLoader
{
    class CustomEventLogEvents : CMSLoaderAttribute

For the Init method, I use the ObjectEvents class to find the event I needed.

public override void Init()
{
    // Assigns custom handlers to events
    EventLogInfo.TYPEINFO.Events.Insert.After += Insert_After;
}

In the Insert_After method, I add the real magic to log my exception to VSO.

Getting the VSO Account Information

After adding my class, I add the actual code to write the exception to VSO. In order to do this, I want to leverage the VSO REST API to access the service and create the Bugs. This requires getting a service username/password. You can use the tool found here to get the credentials:

http://nakedalm.com/tools/vso-service-credential-viewer/

Using the utility, I got my service account username/password. I add those to my Custom settings for use in my code.

//Set the VSO account information
string accountName = SettingsKeyInfoProvider.GetValue("Custom.VSOAccountName");
string urlAccount = string.Format("https://{0}.visualstudio.com", accountName);
string serviceAccountUsername = SettingsKeyInfoProvider.GetValue("Custom.VSOServiceAccountName");
string servviceAccountPassword = SettingsKeyInfoProvider.GetValue("Custom.VSOServiceAccountPassword");
 
//Hit VSOnline unauthenticated
var client = HttpClientFactory.Create(new HttpClientHandler() { AllowAutoRedirect = false });
 
var resp = await client.GetAsync(urlAccount, HttpCompletionOption.ResponseHeadersRead);
 
if (!resp.Headers.Contains("x-tfs-fedauthrealm") ||
    !resp.Headers.Contains("x-tfs-fedauthissuer"))
{
    Console.WriteLine("Cannot determine federation data");
    return;
}
 
string realm = resp.Headers.GetValues("x-tfs-fedauthrealm").First();
string issuer = resp.Headers.GetValues("x-tfs-fedauthissuer").First();

After accessing the REST Service with my credentials, I receive my OAUTH token. With that, I have everything I need to access my service.

//Get an access token from ACS using WRAP protocol
string acsHost = new Uri(issuer.ToString()).Host;
string acsRequestTokenUrl = string.Format("https://{0}/WRAPv0.9", acsHost);
resp = await client.PostAsync(acsRequestTokenUrl, new FormUrlEncodedContent(
    new KeyValuePair<stringstring>[] {
    new KeyValuePair<stringstring>("wrap_scope", realm.ToString()),
    new KeyValuePair<stringstring>("wrap_name", serviceAccountUsername),
    new KeyValuePair<stringstring>("wrap_password", servviceAccountPassword)
    }
));
 
resp.EnsureSuccessStatusCode();
var acsTokenData = await resp.Content.ReadAsFormDataAsync();
var accessToken = acsTokenData["wrap_access_token"];
var accessTokenExpiredInSecs = int.Parse(acsTokenData["wrap_access_token_expires_in"]);

Note
I am using a third-party utility to get the credentials. There may be others ways to access VSO Online, however, they require Basic authentication. One example can be found here:

https://bryansdemo.visualstudio.com/_details/security/altcreds

Adding the REST API Calls

At this point, I am finally ready to log my exception as a Bug. I create JSON data using the exception details. I set specific fields to the exception details, for easy identification.

sb.Append("[");
sb.Append("{");
sb.Append("\"op\":\"add\",");
sb.Append("\"path\":\"/fields/System.Title\",");
sb.Append("\"value\":\"Error in application\"");
sb.Append("},");
sb.Append("{");
sb.Append("\"op\":\"add\",");
sb.Append("\"path\":\"/fields/System.History\",");
sb.Append("\"value\":\" Description = " + einfo.EventDescription.Replace("\"""'").Replace("\\""/") + "\"");
sb.Append("},");
sb.Append("{");
sb.Append("\"op\":\"add\",");
sb.Append("\"path\":\"/fields/Microsoft.VSTS.TCM.ReproSteps\",");
sb.Append("\"value\":\"Source = " + einfo.Source.Replace("\"""'").Replace("\\""/") + "\"");
sb.Append("}");
sb.Append("]");

With that, I make a call to the VSO REST API to add the item as a Bug.

var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, urlAccount + "/DefaultCollection/" + SettingsKeyInfoProvider.GetValue("Custom.VSOProjectName") + "/_apis/wit/workitems/$Bug?api-version=1.0")
{
    Content = new StringContent(sb.ToString(), Encoding.UTF8,
        "application/json-patch+json")
};
 
request.Headers.Authorization = new AuthenticationHeaderValue(
    "WRAP",
    string.Format("access_token=\"{0}\"", accessToken));
 
await client.SendAsync(request);

Testing

With my code in place, I can test my integration. First, I make sure I have no Bugs currently in my VSO interface.

Empty Bug List

I access the site and generate an exception. I then check my VSO UI to see if it was logged properly.

Bug List

I can now verify that the Bug details were set correctly.

Bug Details

Bug History

Wrapping Up

With the above code, I can now log any exceptions right into Visual Studio Online. This would make tracking much easier and assignment to a developer a breeze. Moving forward, I would add more filters on the exceptions, with specific mappings/assignments based on the type/content. Additionally, I can use other VSO REST API functions to create work items, defects, and other types.

I hope this blog shows you how you can capture Kentico events and log them to external systems easily. If you have any other ideas, let me know in the comments. Good luck!

Download the Event Handler

Helpful Links

If you want to know more about integrating with Visual Studio Online, check these links:

https://www.visualstudio.com/en-us/integrate/get-started/auth/overview

https://www.visualstudio.com/integrate/api/wit/work-items

https://msdn.microsoft.com/en-us/library/dd236908.aspx

This blog is intended for information purposes only and provided an example of one of the many ways to accomplish the described task. Always consult Kentico Documentation for best practices and additional examples that may be more effective in your specific situation.

Share this article on   LinkedIn

Bryan Soltis

Hello. I am a Technical Evangelist here at Kentico and will be helping the technical community by providing guidance and best practices for all areas of the product. I might also do some karaoke. We'll see how the night goes...

Comments