Skip to content

Commit 9d5956d

Browse files
authored
Merge pull request #1548 from ValerasNarbutas/newsample/1546TenantTelemetryListener
2 parents c24de06 + 64d74ff commit 9d5956d

28 files changed

Lines changed: 33811 additions & 0 deletions

samples/react-application-tenant-telemetry-listener/.eslintrc.js

Lines changed: 319 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
6+
# Dependency directories
7+
node_modules
8+
9+
# Build generated files
10+
dist
11+
lib
12+
release
13+
solution
14+
temp
15+
*.sppkg
16+
.heft
17+
18+
# Coverage directory used by tools like istanbul
19+
coverage
20+
21+
# OSX
22+
.DS_Store
23+
24+
# Visual Studio files
25+
.ntvs_analysis.dat
26+
.vs
27+
bin
28+
obj
29+
30+
# Resx Generated Code
31+
*.resx.ts
32+
33+
# Styles Generated Code
34+
*.scss.ts
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
!dist
2+
config
3+
4+
gulpfile.js
5+
6+
release
7+
src
8+
temp
9+
10+
tsconfig.json
11+
tslint.json
12+
13+
*.log
14+
15+
.yo-rc.json
16+
.vscode
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v22.16.0
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"@microsoft/generator-sharepoint": {
3+
"whichFolder": "subdir",
4+
"solutionName": "react-application-tenant-telemetry-listener",
5+
"componentType": "extension",
6+
"extensionType": "ApplicationCustomizer",
7+
"template": "none",
8+
"componentName": "TenantTelemetry",
9+
"plusBeta": false,
10+
"isCreatingSolution": true,
11+
"nodeVersion": "22.21.1",
12+
"sdksVersions": {},
13+
"version": "1.21.1",
14+
"libraryName": "react-application-tenant-telemetry-listener",
15+
"libraryId": "136ab316-3f7a-47f0-83c9-b30253706f3f",
16+
"environment": "spo",
17+
"packageManager": "npm",
18+
"solutionShortDescription": "react-application-tenant-telemetry-listener description",
19+
"skipFeatureDeployment": true,
20+
"isDomainIsolated": false
21+
}
22+
}
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
# Tenant Telemetry Application Customizer
2+
3+
A lightweight tenant-wide Application Customizer that automatically collects telemetry events from any SPFx Web Part or extension across the entire tenant.
4+
This solution enables organizations to track:
5+
6+
* Which sites use which custom SPFx components
7+
* Component versions
8+
* Page usage
9+
* Custom events from web parts
10+
* User interactions
11+
* Performance / feature adoption
12+
13+
The telemetry is sent either directly to **Azure Application Insights** or to a custom **Azure Function** endpoint depending on your configuration.
14+
15+
![Telemetry Overview](assets/preview.png)
16+
17+
## Summary
18+
19+
This SPFx Application Customizer listens for telemetry events via the browser’s `CustomEvent` API:
20+
21+
```ts
22+
window.dispatchEvent(new CustomEvent("spfx-telemetry", { detail: { ... } }));
23+
```
24+
25+
The extension enriches the payload with site and page context and then forwards it through one of two configurable channels:
26+
27+
### **Telemetry modes**
28+
29+
| Mode | Description |
30+
| ------------- | ----------------------------------------------------------------------------------------------------------------------- |
31+
| `appinsights` | Sends events directly to Azure Application Insights using a Connection String |
32+
| `function` | Sends events to a custom Azure Function (HTTP POST) for further processing (Event Grid, Log Analytics, Cosmos DB, etc.) |
33+
34+
### **This sample includes**
35+
36+
✔ Application Customizer
37+
✔ TelemetryService (AppInsights + Function modes)
38+
✔ WebPart integration example
39+
✔ Console test commands
40+
✔ Deployment instructions (tenant-wide)
41+
✔ App Insights Log queries
42+
43+
## Compatibility
44+
45+
| :warning: Important |
46+
|:---------------------------|
47+
| Every SPFx version is optimally compatible with specific versions of Node.js. In order to be able to Toolchain this sample, you need to ensure that the version of Node on your workstation matches one of the versions listed in this section. This sample will not work on a different version of Node.|
48+
|Refer to <https://aka.ms/spfx-matrix> for more information on SPFx compatibility. |
49+
50+
This sample is optimally compatible with the following environment configuration:
51+
52+
![SPFx 1.21.1](https://img.shields.io/badge/SPFx-1.21.1-green.svg)
53+
![Node.js v22](https://img.shields.io/badge/Node.js-v22-green.svg)
54+
![Toolchain: Heft](https://img.shields.io/badge/Toolchain-Heft-green.svg)
55+
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
56+
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower")
57+
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
58+
![Local Workbench Unsupported](https://img.shields.io/badge/Local%20Workbench-Unsupported-red.svg "Local workbench is no longer available as of SPFx 1.13 and above")
59+
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
60+
![Compatible with Remote Containers](https://img.shields.io/badge/Remote%20Containers-Compatible-green.svg)
61+
62+
## Applies to
63+
64+
* [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
65+
* [Microsoft 365 tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-developer-tenant)
66+
67+
## Contributors
68+
69+
* [Valeras Narbutas](https://github.com/ValerasNarbutas)
70+
71+
## Version history
72+
73+
| Version | Date | Comments |
74+
| ------- | ---------- | --------------- |
75+
| 1.0 | November 18, 2025 | Initial release |
76+
77+
## Prerequisites
78+
79+
* Azure Application Insights **or** Azure Function endpoint
80+
* Tenant App Catalog
81+
* Modern SharePoint pages
82+
83+
## Minimal Path to Awesome
84+
85+
### 1. Clone the repo
86+
87+
```bash
88+
npm install
89+
gulp build
90+
gulp bundle --ship
91+
gulp package-solution --ship
92+
```
93+
94+
### 2. Upload the `.sppkg` to the Tenant App Catalog
95+
96+
```
97+
/sites/appcatalog/Apps for SharePoint
98+
```
99+
100+
Check:
101+
102+
**Make this solution available to all sites**
103+
✔ Deploy
104+
105+
### 3. Register the Application Customizer in Tenant Wide Extensions
106+
107+
Open:
108+
109+
```
110+
/sites/appcatalog/Lists/TenantWideExtensions
111+
```
112+
113+
Create a **new item** with:
114+
115+
| Field | Value |
116+
| ----------- | ------------------------------------------- |
117+
| Title | TenantTelemetry |
118+
| ComponentId | `11352c34-6ebe-4679-82bb-734061f2cae0` |
119+
| Location | `ClientSideExtension.ApplicationCustomizer` |
120+
| Sequence | `1` |
121+
122+
### 4. Add configuration (App Insights mode)
123+
124+
```json
125+
{
126+
"mode": "appinsights",
127+
"endpoint": "InstrumentationKey=XXXX;IngestionEndpoint=YYYY"
128+
}
129+
```
130+
131+
### (Optional) Azure Function mode
132+
133+
```json
134+
{
135+
"mode": "function",
136+
"endpoint": "https://yourfunction.azurewebsites.net/api/telemetry"
137+
}
138+
```
139+
140+
Save → telemetry starts flowing across the entire tenant.
141+
142+
![Telemetry AppCatalog](assets/previewAppCatalog.png)
143+
144+
## How to Use in Your Web Parts
145+
146+
Your web part (or another extension) simply dispatches an event:
147+
148+
```ts
149+
window.dispatchEvent(
150+
new CustomEvent("spfx-telemetry", {
151+
detail: {
152+
eventName: "MyWebPart_Rendered",
153+
componentId: this.context.manifest.id,
154+
componentName: this.context.manifest.alias,
155+
componentVersion: this.context.manifest.version,
156+
customData: {
157+
action: "render",
158+
filterValue: this.properties.category
159+
}
160+
}
161+
})
162+
);
163+
```
164+
165+
Example webpart can be found in samples:
166+
[https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-telemetry-event-sender-sample](https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-telemetry-event-sender-sample)
167+
168+
That’s all.
169+
No dependency imports. No service initialization.
170+
The Application Customizer handles all communication.
171+
172+
## How to Test Via Console
173+
174+
Open any modern SharePoint page, then run:
175+
176+
```js
177+
window.dispatchEvent(
178+
new CustomEvent("spfx-telemetry", {
179+
detail: {
180+
eventName: "Console_Test_Event",
181+
componentId: "console",
182+
componentName: "Console",
183+
componentVersion: "0.0.1",
184+
customData: { message: "Hello from console!" }
185+
}
186+
})
187+
);
188+
```
189+
190+
![Telemetry Overview debug](assets/previewDebug.png)
191+
192+
The Application Customizer logs the event and pushes telemetry.
193+
194+
## How to Verify in Application Insights
195+
196+
Go to **Logs** and run:
197+
198+
```kusto
199+
customEvents
200+
| where name == "Console_Test_Event"
201+
| order by timestamp desc
202+
```
203+
204+
Or view *all* events:
205+
206+
```kusto
207+
customEvents
208+
| extend siteUrl = tostring(customDimensions.siteUrl)
209+
| extend componentName = tostring(customDimensions.componentName)
210+
| project timestamp, name, siteUrl, componentName, customDimensions
211+
| top 50 by timestamp desc
212+
```
213+
214+
## How to Test Azure Function Mode
215+
216+
If using Azure Function mode, telemetry is sent via:
217+
218+
```http
219+
POST https://yourfunction.azurewebsites.net/api/telemetry
220+
Content-Type: application/json
221+
Body: { ...payload }
222+
```
223+
224+
### Check your Function
225+
226+
#### 1. Azure Portal → Function App → Function → **Monitor**
227+
228+
You should see requests.
229+
230+
#### 2. Azure Portal → Function → **Logs**
231+
232+
You should see the JSON payload:
233+
234+
```json
235+
{
236+
"eventName": "Console_Test_Event",
237+
"siteUrl": "https://tenant.sharepoint.com/sites/...",
238+
"pageUrl": "https://tenant.sharepoint.com/sites/.../SitePages/Page.aspx",
239+
"componentId": "...",
240+
"componentVersion": "...",
241+
"customData": {
242+
"message": "Hello from console!"
243+
}
244+
}
245+
```
246+
247+
#### 3. Your function can forward to
248+
249+
* Event Grid
250+
* Log Analytics
251+
* Cosmos DB
252+
* Azure Storage
253+
254+
---
255+
256+
## Debug URL
257+
258+
To test the extension locally:
259+
260+
```
261+
?loadSPFX=true
262+
&debugManifestsFile=https://localhost:4321/temp/manifests.js
263+
&customActions={"11352c34-6ebe-4679-82bb-734061f2cae0":{"location":"ClientSideExtension.ApplicationCustomizer","properties":{"mode":"appinsights","endpoint":"InstrumentationKey=X;IngestionEndpoint=Y"}}}
264+
```
265+
266+
(Update `ComponentId` and endpoint as needed.)
267+
268+
## Features
269+
270+
This sample demonstrates:
271+
272+
* A **tenant-wide telemetry** system for SPFx
273+
* A **runtime-configurable backend** (App Insights or Azure Function)
274+
* A simple, framework-agnostic **telemetry event protocol** using CustomEvent
275+
* A lightweight **Application Customizer listener**
276+
* Web part instrumentation without dependencies
277+
* Support for:
278+
✔ Site-level context
279+
✔ Page URL
280+
✔ Component identity and version
281+
✔ Custom event data
282+
283+
## Disclaimer
284+
285+
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
184 KB
Loading
60 KB
Loading
76.9 KB
Loading

0 commit comments

Comments
 (0)