Skip to content

Commit 76dc759

Browse files
solonkoolesyaOlesia Solonko
andauthored
Refactor application insight (#40)
* Add Application Insight integration * Refactor Application Insight integration --------- Co-authored-by: Olesia Solonko <[email protected]>
1 parent 4741d88 commit 76dc759

File tree

5 files changed

+131
-1
lines changed

5 files changed

+131
-1
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,6 @@ Thumbs.db
4646
*.log
4747
/tmp/pygeoapi.log
4848
frontend/node_modules/
49+
50+
flake.lock
51+
flake.nix

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ services:
4141
- CONTAINER_HOST=0.0.0.0
4242
- CONTAINER_PORT=5000
4343
- PYGEOAPI_SERVER_URL=http://localhost:5000
44-
#- APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=b4d987bc-1712-4026-aafb-f590fd854ab3;IngestionEndpoint=https://norwayeast-0.in.applicationinsights.azure.com/;LiveEndpoint=https://norwayeast.livediagnostics.monitor.azure.com/;ApplicationId=9fc4692c-7609-4abb-b547-49203ac14d4a
4544
profiles:
4645
- local
4746

@@ -52,6 +51,7 @@ services:
5251
- "8080:80"
5352
environment:
5453
- API_BASE_URL=http://backend-local:5000
54+
- APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=b4d987bc-1712-4026-aafb-f590fd854ab3;IngestionEndpoint=https://norwayeast-0.in.applicationinsights.azure.com/;LiveEndpoint=https://norwayeast.livediagnostics.monitor.azure.com/;ApplicationId=9fc4692c-7609-4abb-b547-49203ac14d4a
5555
depends_on:
5656
- backend-local
5757
profiles:

frontend/build.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ if (apiBaseUrl) {
1818
html = html.replace("</head>", `${scriptTag}\n</head>`);
1919
}
2020

21+
// Add Application Insights connection string if available
22+
const appInsightsConnectionString = process.env.APPLICATIONINSIGHTS_CONNECTION_STRING || "";
23+
if (appInsightsConnectionString) {
24+
const appInsightsScript = `<script>window.APPLICATIONINSIGHTS_CONNECTION_STRING = "${appInsightsConnectionString}";</script>`;
25+
html = html.replace("</head>", `${appInsightsScript}\n</head>`);
26+
}
27+
2128
const distHtmlPath = path.join(__dirname, "dist", "index.html");
2229
fs.writeFileSync(distHtmlPath, html);
2330

@@ -27,3 +34,9 @@ const distJsDir = path.join(__dirname, "dist", "js");
2734
fs.readdirSync(srcJsDir).forEach((file) => {
2835
fs.copyFileSync(path.join(srcJsDir, file), path.join(distJsDir, file));
2936
});
37+
38+
if (appInsightsConnectionString) {
39+
console.log("Application Insights: Enabled");
40+
} else {
41+
console.log("Application Insights: Disabled (no connection string provided)");
42+
}

frontend/src/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
<!-- <script src="js/points-layer.js"></script> -->
1818
<!-- Add this to your index.html file, wherever your other script tags are located -->
1919
<!-- <script src="js/norway-hazard-tiles.js"></script> -->
20+
<!-- Application Insights -->
21+
<script src="js/appInsights.js"></script>
2022
<style>
2123
/* Reset and full screen layout */
2224
* {

frontend/src/js/appInsights.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
(function() {
2+
const connectionString = window.APPLICATIONINSIGHTS_CONNECTION_STRING;
3+
4+
if (!connectionString) {
5+
return;
6+
}
7+
8+
const script = document.createElement('script');
9+
script.src = 'https://js.monitor.azure.com/scripts/b/ai.2.min.js';
10+
script.async = true;
11+
12+
script.onload = function() {
13+
const appInsights = new Microsoft.ApplicationInsights.ApplicationInsights({
14+
config: {
15+
connectionString: connectionString,
16+
enableAutoRouteTracking: false,
17+
disableExceptionTracking: true,
18+
disableTelemetry: false,
19+
samplingPercentage: 100
20+
}
21+
});
22+
23+
appInsights.loadAppInsights();
24+
25+
// Generate anonymous user ID for session tracking
26+
const userId = sessionStorage.getItem('ai_user') ||
27+
(function() {
28+
// Prefer cryptographically secure random values
29+
let randStr = '';
30+
if (window.crypto && window.crypto.getRandomValues) {
31+
// Generate 16 random bytes, then encode as hex
32+
const buf = new Uint8Array(16);
33+
window.crypto.getRandomValues(buf);
34+
randStr = Array.from(buf, b => b.toString(16).padStart(2, '0')).join('');
35+
} else {
36+
// Fallback if crypto not supported (rare): use Math.random, but warn
37+
randStr = Math.random().toString(36).substring(2, 15);
38+
}
39+
return 'user_' + randStr;
40+
})();
41+
sessionStorage.setItem('ai_user', userId);
42+
appInsights.setAuthenticatedUserContext(userId);
43+
44+
// Collect comprehensive user statistics
45+
const userStats = {
46+
referrer: document.referrer || 'direct',
47+
userAgent: navigator.userAgent,
48+
language: navigator.language,
49+
languages: navigator.languages ? navigator.languages.join(',') : '',
50+
screenResolution: `${screen.width}x${screen.height}`,
51+
colorDepth: screen.colorDepth,
52+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
53+
platform: navigator.platform,
54+
cookieEnabled: navigator.cookieEnabled,
55+
onLine: navigator.onLine,
56+
deviceMemory: navigator.deviceMemory || 'unknown',
57+
connection: navigator.connection ? {
58+
effectiveType: navigator.connection.effectiveType,
59+
downlink: navigator.connection.downlink,
60+
rtt: navigator.connection.rtt
61+
} : 'unknown'
62+
};
63+
64+
// Track page view with enhanced user statistics
65+
appInsights.trackPageView({
66+
name: 'Tsunami Map',
67+
properties: userStats
68+
});
69+
70+
// Track user location (with permission)
71+
if (navigator.geolocation && 'geolocation' in navigator) {
72+
navigator.geolocation.getCurrentPosition(
73+
function(position) {
74+
// Track approximate location (country/region level for privacy)
75+
appInsights.trackEvent('UserLocation', {
76+
latitude: Math.round(position.coords.latitude * 10) / 10, // Rounded for privacy
77+
longitude: Math.round(position.coords.longitude * 10) / 10, // Rounded for privacy
78+
accuracy: position.coords.accuracy > 1000 ? 'low' : 'high'
79+
});
80+
},
81+
function(error) {
82+
// Track location access denied/failed
83+
appInsights.trackEvent('LocationAccessDenied', {
84+
error: error.message || 'unknown'
85+
});
86+
},
87+
{ enableHighAccuracy: false, timeout: 5000, maximumAge: 300000 }
88+
);
89+
}
90+
91+
// Track map interactions
92+
window.trackMapInteraction = function(action, properties) {
93+
appInsights.trackEvent('MapInteraction', {
94+
action: action,
95+
...properties,
96+
timestamp: new Date().toISOString()
97+
});
98+
};
99+
100+
// Track session duration
101+
let sessionStart = Date.now();
102+
window.addEventListener('beforeunload', function() {
103+
const sessionDuration = Math.round((Date.now() - sessionStart) / 1000);
104+
appInsights.trackEvent('SessionEnd', {
105+
durationSeconds: sessionDuration,
106+
userId: userId
107+
});
108+
});
109+
};
110+
111+
document.head.appendChild(script);
112+
})();

0 commit comments

Comments
 (0)