@crimson-education/browser-logger 2.0.2 → 3.0.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 +6 -76
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +42 -186
- 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 +2 -2
- package/lib/reporters/amplifyReporter.d.ts.map +1 -1
- package/lib/reporters/amplifyReporter.js +13 -14
- package/lib/reporters/amplifyReporter.js.map +1 -1
- package/lib/reporters/datadogReporter.d.ts +7 -5
- package/lib/reporters/datadogReporter.d.ts.map +1 -1
- package/lib/reporters/datadogReporter.js +37 -100
- package/lib/reporters/datadogReporter.js.map +1 -1
- package/lib/reporters/gtmReporter.d.ts.map +1 -1
- package/lib/reporters/gtmReporter.js +2 -7
- 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 +210 -17
- 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/logger.d.ts +43 -2
- package/lib/types/logger.d.ts.map +1 -1
- package/lib/types/logger.js.map +1 -1
- package/lib/types/reporter.d.ts +58 -6
- package/lib/types/reporter.d.ts.map +1 -1
- package/lib/utils.d.ts +9 -8
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +28 -48
- package/lib/utils.js.map +1 -1
- package/lib/utils.test.js +18 -5
- 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 +15 -16
- 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
|
@@ -9,7 +9,10 @@ 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>;
|
|
13
16
|
|
|
14
17
|
export interface AmplifyReporterConfig extends ReporterConfigBase {
|
|
15
18
|
/**
|
|
@@ -133,16 +136,11 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
133
136
|
...event.tags,
|
|
134
137
|
},
|
|
135
138
|
false,
|
|
136
|
-
config.ignoreMetadataPatterns,
|
|
137
139
|
) as Record<string, string>,
|
|
138
140
|
metrics: event.metrics,
|
|
139
141
|
});
|
|
140
142
|
},
|
|
141
143
|
addBreadcrumb: function (breadcrumb: ReporterBreadcrumb): void {
|
|
142
|
-
if (breadcrumb.category && config.ignoreBreadcrumbCategories?.includes(breadcrumb.category)) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
144
|
reporter.trackEvent({
|
|
147
145
|
message: breadcrumb.message,
|
|
148
146
|
metadata: {
|
|
@@ -152,7 +150,7 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
152
150
|
});
|
|
153
151
|
},
|
|
154
152
|
addMetadata: function (metadata: Metadata): void {
|
|
155
|
-
Object.assign(allMetadata, asAttributeMap(metadata, true
|
|
153
|
+
Object.assign(allMetadata, asAttributeMap(metadata, true));
|
|
156
154
|
Analytics.updateEndpoint({
|
|
157
155
|
attributes: allMetadata,
|
|
158
156
|
}).catch(() => {
|
|
@@ -188,16 +186,10 @@ export function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig
|
|
|
188
186
|
/**
|
|
189
187
|
* Pinpoint has strict attribute name and value length limits
|
|
190
188
|
*/
|
|
191
|
-
|
|
192
|
-
export function asAttributeMap(
|
|
193
|
-
values: Record<string, unknown>,
|
|
194
|
-
groupValues = true,
|
|
195
|
-
ignorePatterns: (string | RegExp)[] = [],
|
|
196
|
-
): AttributeMap {
|
|
189
|
+
export function asAttributeMap(values: Record<string, unknown>, groupValues = true): AttributeMap {
|
|
197
190
|
const attributeMap = buildAttributeMap(values, undefined, groupValues);
|
|
198
|
-
const filteredAttributeMap = filterAttributeMap(attributeMap, ignorePatterns);
|
|
199
191
|
|
|
200
|
-
const checkedEntries = Object.entries(
|
|
192
|
+
const checkedEntries = Object.entries(attributeMap).map(([key, value]) => {
|
|
201
193
|
const truncatedKey = key.length > 50 ? `___${key.slice(-47)}` : key;
|
|
202
194
|
const truncatedValue = Array.isArray(value)
|
|
203
195
|
? value?.map((val) => val.slice(0, 100)) ?? null
|
|
@@ -206,6 +198,14 @@ export function asAttributeMap(
|
|
|
206
198
|
return [truncatedKey, truncatedValue];
|
|
207
199
|
});
|
|
208
200
|
|
|
201
|
+
// Pinpoint only accepts 40 attributes
|
|
202
|
+
if (checkedEntries.length > 40) {
|
|
203
|
+
logger.error(`Amplify only allows 40 attributes per event, truncating to 40 attributes`, {
|
|
204
|
+
attributes: checkedEntries,
|
|
205
|
+
});
|
|
206
|
+
checkedEntries.length = 40;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
209
|
return Object.fromEntries(checkedEntries);
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -215,7 +215,6 @@ export function asAttributeMap(
|
|
|
215
215
|
* all of its values are of type `string[]` to appease Pinpoint.
|
|
216
216
|
*/
|
|
217
217
|
export function buildAttributeMap(
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
219
218
|
values: Record<string, any>,
|
|
220
219
|
parentKey: string | undefined = undefined,
|
|
221
220
|
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
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { logger, globalMetadata as globalLogMetadata } from '../logger';
|
|
2
|
+
import { IReporter, LogLevel, ReporterConfigBase, ServiceInfo } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface LogReporterConfig extends ReporterConfigBase {
|
|
5
|
+
/**
|
|
6
|
+
* The fallback log level to apply to all trackEvent calls (if the event's level is not set).
|
|
7
|
+
* Defaults to info.
|
|
8
|
+
*/
|
|
9
|
+
trackEventDefaultLevel?: LogLevel;
|
|
10
|
+
/**
|
|
11
|
+
* The log level to apply to all addBreadcrumb calls.
|
|
12
|
+
* Defaults to info.
|
|
13
|
+
*/
|
|
14
|
+
addBreadcrumbLevel?: LogLevel;
|
|
15
|
+
/**
|
|
16
|
+
* The log level to apply to all setRouteName calls.
|
|
17
|
+
* Defaults to debug.
|
|
18
|
+
*/
|
|
19
|
+
setRouteNameLevel?: LogLevel;
|
|
20
|
+
/**
|
|
21
|
+
* The log level to apply to all setPageName calls.
|
|
22
|
+
* Defaults to debug.
|
|
23
|
+
*/
|
|
24
|
+
setPageNameLevel?: LogLevel;
|
|
25
|
+
/**
|
|
26
|
+
* The log level to apply to all recordSession calls.
|
|
27
|
+
* Defaults to debug.
|
|
28
|
+
*/
|
|
29
|
+
recordSessionLevel?: LogLevel;
|
|
30
|
+
/**
|
|
31
|
+
* The log level to apply to all recordSessionStop calls.
|
|
32
|
+
* Defaults to debug.
|
|
33
|
+
*/
|
|
34
|
+
recordSessionStopLevel?: LogLevel;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function logReporter(info: ServiceInfo, config: LogReporterConfig) {
|
|
38
|
+
const reporter: IReporter = {
|
|
39
|
+
...config,
|
|
40
|
+
trackEvent(event) {
|
|
41
|
+
// config.trackEventLevel and config.level overrides event.level here, as it is an override, while config.trackEventDefaultLevel is a fallback.
|
|
42
|
+
const level = event.level ?? config.trackEventDefaultLevel ?? LogLevel.Info;
|
|
43
|
+
logger.log(level, event.message, {
|
|
44
|
+
...(event.metadata ?? {}),
|
|
45
|
+
...(event.metrics ?? {}),
|
|
46
|
+
...(event.tags ?? {}),
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
addBreadcrumb(breadcrumb) {
|
|
50
|
+
const level = config.addBreadcrumbLevel ?? LogLevel.Info;
|
|
51
|
+
logger.log(level, breadcrumb.message, {
|
|
52
|
+
...(breadcrumb.metadata ?? {}),
|
|
53
|
+
category: breadcrumb.category,
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
addMetadata(metadata) {
|
|
57
|
+
Object.assign(globalLogMetadata, metadata);
|
|
58
|
+
},
|
|
59
|
+
setUser(user) {
|
|
60
|
+
Object.assign(globalLogMetadata, { user });
|
|
61
|
+
},
|
|
62
|
+
setRouteName(routeName) {
|
|
63
|
+
Object.assign(globalLogMetadata, { routeName });
|
|
64
|
+
const level = config.setRouteNameLevel ?? LogLevel.Debug;
|
|
65
|
+
logger.log(level, `Route changed to ${routeName}`);
|
|
66
|
+
},
|
|
67
|
+
setPageName(pageName) {
|
|
68
|
+
Object.assign(globalLogMetadata, { pageName });
|
|
69
|
+
const level = config.setPageNameLevel ?? LogLevel.Debug;
|
|
70
|
+
logger.log(level, `Page changed to ${pageName}`);
|
|
71
|
+
},
|
|
72
|
+
reportError(error, metadata) {
|
|
73
|
+
logger.error(typeof error === 'string' ? error : error.message, { ...metadata, error });
|
|
74
|
+
},
|
|
75
|
+
recordSession() {
|
|
76
|
+
const level = config.recordSessionLevel ?? LogLevel.Debug;
|
|
77
|
+
logger.log(level, 'Recording Session');
|
|
78
|
+
},
|
|
79
|
+
recordSessionStop() {
|
|
80
|
+
const level = config.recordSessionStopLevel ?? LogLevel.Debug;
|
|
81
|
+
logger.log(level, 'Recording Session Stopping');
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return reporter;
|
|
86
|
+
}
|