@crimson-education/browser-logger 4.1.4 → 5.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -29
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +22 -41
- package/lib/index.js.map +1 -1
- package/lib/logger/consoleTransport.d.ts +1 -1
- package/lib/logger/consoleTransport.d.ts.map +1 -1
- package/lib/logger/consoleTransport.js +16 -22
- package/lib/logger/consoleTransport.js.map +1 -1
- package/lib/logger/datadogTransport.d.ts +1 -1
- package/lib/logger/datadogTransport.d.ts.map +1 -1
- package/lib/logger/datadogTransport.js +4 -8
- package/lib/logger/datadogTransport.js.map +1 -1
- package/lib/logger/index.js +29 -49
- package/lib/logger/index.js.map +1 -1
- package/lib/logger/index.test.js +16 -18
- package/lib/logger/index.test.js.map +1 -1
- package/lib/logger/utils.js +4 -9
- package/lib/logger/utils.js.map +1 -1
- package/lib/reporters/amplifyReporter.d.ts +3 -3
- package/lib/reporters/amplifyReporter.d.ts.map +1 -1
- package/lib/reporters/amplifyReporter.js +154 -82
- package/lib/reporters/amplifyReporter.js.map +1 -1
- package/lib/reporters/amplifyReporter.test.js +9 -12
- package/lib/reporters/amplifyReporter.test.js.map +1 -1
- package/lib/reporters/datadogReporter.d.ts +1 -1
- package/lib/reporters/datadogReporter.d.ts.map +1 -1
- package/lib/reporters/datadogReporter.js +117 -48
- package/lib/reporters/datadogReporter.js.map +1 -1
- package/lib/reporters/gtmReporter.d.ts +1 -1
- package/lib/reporters/gtmReporter.d.ts.map +1 -1
- package/lib/reporters/gtmReporter.js +1 -5
- package/lib/reporters/gtmReporter.js.map +1 -1
- package/lib/reporters/index.js +46 -71
- package/lib/reporters/index.js.map +1 -1
- package/lib/reporters/logReporter.js +24 -34
- package/lib/reporters/logReporter.js.map +1 -1
- package/lib/types/index.js +2 -18
- package/lib/types/index.js.map +1 -1
- package/lib/types/logger.d.ts +3 -3
- package/lib/types/logger.d.ts.map +1 -1
- package/lib/types/logger.js +2 -5
- package/lib/types/logger.js.map +1 -1
- package/lib/types/reporter.d.ts +6 -6
- package/lib/types/reporter.d.ts.map +1 -1
- package/lib/types/reporter.js +1 -2
- package/lib/utils.js +1 -5
- package/lib/utils.js.map +1 -1
- package/lib/utils.test.js +2 -4
- package/lib/utils.test.js.map +1 -1
- package/package.json +15 -13
- package/src/reporters/amplifyReporter.ts +165 -68
- package/src/reporters/datadogReporter.ts +106 -18
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Amplify } from '@aws-amplify/core';
|
|
2
|
+
import { signIn, signOut, getCurrentUser } from '@aws-amplify/auth';
|
|
3
|
+
import { record } from '@aws-amplify/analytics';
|
|
3
4
|
import { Endpoint, FinalizeRequestMiddleware } from '@aws-sdk/types';
|
|
4
5
|
import { HttpRequest } from '@aws-sdk/protocol-http';
|
|
5
6
|
import { PinpointClient, ServiceInputTypes, ServiceOutputTypes } from '@aws-sdk/client-pinpoint';
|
|
@@ -20,6 +21,134 @@ type AttributeMap = Record<string, string[] | string | null>;
|
|
|
20
21
|
|
|
21
22
|
type AmplifyAutoTrackSource = 'pageView' | 'event' | 'session';
|
|
22
23
|
|
|
24
|
+
// Auto-tracking implementation for Gen2
|
|
25
|
+
class AmplifyAutoTracker {
|
|
26
|
+
private config: AmplifyReporterConfig;
|
|
27
|
+
private allMetadata: AttributeMap;
|
|
28
|
+
private currentUser: ReportUser | null = null;
|
|
29
|
+
private sessionStartTime: number = Date.now();
|
|
30
|
+
private pageViewCount: number = 0;
|
|
31
|
+
private lastPageUrl: string = window.location.href;
|
|
32
|
+
private isPageVisible: boolean = !document.hidden;
|
|
33
|
+
|
|
34
|
+
constructor(config: AmplifyReporterConfig, allMetadata: AttributeMap) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.allMetadata = allMetadata;
|
|
37
|
+
this.setupEventListeners();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private setupEventListeners() {
|
|
41
|
+
// Page visibility tracking
|
|
42
|
+
document.addEventListener('visibilitychange', () => {
|
|
43
|
+
this.isPageVisible = !document.hidden;
|
|
44
|
+
if (this.config.autoTrackSessions) {
|
|
45
|
+
this.trackSessionState();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Page view tracking
|
|
50
|
+
if (this.config.autoTrackPageViews) {
|
|
51
|
+
this.trackPageView();
|
|
52
|
+
|
|
53
|
+
// Track route changes for SPAs
|
|
54
|
+
let currentUrl = window.location.href;
|
|
55
|
+
const observer = new MutationObserver(() => {
|
|
56
|
+
if (window.location.href !== currentUrl) {
|
|
57
|
+
currentUrl = window.location.href;
|
|
58
|
+
this.trackPageView();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// User interaction tracking
|
|
65
|
+
if (this.config.autoTrackEvents) {
|
|
66
|
+
this.setupInteractionTracking();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private trackPageView() {
|
|
71
|
+
this.pageViewCount++;
|
|
72
|
+
const pageViewEvent = {
|
|
73
|
+
message: 'Page View',
|
|
74
|
+
metadata: {
|
|
75
|
+
url: window.location.href,
|
|
76
|
+
title: document.title,
|
|
77
|
+
referrer: document.referrer,
|
|
78
|
+
pageViewCount: this.pageViewCount,
|
|
79
|
+
...this.getAutoTrackMetadata('pageView'),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
record({
|
|
84
|
+
name: pageViewEvent.message,
|
|
85
|
+
attributes: asAttributeMap(pageViewEvent.metadata, false) as Record<string, string>,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private trackSessionState() {
|
|
90
|
+
const sessionDuration = Date.now() - this.sessionStartTime;
|
|
91
|
+
const sessionEvent = {
|
|
92
|
+
message: this.isPageVisible ? 'Session Active' : 'Session Inactive',
|
|
93
|
+
metadata: {
|
|
94
|
+
sessionDuration,
|
|
95
|
+
isVisible: this.isPageVisible,
|
|
96
|
+
...this.getAutoTrackMetadata('session'),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
record({
|
|
101
|
+
name: sessionEvent.message,
|
|
102
|
+
attributes: asAttributeMap(sessionEvent.metadata, false) as Record<string, string>,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private setupInteractionTracking() {
|
|
107
|
+
const selectorPrefix = this.config.selectorPrefix ?? 'data-analytics-';
|
|
108
|
+
|
|
109
|
+
document.addEventListener('click', (event) => {
|
|
110
|
+
const target = event.target as HTMLElement;
|
|
111
|
+
if (!target) return;
|
|
112
|
+
|
|
113
|
+
// Find the closest element with analytics attributes
|
|
114
|
+
const analyticsElement = target.closest(`[${selectorPrefix}name]`);
|
|
115
|
+
if (!analyticsElement) return;
|
|
116
|
+
|
|
117
|
+
const analyticsName = analyticsElement.getAttribute(`${selectorPrefix}name`);
|
|
118
|
+
if (!analyticsName) return;
|
|
119
|
+
|
|
120
|
+
const interactionEvent = {
|
|
121
|
+
message: 'User Interaction',
|
|
122
|
+
metadata: {
|
|
123
|
+
elementName: analyticsName,
|
|
124
|
+
elementType: target.tagName.toLowerCase(),
|
|
125
|
+
elementText: target.textContent?.slice(0, 100),
|
|
126
|
+
interactionType: 'click',
|
|
127
|
+
...this.getAutoTrackMetadata('event'),
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
record({
|
|
132
|
+
name: interactionEvent.message,
|
|
133
|
+
attributes: asAttributeMap(interactionEvent.metadata, false) as Record<string, string>,
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private getAutoTrackMetadata(source: AmplifyAutoTrackSource): Metadata {
|
|
139
|
+
const { beforeAutoTrack } = this.config;
|
|
140
|
+
return typeof beforeAutoTrack === 'function' ? (beforeAutoTrack(source) ?? {}) : {};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public setUser(user: ReportUser | null) {
|
|
144
|
+
this.currentUser = user;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public updateMetadata(metadata: AttributeMap) {
|
|
148
|
+
Object.assign(this.allMetadata, metadata);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
23
152
|
export interface AmplifyReporterConfig extends ReporterConfigBase {
|
|
24
153
|
/**
|
|
25
154
|
* AWS Region for Amplify.
|
|
@@ -97,19 +226,24 @@ type AmplifyReporterBufferingConfig = {
|
|
|
97
226
|
|
|
98
227
|
export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig): IReporter {
|
|
99
228
|
if (config.identityPoolId !== false) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
229
|
+
// Gen2 configuration structure
|
|
230
|
+
const authConfig: any = {
|
|
231
|
+
Cognito: {
|
|
232
|
+
identityPoolId: config.identityPoolId,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// Only add user pool config if both userPoolId and userPoolWebClientId are provided
|
|
237
|
+
if (config.userPoolId && config.userPoolWebClientId) {
|
|
238
|
+
authConfig.Cognito.userPoolId = config.userPoolId;
|
|
239
|
+
authConfig.Cognito.userPoolClientId = config.userPoolWebClientId;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
Amplify.configure({
|
|
243
|
+
Auth: authConfig,
|
|
105
244
|
});
|
|
106
245
|
}
|
|
107
246
|
|
|
108
|
-
const wrapAutoTrackMiddleware = (source: AmplifyAutoTrackSource) => {
|
|
109
|
-
const { beforeAutoTrack } = config;
|
|
110
|
-
return typeof beforeAutoTrack === 'function' ? () => beforeAutoTrack(source) ?? {} : undefined;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
247
|
const allMetadata = asAttributeMap({
|
|
114
248
|
appName: info.service,
|
|
115
249
|
service: info.service,
|
|
@@ -118,40 +252,18 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
118
252
|
version: info.version,
|
|
119
253
|
});
|
|
120
254
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
255
|
+
Amplify.configure({
|
|
256
|
+
Analytics: {
|
|
257
|
+
Pinpoint: {
|
|
258
|
+
appId: config.analyticsAppId,
|
|
259
|
+
region: config.region,
|
|
260
|
+
...config.buffering,
|
|
261
|
+
},
|
|
126
262
|
},
|
|
127
|
-
...config.buffering,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Session autotracking is enabled by default for backwards compatibility reasons, so we _must_
|
|
131
|
-
// call this unconditionally to ensure we opt out of session tracking when `autoTrackSessions` isn't set
|
|
132
|
-
// See: https://docs.amplify.aws/lib/analytics/autotrack/q/platform/js/#session-tracking
|
|
133
|
-
Analytics.autoTrack('session', {
|
|
134
|
-
enable: config.autoTrackSessions === true,
|
|
135
|
-
attributes: wrapAutoTrackMiddleware('session'),
|
|
136
263
|
});
|
|
137
264
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
enable: true,
|
|
141
|
-
eventName: 'pageView',
|
|
142
|
-
type: 'SPA',
|
|
143
|
-
provider: 'AWSPinpoint',
|
|
144
|
-
attributes: wrapAutoTrackMiddleware('pageView'),
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (config.autoTrackEvents) {
|
|
149
|
-
Analytics.autoTrack('event', {
|
|
150
|
-
enable: true,
|
|
151
|
-
selectorPrefix: config.selectorPrefix ?? 'data-analytics-',
|
|
152
|
-
attributes: wrapAutoTrackMiddleware('event'),
|
|
153
|
-
});
|
|
154
|
-
}
|
|
265
|
+
// Initialize auto-tracker for Gen2
|
|
266
|
+
const autoTracker = new AmplifyAutoTracker(config, allMetadata);
|
|
155
267
|
|
|
156
268
|
if (config.proxyUrl) {
|
|
157
269
|
installPinpointProxy(new URL(config.proxyUrl));
|
|
@@ -159,7 +271,7 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
159
271
|
|
|
160
272
|
const reporter: IReporter = {
|
|
161
273
|
trackEvent: function (event: ReporterEvent): void {
|
|
162
|
-
|
|
274
|
+
record({
|
|
163
275
|
name: event.message,
|
|
164
276
|
attributes: asAttributeMap(
|
|
165
277
|
{
|
|
@@ -169,8 +281,6 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
169
281
|
false,
|
|
170
282
|
) as Record<string, string>,
|
|
171
283
|
metrics: event.metrics,
|
|
172
|
-
}).catch(() => {
|
|
173
|
-
// Swallow; see: https://crimsonhq.slack.com/archives/G4UN6Q4KF/p1648599302847539
|
|
174
284
|
});
|
|
175
285
|
},
|
|
176
286
|
addBreadcrumb: function (breadcrumb: ReporterBreadcrumb): void {
|
|
@@ -184,11 +294,9 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
184
294
|
},
|
|
185
295
|
addMetadata: function (metadata: Metadata): void {
|
|
186
296
|
Object.assign(allMetadata, asAttributeMap(metadata, true));
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// Swallow; see: https://crimsonhq.slack.com/archives/G4UN6Q4KF/p1648599302847539
|
|
191
|
-
});
|
|
297
|
+
autoTracker.updateMetadata(asAttributeMap(metadata, true));
|
|
298
|
+
// Note: updateEndpoint is not available in Amplify v7
|
|
299
|
+
// Metadata updates would need to be handled differently
|
|
192
300
|
},
|
|
193
301
|
setUser: function (user: ReportUser | null): void {
|
|
194
302
|
const userMetadata = user
|
|
@@ -197,20 +305,9 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
197
305
|
})
|
|
198
306
|
: {};
|
|
199
307
|
Object.assign(allMetadata, asAttributeMap(userMetadata, true));
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
userAttributes: user
|
|
204
|
-
? asAttributeMap({
|
|
205
|
-
id: user.id,
|
|
206
|
-
email: user.email,
|
|
207
|
-
name: user.name ?? user.email,
|
|
208
|
-
username: user.username,
|
|
209
|
-
})
|
|
210
|
-
: {},
|
|
211
|
-
}).catch(() => {
|
|
212
|
-
// Swallow; see: https://crimsonhq.slack.com/archives/G4UN6Q4KF/p1648599302847539
|
|
213
|
-
});
|
|
308
|
+
autoTracker.setUser(user);
|
|
309
|
+
// Note: updateEndpoint is not available in Amplify v7
|
|
310
|
+
// User updates would need to be handled differently
|
|
214
311
|
},
|
|
215
312
|
setRouteName: function (routeName: string): void {
|
|
216
313
|
reporter.addMetadata({ routeName });
|
|
@@ -232,8 +329,8 @@ export function asAttributeMap(values: Record<string, unknown>, groupValues = tr
|
|
|
232
329
|
const checkedEntries = Object.entries(attributeMap).map(([key, value]) => {
|
|
233
330
|
const truncatedKey = key.length > 50 ? `___${key.slice(-47)}` : key;
|
|
234
331
|
const truncatedValue = Array.isArray(value)
|
|
235
|
-
? value?.map((val) => val.slice(0, 100)) ?? null
|
|
236
|
-
: value?.slice(0, 100) ?? null;
|
|
332
|
+
? (value?.map((val) => val.slice(0, 100)) ?? null)
|
|
333
|
+
: (value?.slice(0, 100) ?? null);
|
|
237
334
|
|
|
238
335
|
return [truncatedKey, truncatedValue];
|
|
239
336
|
});
|
|
@@ -291,7 +388,7 @@ function installPinpointProxy(proxyUrl: URL) {
|
|
|
291
388
|
// The Pinpoint client _also_ isn't available synchronously because it is instantiated when events
|
|
292
389
|
// get sent out, and then reconfigured whenever the API credentials change. We need to hook `_initClients`
|
|
293
390
|
// to ensure that the Pinpoint client being used is always patched with our custom endpoint.
|
|
294
|
-
const provider = Analytics
|
|
391
|
+
const provider = (globalThis as any).Analytics?.getPluggable?.('AWSPinpoint') as any;
|
|
295
392
|
if (!provider || typeof provider._initClients !== 'function') {
|
|
296
393
|
logger.error(
|
|
297
394
|
'Installation of the Pinpoint proxy failed. This likely means the internals of the @aws-amplify/analytics package have changed.',
|
|
@@ -15,6 +15,95 @@ import { datadogRum, DefaultPrivacyLevel, RumInitConfiguration } from '@datadog/
|
|
|
15
15
|
import { logTransports } from '../logger';
|
|
16
16
|
import { DatadogLogTransportConfig, datadogTransport } from '../logger/datadogTransport';
|
|
17
17
|
|
|
18
|
+
// User frustration detection for Datadog Gen2
|
|
19
|
+
class DatadogFrustrationDetector {
|
|
20
|
+
private config: DatadogReporterConfig;
|
|
21
|
+
private frustrationEvents: Array<{ type: string; timestamp: number }> = [];
|
|
22
|
+
private readonly FRUSTRATION_THRESHOLD = 3; // Number of events to trigger frustration
|
|
23
|
+
private readonly FRUSTRATION_WINDOW = 5000; // Time window in ms
|
|
24
|
+
|
|
25
|
+
constructor(config: DatadogReporterConfig) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
if (config.trackFrustrations) {
|
|
28
|
+
this.setupFrustrationDetection();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private setupFrustrationDetection() {
|
|
33
|
+
// Track rapid clicks (potential frustration)
|
|
34
|
+
let clickCount = 0;
|
|
35
|
+
let lastClickTime = 0;
|
|
36
|
+
|
|
37
|
+
document.addEventListener('click', (event) => {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
if (now - lastClickTime < 1000) {
|
|
40
|
+
// Rapid clicks within 1 second
|
|
41
|
+
clickCount++;
|
|
42
|
+
if (clickCount >= 3) {
|
|
43
|
+
this.recordFrustrationEvent('rapid_clicks', event);
|
|
44
|
+
clickCount = 0;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
clickCount = 1;
|
|
48
|
+
}
|
|
49
|
+
lastClickTime = now;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Track form errors (potential frustration)
|
|
53
|
+
document.addEventListener('invalid', (event) => {
|
|
54
|
+
this.recordFrustrationEvent('form_validation_error', event);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Track 404 errors (potential frustration)
|
|
58
|
+
window.addEventListener('error', (event) => {
|
|
59
|
+
if (event.message.includes('404') || event.message.includes('Not Found')) {
|
|
60
|
+
this.recordFrustrationEvent('page_not_found', event);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Track network errors (potential frustration)
|
|
65
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
66
|
+
if (event.reason && typeof event.reason === 'object' && 'status' in event.reason) {
|
|
67
|
+
const status = (event.reason as any).status;
|
|
68
|
+
if (status >= 400) {
|
|
69
|
+
this.recordFrustrationEvent('network_error', event);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private recordFrustrationEvent(type: string, originalEvent: Event) {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
this.frustrationEvents.push({ type, timestamp: now });
|
|
78
|
+
|
|
79
|
+
// Clean old events outside the window
|
|
80
|
+
this.frustrationEvents = this.frustrationEvents.filter((event) => now - event.timestamp < this.FRUSTRATION_WINDOW);
|
|
81
|
+
|
|
82
|
+
// Check if we have enough events to trigger frustration
|
|
83
|
+
if (this.frustrationEvents.length >= this.FRUSTRATION_THRESHOLD) {
|
|
84
|
+
this.triggerFrustrationDetection();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private triggerFrustrationDetection() {
|
|
89
|
+
const frustrationData = {
|
|
90
|
+
frustrationType: 'user_frustration_detected',
|
|
91
|
+
frustrationEvents: this.frustrationEvents.map((e) => e.type),
|
|
92
|
+
frustrationCount: this.frustrationEvents.length,
|
|
93
|
+
pageUrl: window.location.href,
|
|
94
|
+
timestamp: new Date().toISOString(),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Send frustration event to Datadog
|
|
98
|
+
if (typeof datadogRum !== 'undefined') {
|
|
99
|
+
datadogRum.addAction('User Frustration Detected', frustrationData);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Clear events after reporting
|
|
103
|
+
this.frustrationEvents = [];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
18
107
|
export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
19
108
|
/** The RUM application ID. */
|
|
20
109
|
applicationId: string;
|
|
@@ -117,7 +206,7 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
|
117
206
|
/**
|
|
118
207
|
* A list of request origins used to inject tracing headers, to be able to connect RUM and backend tracing.
|
|
119
208
|
*/
|
|
120
|
-
|
|
209
|
+
allowedTrackingOrigins?: (string | RegExp)[];
|
|
121
210
|
/**
|
|
122
211
|
* Enables action tracking for user interactions. This enables the Heatmap tab within Datadog
|
|
123
212
|
*/
|
|
@@ -142,18 +231,17 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
142
231
|
// Only init datadog logs if something is using it.
|
|
143
232
|
if (forwardErrorsToLogs !== true && enableLogTransport !== true) {
|
|
144
233
|
datadogLogs.init({
|
|
145
|
-
site: config.site,
|
|
146
|
-
proxyUrl: config.proxyUrl,
|
|
234
|
+
site: config.site as any,
|
|
147
235
|
clientToken: config.clientToken,
|
|
148
236
|
service: info.service,
|
|
149
237
|
env: info.environment,
|
|
150
238
|
version: config.version ?? info.version,
|
|
151
239
|
|
|
152
|
-
|
|
240
|
+
// Note: sampleRate is not available in newer Datadog versions
|
|
153
241
|
beforeSend: config.beforeLogsSend,
|
|
154
242
|
|
|
155
243
|
useSecureSessionCookie: config.useSecureSessionCookie ?? !isLocalhost,
|
|
156
|
-
|
|
244
|
+
// Note: useCrossSiteSessionCookie is not available in newer Datadog versions
|
|
157
245
|
trackSessionAcrossSubdomains: config.trackSessionAcrossSubdomains,
|
|
158
246
|
|
|
159
247
|
forwardErrorsToLogs,
|
|
@@ -168,34 +256,34 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
168
256
|
|
|
169
257
|
datadogRum.init({
|
|
170
258
|
enableExperimentalFeatures: ['feature_flags'],
|
|
171
|
-
site: config.site,
|
|
172
|
-
proxyUrl: config.proxyUrl,
|
|
259
|
+
site: config.site as any,
|
|
173
260
|
clientToken: config.clientToken,
|
|
174
261
|
applicationId: config.applicationId,
|
|
175
262
|
service: info.service,
|
|
176
263
|
env: info.environment,
|
|
177
264
|
version: config.version ?? info.version,
|
|
178
265
|
|
|
179
|
-
|
|
180
|
-
|
|
266
|
+
// Note: sampleRate is not available in newer Datadog versions
|
|
267
|
+
// Note: replaySampleRate is not available in newer Datadog versions
|
|
181
268
|
|
|
182
269
|
useSecureSessionCookie: config.useSecureSessionCookie ?? !isLocalhost,
|
|
183
|
-
|
|
270
|
+
// Note: useCrossSiteSessionCookie is not available in newer Datadog versions
|
|
184
271
|
trackSessionAcrossSubdomains: config.trackSessionAcrossSubdomains,
|
|
185
272
|
|
|
186
|
-
trackInteractions: config.trackInteractions ?? false,
|
|
187
|
-
trackFrustrations: config.trackFrustrations ?? false,
|
|
188
273
|
trackViewsManually: config.trackViewsManually ?? false,
|
|
189
274
|
actionNameAttribute: config.actionNameAttribute ?? 'data-analytics-name',
|
|
190
275
|
beforeSend: config.beforeSend,
|
|
191
276
|
|
|
192
277
|
defaultPrivacyLevel: config.defaultPrivacyLevel ?? 'mask-user-input',
|
|
193
|
-
|
|
278
|
+
allowedTrackingOrigins: config.allowedTrackingOrigins,
|
|
194
279
|
trackUserInteractions: config.trackUserInteractions ?? false,
|
|
195
280
|
|
|
196
281
|
excludedActivityUrls: config.excludedActivityUrls,
|
|
197
282
|
});
|
|
198
283
|
|
|
284
|
+
// Initialize frustration detector
|
|
285
|
+
const frustrationDetector = new DatadogFrustrationDetector(config);
|
|
286
|
+
|
|
199
287
|
const reporter: IReporter = {
|
|
200
288
|
trackEvent: function (event: ReporterEvent): void {
|
|
201
289
|
datadogRum.addAction(event.message, {
|
|
@@ -214,16 +302,16 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
214
302
|
addMetadata: function (metadata: Metadata): void {
|
|
215
303
|
for (const [key, value] of Object.entries(metadata)) {
|
|
216
304
|
if (value !== null) {
|
|
217
|
-
datadogRum.
|
|
305
|
+
datadogRum.setGlobalContextProperty(key, value);
|
|
218
306
|
|
|
219
307
|
// Note, this will add duplicate context data in logs.
|
|
220
308
|
// But this is valuable for logs ingested outside of the browser logger.
|
|
221
|
-
datadogLogs.
|
|
309
|
+
datadogLogs.setGlobalContextProperty(key, value);
|
|
222
310
|
} else {
|
|
223
|
-
datadogRum.
|
|
311
|
+
datadogRum.removeGlobalContextProperty(key);
|
|
224
312
|
|
|
225
313
|
// But this is valuable for logs ingested outside of the browser logger.
|
|
226
|
-
datadogLogs.
|
|
314
|
+
datadogLogs.removeGlobalContextProperty(key);
|
|
227
315
|
}
|
|
228
316
|
}
|
|
229
317
|
},
|
|
@@ -236,7 +324,7 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
236
324
|
name: user.name ?? user.email,
|
|
237
325
|
});
|
|
238
326
|
} else {
|
|
239
|
-
datadogRum.
|
|
327
|
+
datadogRum.setUser({});
|
|
240
328
|
}
|
|
241
329
|
},
|
|
242
330
|
setRouteName: function (routeName: string): void {
|