@crimson-education/browser-logger 2.0.2 → 3.0.1-pinpoint-middleware.0
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 +212 -67
- package/lib/index.d.ts +12 -82
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +69 -213
- package/lib/index.js.map +1 -1
- package/lib/logger/consoleTransport.d.ts +37 -0
- package/lib/logger/consoleTransport.d.ts.map +1 -0
- package/lib/logger/consoleTransport.js +81 -0
- package/lib/logger/consoleTransport.js.map +1 -0
- package/lib/logger/datadogTransport.d.ts +8 -0
- package/lib/logger/datadogTransport.d.ts.map +1 -0
- package/lib/logger/datadogTransport.js +21 -0
- package/lib/logger/datadogTransport.js.map +1 -0
- package/lib/logger/index.d.ts +16 -0
- package/lib/logger/index.d.ts.map +1 -0
- package/lib/logger/index.js +148 -0
- package/lib/logger/index.js.map +1 -0
- package/lib/logger/index.test.d.ts +2 -0
- package/lib/logger/index.test.d.ts.map +1 -0
- package/lib/logger/index.test.js +60 -0
- package/lib/logger/index.test.js.map +1 -0
- package/lib/logger/utils.d.ts +15 -0
- package/lib/logger/utils.d.ts.map +1 -0
- package/lib/logger/utils.js +32 -0
- package/lib/logger/utils.js.map +1 -0
- package/lib/reporters/amplifyReporter.d.ts +83 -77
- package/lib/reporters/amplifyReporter.d.ts.map +1 -1
- package/lib/reporters/amplifyReporter.js +162 -151
- package/lib/reporters/amplifyReporter.js.map +1 -1
- package/lib/reporters/amplifyReporter.test.d.ts +1 -1
- package/lib/reporters/amplifyReporter.test.js +50 -50
- package/lib/reporters/datadogReporter.d.ts +76 -74
- package/lib/reporters/datadogReporter.d.ts.map +1 -1
- package/lib/reporters/datadogReporter.js +122 -185
- package/lib/reporters/datadogReporter.js.map +1 -1
- package/lib/reporters/gtmReporter.d.ts +8 -8
- package/lib/reporters/gtmReporter.d.ts.map +1 -1
- package/lib/reporters/gtmReporter.js +56 -61
- package/lib/reporters/gtmReporter.js.map +1 -1
- package/lib/reporters/index.d.ts +66 -3
- package/lib/reporters/index.d.ts.map +1 -1
- package/lib/reporters/index.js +212 -19
- package/lib/reporters/index.js.map +1 -1
- package/lib/reporters/logReporter.d.ts +35 -0
- package/lib/reporters/logReporter.d.ts.map +1 -0
- package/lib/reporters/logReporter.js +62 -0
- package/lib/reporters/logReporter.js.map +1 -0
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.js +18 -18
- package/lib/types/logger.d.ts +77 -36
- package/lib/types/logger.d.ts.map +1 -1
- package/lib/types/logger.js +10 -10
- package/lib/types/logger.js.map +1 -1
- package/lib/types/reporter.d.ts +154 -102
- package/lib/types/reporter.d.ts.map +1 -1
- package/lib/types/reporter.js +2 -2
- package/lib/utils.d.ts +9 -8
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +31 -51
- package/lib/utils.js.map +1 -1
- package/lib/utils.test.d.ts +1 -1
- package/lib/utils.test.js +31 -18
- package/lib/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +38 -208
- package/src/logger/consoleTransport.ts +101 -0
- package/src/logger/datadogTransport.ts +20 -0
- package/src/logger/index.test.ts +68 -0
- package/src/logger/index.ts +139 -0
- package/src/logger/utils.ts +28 -0
- package/src/reporters/amplifyReporter.ts +37 -17
- package/src/reporters/datadogReporter.ts +48 -116
- package/src/reporters/gtmReporter.ts +2 -7
- package/src/reporters/index.ts +216 -3
- package/src/reporters/logReporter.ts +86 -0
- package/src/types/logger.ts +49 -4
- package/src/types/reporter.ts +66 -6
- package/src/utils.test.ts +20 -6
- package/src/utils.ts +37 -62
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { LogLevel } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a log level is above the global log level.
|
|
5
|
+
* @param level The log level to check
|
|
6
|
+
* @param checkLevel THe log level to check against
|
|
7
|
+
* @returns Is above the checkLevel?
|
|
8
|
+
*/
|
|
9
|
+
export function isAboveLevel(level: LogLevel, checkLevel: LogLevel): boolean {
|
|
10
|
+
const levels = [LogLevel.Debug, LogLevel.Info, LogLevel.Warn, LogLevel.Error];
|
|
11
|
+
return levels.indexOf(level) >= levels.indexOf(checkLevel);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Ensures a message output is a string.
|
|
16
|
+
* @param message The message to assert is/convert to a string
|
|
17
|
+
* @returns The message as a string
|
|
18
|
+
*/
|
|
19
|
+
export function getLogMessage(message: any): string {
|
|
20
|
+
if (typeof message === 'string') {
|
|
21
|
+
return message;
|
|
22
|
+
}
|
|
23
|
+
if (typeof message === 'object' && message !== null) {
|
|
24
|
+
const data = { ...message };
|
|
25
|
+
return JSON.stringify(data);
|
|
26
|
+
}
|
|
27
|
+
return String(message);
|
|
28
|
+
}
|
|
@@ -9,7 +9,12 @@ import {
|
|
|
9
9
|
} from '../types';
|
|
10
10
|
import { Auth } from '@aws-amplify/auth';
|
|
11
11
|
import { Analytics } from '@aws-amplify/analytics';
|
|
12
|
-
import {
|
|
12
|
+
import { logger } from '../logger';
|
|
13
|
+
|
|
14
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
15
|
+
type AttributeMap = Record<string, string[] | string | null>;
|
|
16
|
+
|
|
17
|
+
type AmplifyAutoTrackSource = 'pageView' | 'event' | 'session';
|
|
13
18
|
|
|
14
19
|
export interface AmplifyReporterConfig extends ReporterConfigBase {
|
|
15
20
|
/**
|
|
@@ -51,6 +56,11 @@ export interface AmplifyReporterConfig extends ReporterConfigBase {
|
|
|
51
56
|
* To keep it simple we define that the web session is active when the page is not hidden and inactive when the page is hidden.
|
|
52
57
|
*/
|
|
53
58
|
autoTrackSessions?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Optional function to run before autotracked analytics events are sent out.
|
|
61
|
+
* The returned metadata is attached to the event.
|
|
62
|
+
*/
|
|
63
|
+
beforeAutoTrack?(source: AmplifyAutoTrackSource): Metadata;
|
|
54
64
|
|
|
55
65
|
/**
|
|
56
66
|
* The data tag prefix to use for attributing HTML elements. Defaults to data-analytics-
|
|
@@ -89,6 +99,11 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
89
99
|
});
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
const wrapAutoTrackMiddleware = (source: AmplifyAutoTrackSource) => {
|
|
103
|
+
const { beforeAutoTrack } = config;
|
|
104
|
+
return typeof beforeAutoTrack === 'function' ? () => beforeAutoTrack(source) : undefined;
|
|
105
|
+
};
|
|
106
|
+
|
|
92
107
|
const allMetadata = asAttributeMap({
|
|
93
108
|
appName: info.service,
|
|
94
109
|
service: info.service,
|
|
@@ -100,19 +115,27 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
100
115
|
Analytics.configure({
|
|
101
116
|
region: config.region,
|
|
102
117
|
appId: config.analyticsAppId,
|
|
103
|
-
autoSessionRecord: config.autoTrackSessions,
|
|
104
118
|
endpoint: {
|
|
105
119
|
attributes: allMetadata,
|
|
106
120
|
},
|
|
107
121
|
...config.buffering,
|
|
108
122
|
});
|
|
109
123
|
|
|
124
|
+
// Session autotracking is enabled by default for backwards compatibility reasons, so we _must_
|
|
125
|
+
// call this unconditionally to ensure we opt out of session tracking when `autoTrackSessions` isn't set
|
|
126
|
+
// See: https://docs.amplify.aws/lib/analytics/autotrack/q/platform/js/#session-tracking
|
|
127
|
+
Analytics.autoTrack('session', {
|
|
128
|
+
enable: config.autoTrackSessions === true,
|
|
129
|
+
attributes: wrapAutoTrackMiddleware('session'),
|
|
130
|
+
});
|
|
131
|
+
|
|
110
132
|
if (config.autoTrackPageViews) {
|
|
111
133
|
Analytics.autoTrack('pageView', {
|
|
112
134
|
enable: true,
|
|
113
135
|
eventName: 'pageView',
|
|
114
136
|
type: 'SPA',
|
|
115
137
|
provider: 'AWSPinpoint',
|
|
138
|
+
attributes: wrapAutoTrackMiddleware('pageView'),
|
|
116
139
|
});
|
|
117
140
|
}
|
|
118
141
|
|
|
@@ -120,6 +143,7 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
120
143
|
Analytics.autoTrack('event', {
|
|
121
144
|
enable: true,
|
|
122
145
|
selectorPrefix: config.selectorPrefix ?? 'data-analytics-',
|
|
146
|
+
attributes: wrapAutoTrackMiddleware('event'),
|
|
123
147
|
});
|
|
124
148
|
}
|
|
125
149
|
|
|
@@ -133,16 +157,11 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
133
157
|
...event.tags,
|
|
134
158
|
},
|
|
135
159
|
false,
|
|
136
|
-
config.ignoreMetadataPatterns,
|
|
137
160
|
) as Record<string, string>,
|
|
138
161
|
metrics: event.metrics,
|
|
139
162
|
});
|
|
140
163
|
},
|
|
141
164
|
addBreadcrumb: function (breadcrumb: ReporterBreadcrumb): void {
|
|
142
|
-
if (breadcrumb.category && config.ignoreBreadcrumbCategories?.includes(breadcrumb.category)) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
165
|
reporter.trackEvent({
|
|
147
166
|
message: breadcrumb.message,
|
|
148
167
|
metadata: {
|
|
@@ -152,7 +171,7 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
152
171
|
});
|
|
153
172
|
},
|
|
154
173
|
addMetadata: function (metadata: Metadata): void {
|
|
155
|
-
Object.assign(allMetadata, asAttributeMap(metadata, true
|
|
174
|
+
Object.assign(allMetadata, asAttributeMap(metadata, true));
|
|
156
175
|
Analytics.updateEndpoint({
|
|
157
176
|
attributes: allMetadata,
|
|
158
177
|
}).catch(() => {
|
|
@@ -188,16 +207,10 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
188
207
|
/**
|
|
189
208
|
* Pinpoint has strict attribute name and value length limits
|
|
190
209
|
*/
|
|
191
|
-
|
|
192
|
-
export function asAttributeMap(
|
|
193
|
-
values: Record<string, unknown>,
|
|
194
|
-
groupValues = true,
|
|
195
|
-
ignorePatterns: (string | RegExp)[] = [],
|
|
196
|
-
): AttributeMap {
|
|
210
|
+
export function asAttributeMap(values: Record<string, unknown>, groupValues = true): AttributeMap {
|
|
197
211
|
const attributeMap = buildAttributeMap(values, undefined, groupValues);
|
|
198
|
-
const filteredAttributeMap = filterAttributeMap(attributeMap, ignorePatterns);
|
|
199
212
|
|
|
200
|
-
const checkedEntries = Object.entries(
|
|
213
|
+
const checkedEntries = Object.entries(attributeMap).map(([key, value]) => {
|
|
201
214
|
const truncatedKey = key.length > 50 ? `___${key.slice(-47)}` : key;
|
|
202
215
|
const truncatedValue = Array.isArray(value)
|
|
203
216
|
? value?.map((val) => val.slice(0, 100)) ?? null
|
|
@@ -206,6 +219,14 @@ export function asAttributeMap(
|
|
|
206
219
|
return [truncatedKey, truncatedValue];
|
|
207
220
|
});
|
|
208
221
|
|
|
222
|
+
// Pinpoint only accepts 40 attributes
|
|
223
|
+
if (checkedEntries.length > 40) {
|
|
224
|
+
logger.error(`Amplify only allows 40 attributes per event, truncating to 40 attributes`, {
|
|
225
|
+
attributes: checkedEntries,
|
|
226
|
+
});
|
|
227
|
+
checkedEntries.length = 40;
|
|
228
|
+
}
|
|
229
|
+
|
|
209
230
|
return Object.fromEntries(checkedEntries);
|
|
210
231
|
}
|
|
211
232
|
|
|
@@ -215,7 +236,6 @@ export function asAttributeMap(
|
|
|
215
236
|
* all of its values are of type `string[]` to appease Pinpoint.
|
|
216
237
|
*/
|
|
217
238
|
export function buildAttributeMap(
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
219
239
|
values: Record<string, any>,
|
|
220
240
|
parentKey: string | undefined = undefined,
|
|
221
241
|
groupValues = true,
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
1
|
import {
|
|
3
2
|
IReporter,
|
|
4
3
|
ReporterBreadcrumb,
|
|
5
4
|
ReporterEvent,
|
|
6
5
|
Metadata,
|
|
7
|
-
Profiler,
|
|
8
6
|
ReportError,
|
|
9
7
|
ReportUser,
|
|
10
8
|
ServiceInfo,
|
|
11
9
|
ReporterConfigBase,
|
|
12
10
|
} from '../types';
|
|
13
|
-
import { datadogLogs, HandlerType
|
|
11
|
+
import { datadogLogs, HandlerType } from '@datadog/browser-logs';
|
|
14
12
|
import { datadogRum, DefaultPrivacyLevel } from '@datadog/browser-rum';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
13
|
+
import { logTransports } from '../logger';
|
|
14
|
+
import { DatadogLogTransportConfig, datadogTransport } from '../logger/datadogTransport';
|
|
17
15
|
|
|
18
16
|
export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
19
17
|
/** The RUM application ID. */
|
|
@@ -75,6 +73,12 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
|
75
73
|
*/
|
|
76
74
|
actionNameAttribute?: string;
|
|
77
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Toggles/configures the Datadog Log Transport.
|
|
78
|
+
* Defaults to true.
|
|
79
|
+
*/
|
|
80
|
+
logTransport?: boolean | DatadogLogTransportConfig;
|
|
81
|
+
|
|
78
82
|
/**
|
|
79
83
|
* By enabling Session Replay, you can automatically mask sensitive elements from being recorded through the RUM Browser SDK.
|
|
80
84
|
*
|
|
@@ -87,42 +91,39 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
|
|
|
87
91
|
allowedTracingOrigins?: (string | RegExp)[];
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
function getDatadogStatusType(level: LogLevel): StatusType {
|
|
91
|
-
switch (level) {
|
|
92
|
-
case LogLevel.Error:
|
|
93
|
-
return StatusType.error;
|
|
94
|
-
|
|
95
|
-
case LogLevel.Warn:
|
|
96
|
-
return StatusType.warn;
|
|
97
|
-
|
|
98
|
-
case LogLevel.Info:
|
|
99
|
-
return StatusType.info;
|
|
100
|
-
|
|
101
|
-
case LogLevel.Debug:
|
|
102
|
-
default:
|
|
103
|
-
return StatusType.debug;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
94
|
export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig): IReporter {
|
|
108
95
|
const isLocalhost = window.location.hostname === 'localhost';
|
|
109
96
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
97
|
+
// Don't forward error logs by default, do enable the log transport by default
|
|
98
|
+
// forwardErrorsToLogs incorrectly is called forwardConsoleLogs, this is for backwards compatibility
|
|
99
|
+
const forwardErrorsToLogs = config.forwardConsoleLogs ?? false;
|
|
100
|
+
const enableLogTransport = config.logTransport !== false;
|
|
101
|
+
|
|
102
|
+
// Only init datadog logs if something is using it.
|
|
103
|
+
if (forwardErrorsToLogs !== true && enableLogTransport !== true) {
|
|
104
|
+
datadogLogs.init({
|
|
105
|
+
site: config.site,
|
|
106
|
+
proxyUrl: config.proxyUrl,
|
|
107
|
+
clientToken: config.clientToken,
|
|
108
|
+
service: info.service,
|
|
109
|
+
env: info.environment,
|
|
110
|
+
version: config.version ?? info.version,
|
|
111
|
+
|
|
112
|
+
sampleRate: config.sampleRate ?? 100,
|
|
113
|
+
|
|
114
|
+
useSecureSessionCookie: config.useSecureSessionCookie ?? !isLocalhost,
|
|
115
|
+
useCrossSiteSessionCookie: config.useCrossSiteSessionCookie ?? false,
|
|
116
|
+
trackSessionAcrossSubdomains: config.trackSessionAcrossSubdomains,
|
|
117
|
+
|
|
118
|
+
forwardErrorsToLogs,
|
|
119
|
+
});
|
|
120
|
+
datadogLogs.logger.setHandler(HandlerType.http);
|
|
121
|
+
}
|
|
123
122
|
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
// Add the datadog log transport
|
|
124
|
+
if (enableLogTransport) {
|
|
125
|
+
logTransports.push(datadogTransport(typeof config.logTransport === 'boolean' ? {} : config.logTransport));
|
|
126
|
+
}
|
|
126
127
|
|
|
127
128
|
datadogRum.init({
|
|
128
129
|
site: config.site,
|
|
@@ -149,39 +150,33 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
149
150
|
allowedTracingOrigins: config.allowedTracingOrigins,
|
|
150
151
|
});
|
|
151
152
|
|
|
152
|
-
const ignoreMetadataPatterns = config.ignoreMetadataPatterns ?? [];
|
|
153
|
-
|
|
154
153
|
const reporter: IReporter = {
|
|
155
154
|
trackEvent: function (event: ReporterEvent): void {
|
|
156
|
-
|
|
155
|
+
datadogRum.addAction(event.message, {
|
|
157
156
|
level: event.level,
|
|
158
|
-
...
|
|
157
|
+
...event.metadata,
|
|
159
158
|
...event.tags,
|
|
160
159
|
...event.metrics,
|
|
161
|
-
};
|
|
162
|
-
datadogRum.addAction(event.message, context);
|
|
163
|
-
datadogLogs.logger?.log(event.message, context, getDatadogStatusType(event.level ?? LogLevel.Info));
|
|
160
|
+
});
|
|
164
161
|
},
|
|
165
162
|
addBreadcrumb: function (breadcrumb: ReporterBreadcrumb): void {
|
|
166
|
-
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const context = {
|
|
163
|
+
datadogRum.addAction(breadcrumb.message, {
|
|
171
164
|
...breadcrumb.metadata,
|
|
172
165
|
category: breadcrumb.category,
|
|
173
|
-
};
|
|
174
|
-
datadogRum.addAction(breadcrumb.message, context);
|
|
166
|
+
});
|
|
175
167
|
},
|
|
176
168
|
addMetadata: function (metadata: Metadata): void {
|
|
177
|
-
metadata = filterAttributeMap(metadata, ignoreMetadataPatterns);
|
|
178
|
-
|
|
179
169
|
for (const [key, value] of Object.entries(metadata)) {
|
|
180
170
|
if (value !== null) {
|
|
181
171
|
datadogRum.addRumGlobalContext(key, value);
|
|
172
|
+
|
|
173
|
+
// Note, this will add duplicate context data in logs.
|
|
174
|
+
// But this is valuable for logs ingested outside of the browser logger.
|
|
182
175
|
datadogLogs.addLoggerGlobalContext(key, value);
|
|
183
176
|
} else {
|
|
184
177
|
datadogRum.removeRumGlobalContext(key);
|
|
178
|
+
|
|
179
|
+
// But this is valuable for logs ingested outside of the browser logger.
|
|
185
180
|
datadogLogs.removeLoggerGlobalContext(key);
|
|
186
181
|
}
|
|
187
182
|
}
|
|
@@ -193,16 +188,8 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
193
188
|
email: user.email,
|
|
194
189
|
name: user.name ?? user.email,
|
|
195
190
|
});
|
|
196
|
-
|
|
197
|
-
datadogLogs.addLoggerGlobalContext('user_id', user.id);
|
|
198
|
-
datadogLogs.addLoggerGlobalContext('user_email', user.email);
|
|
199
|
-
datadogLogs.addLoggerGlobalContext('user_username', user.username);
|
|
200
191
|
} else {
|
|
201
192
|
datadogRum.removeUser();
|
|
202
|
-
|
|
203
|
-
datadogLogs.removeLoggerGlobalContext('user_id');
|
|
204
|
-
datadogLogs.removeLoggerGlobalContext('user_email');
|
|
205
|
-
datadogLogs.removeLoggerGlobalContext('user_username');
|
|
206
193
|
}
|
|
207
194
|
},
|
|
208
195
|
setRouteName: function (routeName: string): void {
|
|
@@ -216,9 +203,6 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
216
203
|
}
|
|
217
204
|
},
|
|
218
205
|
reportError: function (error: ReportError, metadata?: Metadata): void {
|
|
219
|
-
metadata = filterAttributeMap(metadata ?? {}, ignoreMetadataPatterns);
|
|
220
|
-
|
|
221
|
-
// Note, datadog should pick up the console error above
|
|
222
206
|
datadogRum.addError(error, metadata);
|
|
223
207
|
},
|
|
224
208
|
recordSession: function (): void {
|
|
@@ -230,55 +214,3 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
|
|
|
230
214
|
};
|
|
231
215
|
return reporter;
|
|
232
216
|
}
|
|
233
|
-
|
|
234
|
-
export function datadogLogger(name?: string, options?: { metadata?: Metadata }): ILogger {
|
|
235
|
-
const loggerName = name ?? 'root';
|
|
236
|
-
const ddLogger = datadogLogs.createLogger(loggerName, {
|
|
237
|
-
context: options?.metadata,
|
|
238
|
-
});
|
|
239
|
-
// Send to datadog and console.
|
|
240
|
-
ddLogger.setHandler([HandlerType.http, HandlerType.console]);
|
|
241
|
-
|
|
242
|
-
const logger: ILogger = {
|
|
243
|
-
startTimer: function (): Profiler {
|
|
244
|
-
const start = new Date();
|
|
245
|
-
|
|
246
|
-
return {
|
|
247
|
-
logger: this,
|
|
248
|
-
done: ({ message, level } = {}) => {
|
|
249
|
-
const duration = new Date().getTime() - start.getTime();
|
|
250
|
-
logger[level ?? LogLevel.Info](message ?? 'Timer Completed', { duration });
|
|
251
|
-
return true;
|
|
252
|
-
},
|
|
253
|
-
};
|
|
254
|
-
},
|
|
255
|
-
child: function (metadata?: Metadata, name?: string): ILogger {
|
|
256
|
-
return datadogLogger(name ?? `${loggerName}.child`, metadata);
|
|
257
|
-
},
|
|
258
|
-
log: (level: LogLevel, message: string, metadata?: any) => {
|
|
259
|
-
switch (level) {
|
|
260
|
-
case LogLevel.Debug:
|
|
261
|
-
ddLogger.debug(message, metadata);
|
|
262
|
-
break;
|
|
263
|
-
|
|
264
|
-
case LogLevel.Info:
|
|
265
|
-
ddLogger.info(message, metadata);
|
|
266
|
-
break;
|
|
267
|
-
|
|
268
|
-
case LogLevel.Warn:
|
|
269
|
-
ddLogger.warn(message, metadata);
|
|
270
|
-
break;
|
|
271
|
-
|
|
272
|
-
case LogLevel.Error:
|
|
273
|
-
ddLogger.error(message, metadata);
|
|
274
|
-
break;
|
|
275
|
-
}
|
|
276
|
-
return logger;
|
|
277
|
-
},
|
|
278
|
-
debug: (message: string, metadata?: any) => logger.log(LogLevel.Debug, message, metadata),
|
|
279
|
-
info: (message: string, metadata?: any) => logger.log(LogLevel.Info, message, metadata),
|
|
280
|
-
warn: (message: string, metadata?: any) => logger.log(LogLevel.Warn, message, metadata),
|
|
281
|
-
error: (message: string, metadata?: any) => logger.log(LogLevel.Error, message, metadata),
|
|
282
|
-
};
|
|
283
|
-
return logger;
|
|
284
|
-
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
1
|
import {
|
|
3
2
|
IReporter,
|
|
4
3
|
ReporterBreadcrumb,
|
|
@@ -8,7 +7,6 @@ import {
|
|
|
8
7
|
ServiceInfo,
|
|
9
8
|
ReporterConfigBase,
|
|
10
9
|
} from '../types';
|
|
11
|
-
import { filterAttributeMap } from '../utils';
|
|
12
10
|
|
|
13
11
|
export type GTMReporterConfig = ReporterConfigBase;
|
|
14
12
|
|
|
@@ -28,6 +26,7 @@ export function gtmReporter(info: ServiceInfo, config: GTMReporterConfig): IRepo
|
|
|
28
26
|
};
|
|
29
27
|
|
|
30
28
|
const reporter: IReporter = {
|
|
29
|
+
...config,
|
|
31
30
|
addMetadata: function (metadata: Metadata): void {
|
|
32
31
|
if (window.dataLayer) {
|
|
33
32
|
// Lazy load base metadata, e.g. if GTM isn't ready yet.
|
|
@@ -36,7 +35,7 @@ export function gtmReporter(info: ServiceInfo, config: GTMReporterConfig): IRepo
|
|
|
36
35
|
loadedDataLayer = true;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
window.dataLayer.push(
|
|
38
|
+
window.dataLayer.push(metadata);
|
|
40
39
|
}
|
|
41
40
|
},
|
|
42
41
|
trackEvent: function (event: ReporterEvent): void {
|
|
@@ -49,10 +48,6 @@ export function gtmReporter(info: ServiceInfo, config: GTMReporterConfig): IRepo
|
|
|
49
48
|
});
|
|
50
49
|
},
|
|
51
50
|
addBreadcrumb: function (breadcrumb: ReporterBreadcrumb): void {
|
|
52
|
-
if (config.ignoreBreadcrumbCategories?.includes(breadcrumb.category ?? '')) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
51
|
reporter.addMetadata({
|
|
57
52
|
...breadcrumb.metadata,
|
|
58
53
|
event: breadcrumb.message,
|
package/src/reporters/index.ts
CHANGED
|
@@ -1,3 +1,216 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { isAboveLevel } from '../logger/utils';
|
|
2
|
+
import {
|
|
3
|
+
IReporter,
|
|
4
|
+
LogLevel,
|
|
5
|
+
Metadata,
|
|
6
|
+
ReporterBreadcrumb,
|
|
7
|
+
ReporterEvent,
|
|
8
|
+
ReporterFilters,
|
|
9
|
+
ReportError,
|
|
10
|
+
ReporterType,
|
|
11
|
+
ReportUser,
|
|
12
|
+
TrackedReporterEvent,
|
|
13
|
+
} from '../types';
|
|
14
|
+
import { filterReporterMetadata } from '../utils';
|
|
15
|
+
|
|
16
|
+
export const reporters: Partial<Record<ReporterType, IReporter>> = {};
|
|
17
|
+
|
|
18
|
+
export let globalEventLevel: LogLevel | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Sets the global event level.
|
|
21
|
+
*/
|
|
22
|
+
export function setEventLevel(level: LogLevel | null): void {
|
|
23
|
+
globalEventLevel = level ?? undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gets reporters, optionally with filters.
|
|
28
|
+
* @param filters Filters to remove or specify specific reporters.
|
|
29
|
+
* @returns Selected reporters, or all registered reporters.
|
|
30
|
+
*/
|
|
31
|
+
function getReporters(filters?: ReporterFilters) {
|
|
32
|
+
let result = Object.entries(reporters);
|
|
33
|
+
if (filters?.excludeReporters) {
|
|
34
|
+
result = result.filter(([key]) => !filters?.excludeReporters?.includes(key as ReporterType));
|
|
35
|
+
}
|
|
36
|
+
if (filters?.toReporters) {
|
|
37
|
+
result = result.filter(([key]) => filters?.toReporters?.includes(key as ReporterType));
|
|
38
|
+
}
|
|
39
|
+
return result.map(([, reporter]) => reporter);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Tracks an Analytics event.
|
|
44
|
+
*/
|
|
45
|
+
export function trackEvent(event: ReporterEvent): void {
|
|
46
|
+
if (globalEventLevel && event.level && !isAboveLevel(event.level, globalEventLevel)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const reporter of getReporters(event)) {
|
|
51
|
+
if (reporter.endpoints?.trackEvent === false) {
|
|
52
|
+
continue;
|
|
53
|
+
} else if (reporter.eventLevel && event.level && !isAboveLevel(event.level, reporter.eventLevel)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const reporterEvent = { ...event };
|
|
58
|
+
reporterEvent.metadata = filterReporterMetadata(event.metadata, reporter);
|
|
59
|
+
reporterEvent.metrics = filterReporterMetadata(event.metrics, reporter);
|
|
60
|
+
reporterEvent.tags = filterReporterMetadata(event.tags, reporter);
|
|
61
|
+
reporter.trackEvent(reporterEvent);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Tracks an Analytics event, recording the time since the last event, if available.
|
|
67
|
+
*/
|
|
68
|
+
export function trackEventSinceLastAction(event: ReporterEvent): void {
|
|
69
|
+
if (globalEventLevel && event.level && !isAboveLevel(event.level, globalEventLevel)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const lastEvent = getLastTrackedEvent();
|
|
74
|
+
if (lastEvent) {
|
|
75
|
+
const duration = new Date().getTime() - lastEvent.occurred.getTime();
|
|
76
|
+
trackEvent({
|
|
77
|
+
...event,
|
|
78
|
+
metadata: {
|
|
79
|
+
...event.metadata,
|
|
80
|
+
lastEventName: lastEvent.message,
|
|
81
|
+
timeSinceLastEvent: duration,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
trackEvent(event);
|
|
86
|
+
}
|
|
87
|
+
sessionStorage.setItem('loggerLastEvent', JSON.stringify({ ...event, occurred: new Date() }));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Gets the last tracked event, if available.
|
|
92
|
+
*/
|
|
93
|
+
export function getLastTrackedEvent(): TrackedReporterEvent | null {
|
|
94
|
+
const eventStr = sessionStorage.getItem('loggerLastEvent');
|
|
95
|
+
if (!eventStr) return null;
|
|
96
|
+
|
|
97
|
+
const event: TrackedReporterEvent = JSON.parse(eventStr);
|
|
98
|
+
event.occurred = new Date(event.occurred);
|
|
99
|
+
return event;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Breadcrumbs to create a trail of events that happened prior to an issue.
|
|
104
|
+
* These events are very similar to traditional logs, but can record more rich structured data.
|
|
105
|
+
*/
|
|
106
|
+
export function addBreadcrumb(breadcrumb: ReporterBreadcrumb): void {
|
|
107
|
+
for (const reporter of getReporters(breadcrumb)) {
|
|
108
|
+
if (reporter.endpoints?.addBreadcrumb === false) {
|
|
109
|
+
continue;
|
|
110
|
+
} else if (breadcrumb.category && reporter.ignoreBreadcrumbCategories?.includes(breadcrumb.category)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const reporterBreadcrumb = { ...breadcrumb };
|
|
115
|
+
reporterBreadcrumb.metadata = filterReporterMetadata(breadcrumb.metadata, reporter);
|
|
116
|
+
reporter.addBreadcrumb(reporterBreadcrumb);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Adds global metadata to all events and logs.
|
|
122
|
+
* @param metadata Metadata to add.
|
|
123
|
+
* @param filters Optional filters to specify which reporters to add metadata to.
|
|
124
|
+
*/
|
|
125
|
+
export function addMetadata(metadata: Metadata, filters?: ReporterFilters): void {
|
|
126
|
+
for (const reporter of getReporters(filters)) {
|
|
127
|
+
if (reporter.endpoints?.addMetadata === false) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
reporter.addMetadata(filterReporterMetadata(metadata, reporter));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Sets the user information in Analytics.
|
|
136
|
+
* @param user User information, or null to clear.
|
|
137
|
+
* @param filters Optional filters to specify which reporters to set the user for.
|
|
138
|
+
*/
|
|
139
|
+
export function setUser(user: ReportUser | null, filters?: ReporterFilters): void {
|
|
140
|
+
for (const reporter of getReporters(filters)) {
|
|
141
|
+
if (reporter.endpoints?.setUser === false) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
reporter.setUser(user);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Sets the route name in Analytics.
|
|
150
|
+
* Some Analytics services use this differentiate SPA Route vs Page changes.
|
|
151
|
+
* @param routeName Name of the Route.
|
|
152
|
+
* @param filters Optional filters to specify which reporters to set the route name for.
|
|
153
|
+
*/
|
|
154
|
+
export function setRouteName(routeName: string, filters?: ReporterFilters): void {
|
|
155
|
+
for (const reporter of getReporters(filters)) {
|
|
156
|
+
if (reporter.endpoints?.setRouteName === false) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
reporter.setRouteName(routeName);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Sets the page name in Analytics.
|
|
165
|
+
* @param pageName Name of the Page.
|
|
166
|
+
* @param filters Optional filters to specify which reporters to set the page name for.
|
|
167
|
+
*/
|
|
168
|
+
export function setPageName(pageName: string, filters?: ReporterFilters): void {
|
|
169
|
+
for (const reporter of getReporters(filters)) {
|
|
170
|
+
if (reporter.endpoints?.setPageName === false) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
reporter.setPageName(pageName);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Reports an Error in Reporters that support error tracking.
|
|
179
|
+
* @param error Error data.
|
|
180
|
+
* @param metadata Metadata to add to the error.
|
|
181
|
+
* @param filters Optional filters to specify which reporters to report the error to.
|
|
182
|
+
*/
|
|
183
|
+
export function reportError(error: ReportError, metadata?: Metadata, filters?: ReporterFilters): void {
|
|
184
|
+
for (const reporter of getReporters(filters)) {
|
|
185
|
+
if (reporter.endpoints?.reportError === false) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
reporter.reportError?.(error, filterReporterMetadata(metadata, reporter));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Starts a session recording in Analytics, e.g. RUM Session Replay
|
|
194
|
+
* @param filters Optional filters to specify which reporters to start a session recording for.
|
|
195
|
+
*/
|
|
196
|
+
export function recordSession(filters?: ReporterFilters): void {
|
|
197
|
+
for (const reporter of getReporters(filters)) {
|
|
198
|
+
if (reporter.endpoints?.recordSession === false) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
reporter.recordSession?.();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Stops a session recording in Analytics, e.g. RUM Session Replay
|
|
207
|
+
* @param filters Optional filters to specify which reporters to stop a session recording for.
|
|
208
|
+
*/
|
|
209
|
+
export function recordSessionStop(filters?: ReporterFilters): void {
|
|
210
|
+
for (const reporter of getReporters(filters)) {
|
|
211
|
+
if (reporter.endpoints?.recordSessionStop === false) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
reporter.recordSessionStop?.();
|
|
215
|
+
}
|
|
216
|
+
}
|