Wednesday 20 September 2017

Use Azure App Insights to track events in your app/web part/provisioning code

In my recent post Add Azure App Insights or Google Analytics to your SharePoint pages with an SPFx Application Customizer we focused on the page tracking/analytics capability of App Insights. But what I really think is cool is the ability to track what is happening your code – whether it’s simply that your web part has executed or your app has been launched (so you can get counts), or that you showed an error message to a user (with the details), or maybe to understand where users are navigating in your app or which options they select. As an example, we have a tabbed interface in one particular app, and I’d really love to know how many users are actually navigating to the 2nd and 3rd tabs (my bet is very few). Well, App Insights event tracking is great for these scenarios and more. I finished the last post with this list:

  • Server side applications (Office 365/SharePoint Add-in)
    • How many app launches are happening? By who/where?
    • What is happening within the app (e.g. which buttons are being clicked/what functionality is being used)?
  • SPFx/Graph
    • How many executions is our web part getting per day?
    • How long do my web parts take to execute on the client side?
    • How long are my async calls taking (e.g. to the Graph)? How different are they for users around the world?
  • Provisioning code
    • How many sites are we creating per day/week/month?
    • How many times do we hit an issue during provisioning?
    • How long does provisioning take?
  • General
    • How many times are we showing an error message to a user?

You get the idea.

A quick getting started recap (for JavaScript/SPFx)

Remember that to use App Insights you need an Azure subscription and an App Insights Resource to be created – this will give you the instrumentation key which you obtain from the Azure portal (see my last post for more details). If you’re working with JavaScript/TypeScript/SPFx, there’s an npm package you can install with:

npm i applicationinsights-js --save

From there, you'll need the couple of lines which get you started. The import statement at the top of your module:

import {AppInsights} from "applicationinsights-js";

..and then the initial bootstrap code which references your App Insights key:

let appInsightsKey: string = "[YOUR KEY FROM THE AZURE PORTAL HERE]";

AppInsights.downloadAndSetup({ instrumentationKey: appInsightsKey });

Now you're ready to log various things which happen in your code to App Insights. In essence, we drop a line in when we want to log that something has happened - let's look at some examples.

Example 1 – logging a call to the Microsoft Graph (in SPFx code)

The code below fetches calendar events for the current user in SPFx code, and is taken from one of the SPFx React samples. This is a fairly typical use of the Microsoft Graph, and the logging approach used here could be used across lots of similar scenarios. The fact that it is in a React component is not important, but the bits to focus on are the calls to App Insights - note that what I’m also doing is recording the *time* taken to make the call, so as well as finding many times my web part is being used, I also start to understand how long calls to the Graph are taking for users around the world. To do this, I simply create a timestamp before the call, and then another in the promise which executes once the data has been fetched and subtract the difference:

In App Insights, I can then see an event for each execution of my web part along with the time taken to call the Graph:

SNAGHTML1f5d5c2c

SNAGHTML203e67e

As I showed in my last App Insights post, as you accumulate data you can use the ‘Analytics’ section in App Insights to filter and sort as you need (e.g. on the 'timeTaken’ value):

SNAGHTML1f6b05fa

Example 2 – logging a call in site provisioning/templating code

So that’s an example of logging in SPFx. As a different flavor, let’s say you are creating some SharePoint sites based on a template – perhaps as part of a self-service site creation tool. You might be interested in things like:

  • How many sites are being created? With what details?  
  • How long is Office 365 taking to create the base site collection?
  • How long does it take for your template to be applied?

In this case, you’ll probably be using the PnP Core library in C# code – so you’ll need the App Insights nuget package from https://www.nuget.org/packages/Microsoft.ApplicationInsights/

To get started in C# code, you’ll need some bootstrap code:

// at top of class..
using Microsoft.ApplicationInsights;

// to initialize..
TelemetryClient telemetry = new TelemetryClient(); 
telemetry.InstrumentationKey = APP_INSIGHTS_KEY;

As the first scenario, let’s say you want to log the fact that a site collection was created and how long that step took. If you’re using PnP provisioning code, it might look something like the below – the key things are:

  • Simple use of the .NET stopwatch clock for timings
  • Creation of ‘metrics’ and ‘properties’ dictionaries to pass details to App Insights
  • Use of the TrackEvent() method to actually log the data

If using PnP provisioning code, you might end up with something like: 

This would then show up in App Insights with details of the SharePoint URL, and the time taken for site collection creation (shown here in the Analytics tool):

SNAGHTML16524535

We can extend this to PnP templating too. In a similar way, I would add logging statements around PnP 'ApplyProvisioningTemplate' call, and perhaps log any errors too.

Code sample:

As expected, you can then get the details of your template being applied with whatever details you logged (site URL, template ID and processing time in my case):

SNAGHTML1f82d233

Summary

App Insights is awesome for integrating into all sorts of Office 365 and SharePoint dev things. By simply dropping a couple of statements in your code, you get to report on it later and get way better visibility of what is happening than you might otherwise have. Whether it’s executions of your web part, errors which are happening, or how often users are going down particular paths in your app, it’s a great way to build this logging in with low effort. In terms of practicalities, you do need an Azure subscription of course and App Insights is chargeable if you pass more than 1GB of data per month (at the time of writing), and your data is only retained for 90 days. BUT, you don’t need to build any kind of front-end or query tool, you can get graphs/charts, weekly summary e-mails, and importantly, alerts if any conditions you define are not met. Additionally, there are lots of ways to integrate with the data (including BLOB download to keep the data for longer). All considered, the features are pretty awesome.

I feel App Insights is definitely under-utilized by Office 365 and SharePoint developers, but there are lots of possibilities. I’m looking forward to building it in to more of our solutions!

Monday 11 September 2017

Manage tenant-scoped SPFx extensions across your SharePoint sites

As I mentioned in Use an SPFx Application Customizer to add JavaScript (e.g. header) to every page in a site, it’s now possible to *globally* deploy SPFx extensions (e.g. page headers, footers or other random pieces of JavaScript) or do a controlled roll-out across *many* SharePoint sites - without the app needing to be installed to each individual site. This is great news, and it was a gap for modern pages until now. SPFx web parts deployed at tenant scope will appear everywhere in the picker, but for SPFx extensions there is still something you need to do locally, and that’s “associate” your extension with the site/web/list/field. For Application Customizers, it’s this step which allows you to control exactly which sites use your extension. To do this you add a CustomAction to your site or web, specifying the GUID of your extension in the ClientSideComponentId property (new for SPFx). Although I’m focusing more on site-level customizations (Application Customizers) in this post, it’s a similar story for SPFx Field Customizers too (ClientSideComponentId is specified on the field) and SPFx Command Set Customizers (CustomAction with ClientSideComponentId is specified on the list). All this can be done a couple of ways:

  • Using CSOM or REST – perhaps in PowerShell or C# code
  • As part of PnP XML, if you are applying a custom template to the site – the XML schema and PnP Core provisioning library now supports this (Sept 2017 release onwards)

In this post I’ll provide some PowerShell and C# code to help you apply Application Customizers across your sites – you could modify for other types of customizer without too much trouble. However, there are some prerequisites in all cases - in some ways, the association step is one of the last things you will do. So let's cover that quickly:

Tenant-scoped SPFx extensions - recap/prerequisites

SPFx extensions and webparts are possible from v1.2 of SPFx onwards. Broadly, the prerequisites needed before the script/code in this article can be used are:-

  • You specified "skipFeatureDeployment": true in the package-solution.json file
  • The app was packaged and then installed to the App Catalog, and the administrator checked the box for 'Make this solution available to all sites in the organization' (shown in image below)
  • The JavaScript bundle for the SPFx app has been deployed to a CDN or other web-hosting location

Here's what the administrator will see when installing to the App Catalog, and the checkbox (which they need to check) that appears when "skipFeatureDeployment": true:

SNAGHTMLf7281c2

The PnP XML option

I'll focus on the C#/PowerShell options in this post, but there’s a PnP XML option which is very useful too. This allows you to include the association as part of a custom site template, and therefore is great for any sites being newly-created from such a template. In fact, you could also use it to apply a ‘partial’ template to existing sites, but I think most people might choose PowerShell or C# code at that point. The main info I want to convey here is that this is available from version 2.18.1709 onwards of PnP Core (Sept 2017 release). The 2017-05 schema has places to specify the ClientSideComponentId property on a CustomAction and on a field, since that’s what the association consists of. The XML extract to provision an Application Customizer at the web level would be:

In next sections, I’ll cover PowerShell first and then C#.

Using PowerShell code and PnP PowerShell

Here are some PowerShell functions to add, remove and list the SPFx global extensions across a selection of sites, done by adding a Custom Action at the root web level – tweak if you need something else. I'm using a simple array of site URLs here, but you could fill that array however you like. Other notes:-

  • I'm using PnP PowerShell cmdlets here - you'll need to install those if you don't have them already, and then get connected to your tenant with Connect-PnPOnline etc.
  • At the time of writing, I had an issue with the PnP cmdlet that they provide (Add-PnPCustomAction), so I'm using direct CSOM in the 'add' method. I raised a GitHub issue about this (also noted in comments in script below), and I'm sure the guys will fix it soon (or tell me I'm doing something wrong ;), but the direct CSOM approach works fine too)
Output:

Registering a globally-deployed extension with addSpfxExtensionCustomAction(ctx) will give:

SNAGHTMLf6529dd

Listing the extensions on a site with:

Get-PnPCustomAction -Web $ctx.Web | Where-Object {$_.Location -eq "ClientSideExtension.ApplicationCustomizer" }

will give:

SNAGHTMLf67a240

Removing an extension with

Remove-CustomActionForSPFxExt $spfxExtName $site $ctx

will give:

SNAGHTMLf69e124

Using C# code and PnP core

But instead of PowerShell, perhaps you want to use C# code instead. Some notes on this:-

  • I'm using the PnP Core library here - you'll need to install the NuGet package to your solution/Azure Function/whatever if you don't have it already. Get this from https://www.nuget.org/packages/SharePointPnPCoreOnline
  • In contrast to the PowerShell above I'm only processing a single site here, but it would be trivial to extend the code to run across whatever sites you need

Sample code:

Output:

As you’d expect, registering a globally-deployed extension with addSpfxExtensionCustomAction(ctx) will give:

SNAGHTMLf59848b

Listing the extensions on a site with getCustomActions(ctx) will give:

SNAGHTMLf55b850

Removing an extension with removeSpfxExtensionCustomAction() will give:

SNAGHTMLf5790d9

Another option – CLI scripts

As another option, note that my esteemed colleague Vardhaman Deshpande also has a super-cool CLI tool to help you manage SPFx extensions. He’s so hipster ;) His scripts offers the ability to manage SPFx Command Set Customizers too. See https://github.com/vman/spfx-extensions-cli for more details.

Summary

For tenant-scoped SPFx Application Customizers, you need to ensure the sites or webs which should use it have a CustomAction with the ClientSideComponentId of your extension (in addition to dealing with the other prerequisite steps i.e. getting the app package and corresponding JavaScript bundle deployed). Although not addressed with this code, it’s a similar approach for SPFx Field Customizers and Command Set Customizers too. Hopefully the options presented in this article (together with the underlying PnP awesomeness) are of some use.