@dash0/sdk-web 0.13.1 → 0.13.3

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.
@@ -1,25 +1,40 @@
1
1
  import { noop } from "./fn";
2
2
 
3
+ const logLevels = {
4
+ debug: 0,
5
+ info: 1,
6
+ warn: 2,
7
+ error: 3,
8
+ } as const;
9
+
10
+ export type LogLevel = keyof typeof logLevels;
11
+
12
+ let activeLogLevel: number = logLevels.warn;
13
+
14
+ /**
15
+ * Changes the logging verbosity of Dash0's web SDK. By default, only warnings and errors are logged.
16
+ */
17
+ export function setActiveLogLevel(level: LogLevel) {
18
+ activeLogLevel = logLevels[level] ?? logLevels.warn;
19
+ }
20
+
3
21
  type Logger = (...args: any[]) => void;
4
22
 
5
- export const log: Logger = createLogger("log");
6
23
  export const info: Logger = createLogger("info");
7
24
  export const warn: Logger = createLogger("warn");
8
25
  export const error: Logger = createLogger("error");
9
26
  export const debug: Logger = createLogger("debug");
10
27
 
11
- function createLogger(method: Extract<keyof Console, "log" | "info" | "warn" | "error" | "debug">): Logger {
12
- if (typeof console === "undefined" || typeof console.log !== "function" || typeof console.log.apply !== "function") {
13
- return noop;
14
- }
28
+ function createLogger(logLevel: LogLevel): Logger {
29
+ if (typeof console !== "undefined" && console[logLevel] && typeof console[logLevel].apply === "function") {
30
+ const numericLogLevel = logLevels[logLevel];
15
31
 
16
- if (console[method] && typeof console[method].apply === "function") {
17
32
  return function () {
18
- console[method].apply(console, arguments as any);
33
+ if (numericLogLevel >= activeLogLevel) {
34
+ console[logLevel].apply(console, arguments as any);
35
+ }
19
36
  };
20
37
  }
21
38
 
22
- return function () {
23
- console.log.apply(console, arguments as any);
24
- };
39
+ return noop;
25
40
  }
@@ -6,6 +6,7 @@ import { addEventListener, removeEventListener } from "./listeners";
6
6
 
7
7
  const TEN_MINUTES_IN_MILLIS = 1000 * 60 * 10;
8
8
  const ONE_DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
9
+ const OBSERVER_WAIT_TIME_MS = 300;
9
10
 
10
11
  export const isResourceTimingAvailable = !!(perf && perf.getEntriesByType);
11
12
  export const isPerformanceObserverAvailable =
@@ -57,6 +58,8 @@ type PerformanceObservationContoller = {
57
58
  cancel: () => void;
58
59
  };
59
60
 
61
+ const usedResources = new WeakSet<PerformanceResourceTiming>();
62
+
60
63
  export function observeResourcePerformance(opts: ObserveResourcePerformanceOptions): PerformanceObservationContoller {
61
64
  if (!isPerformanceObserverAvailable) return observeWithoutPerformanceObserverSupport(opts.onEnd);
62
65
 
@@ -64,7 +67,7 @@ export function observeResourcePerformance(opts: ObserveResourcePerformanceOptio
64
67
  let startTime: number;
65
68
  let endTime: number;
66
69
 
67
- let resource: PerformanceResourceTiming | undefined;
70
+ const resources: PerformanceResourceTiming[] = [];
68
71
 
69
72
  // Global resources that will need to be disposed
70
73
  let observer: PerformanceObserver | undefined;
@@ -100,43 +103,40 @@ export function observeResourcePerformance(opts: ObserveResourcePerformanceOptio
100
103
  endTime = perf.now();
101
104
  cancelFallbackEndNeverCalledTimer();
102
105
 
103
- if (resource || !isWaitingAcceptable()) {
104
- end();
105
- } else {
106
- addEventListener(doc!, "visibilitychange", onVisibilityChanged);
107
- fallbackNoResourceFoundTimerHandle = setTimeout(end, opts.maxWaitForResourceMillis);
106
+ if (!isWaitingAcceptable()) {
107
+ return end();
108
108
  }
109
+
110
+ setTimeout(() => end(), Math.min(OBSERVER_WAIT_TIME_MS, opts.maxWaitForResourceMillis));
111
+ addEventListener(doc!, "visibilitychange", onVisibilityChanged);
112
+ fallbackNoResourceFoundTimerHandle = setTimeout(end, opts.maxWaitForResourceMillis);
109
113
  }
110
114
 
111
115
  function end() {
112
116
  disposeGlobalResources();
113
117
 
118
+ const resource = findBestMatchingResource();
119
+
114
120
  // In some old web browsers, e.g. Chrome 31, the value provided as the duration
115
121
  // can be very wrong. We have seen cases where this value is measured in years.
116
122
  // If this does seem be the case, then we will ignore the duration property and
117
123
  // instead prefer our approximation.
118
124
  if (resource?.duration && resource.duration < ONE_DAY_IN_MILLIS) {
119
- opts.onEnd({ resource, duration: Math.round(resource.duration) });
125
+ opts.onEnd({ resource, duration: resource.duration });
120
126
  } else {
121
- opts.onEnd({ resource, duration: Math.round(endTime - startTime) });
127
+ opts.onEnd({ resource, duration: endTime - startTime });
122
128
  }
123
129
  }
124
130
 
125
131
  function onResourceFound(list: PerformanceObserverEntryList) {
126
- const r = list.getEntriesByType("resource").find((e) => {
127
- // This polymorphism is not properly represented in the api types. The cast is safe since we're only accessing the resource timings.
128
- const entry = e as PerformanceResourceTiming;
129
- return (
130
- entry.startTime >= startTime &&
131
- (!endTime || endTime + opts.maxToleranceForResourceTimingsMillis >= entry.responseEnd) &&
132
- opts.resourceMatcher(entry)
133
- );
134
- });
135
-
136
- if (!r) return;
137
-
138
- resource = r as PerformanceResourceTiming;
139
- if (endTime) end();
132
+ list
133
+ .getEntriesByType("resource")
134
+ .filter((e) => {
135
+ // This polymorphism is not properly represented in the api types. The cast is safe since we're only accessing the resource timings.
136
+ const entry = e as PerformanceResourceTiming;
137
+ return entry.startTime >= startTime && opts.resourceMatcher(entry);
138
+ })
139
+ .forEach((entry) => resources.push(entry as PerformanceResourceTiming));
140
140
  }
141
141
 
142
142
  function onVisibilityChanged() {
@@ -179,6 +179,39 @@ export function observeResourcePerformance(opts: ObserveResourcePerformanceOptio
179
179
  if (!doc) return;
180
180
  removeEventListener(doc, "visibilitychange", onVisibilityChanged);
181
181
  }
182
+
183
+ function findBestMatchingResource(): PerformanceResourceTiming | undefined {
184
+ if (!resources.length) return undefined;
185
+
186
+ let bestMatch: PerformanceResourceTiming | undefined;
187
+ const matchingResources = resources.filter(
188
+ (res) => res.responseEnd <= endTime + opts.maxToleranceForResourceTimingsMillis && !usedResources.has(res)
189
+ );
190
+
191
+ if (!matchingResources.length) {
192
+ return undefined;
193
+ }
194
+
195
+ if (matchingResources.length === 1) {
196
+ bestMatch = matchingResources[0];
197
+ }
198
+
199
+ let bestScore: number | undefined;
200
+ if (!bestMatch) {
201
+ for (const res of matchingResources) {
202
+ const score = Math.abs(endTime - startTime - res.duration) + Math.abs(res.responseEnd - endTime);
203
+ if (bestScore === undefined || score < bestScore) {
204
+ bestScore = score;
205
+ bestMatch = res;
206
+ }
207
+ }
208
+ }
209
+
210
+ if (!bestMatch) return;
211
+
212
+ usedResources.add(bestMatch);
213
+ return bestMatch;
214
+ }
182
215
  }
183
216
 
184
217
  // We may only wait for resource data to arrive as long as the document is visible or in the process
package/src/vars.ts CHANGED
@@ -129,7 +129,7 @@ export type Vars = {
129
129
  * applied when matching a request to its respective performance entry. A higher value might increase match frequency at
130
130
  * the cost of potential incorrect matches. Matching is performed based on request timing and url.
131
131
  *
132
- * @default 3000
132
+ * @default 50
133
133
  */
134
134
  maxToleranceForResourceTimingsMillis: number;
135
135
 
@@ -160,7 +160,7 @@ export const vars: Vars = {
160
160
  wrapTimers: true,
161
161
  propagateTraceHeadersCorsURLs: [],
162
162
  maxWaitForResourceTimingsMillis: 10000,
163
- maxToleranceForResourceTimingsMillis: 3000,
163
+ maxToleranceForResourceTimingsMillis: 50,
164
164
  headersToCapture: [],
165
165
  pageViewInstrumentation: {
166
166
  trackVirtualPageViews: true,