@dash0/sdk-web 0.13.1 → 0.13.2
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 +1 -1
- package/dist/dash0.iife.js +1 -1
- package/dist/dash0.iife.js.map +1 -1
- package/dist/dash0.js +1 -1
- package/dist/dash0.js.map +1 -1
- package/dist/dash0.umd.cjs +1 -1
- package/dist/dash0.umd.cjs.map +1 -1
- package/dist/modules/instrumentations/http/fetch.js +5 -2
- package/dist/modules/semantic-conventions.js +2 -0
- package/dist/modules/utils/performance.js +43 -19
- package/dist/modules/vars.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/semantic-conventions.d.ts +1 -0
- package/dist/types/vars.d.ts +1 -1
- package/package.json +1 -1
- package/src/instrumentations/http/fetch.ts +5 -1
- package/src/semantic-conventions.ts +3 -0
- package/src/utils/performance.ts +55 -22
- package/src/vars.ts +2 -2
|
@@ -23,6 +23,7 @@ export declare const USER_ROLES = "user.roles";
|
|
|
23
23
|
export declare const EXCEPTION_MESSAGE = "exception.message";
|
|
24
24
|
export declare const EXCEPTION_TYPE = "exception.type";
|
|
25
25
|
export declare const EXCEPTION_STACKTRACE = "exception.stacktrace";
|
|
26
|
+
export declare const ERROR_TYPE = "error.type";
|
|
26
27
|
export declare const URL_DOMAIN = "url.domain";
|
|
27
28
|
export declare const URL_FRAGMENT = "url.fragment";
|
|
28
29
|
export declare const URL_FULL = "url.full";
|
package/dist/types/vars.d.ts
CHANGED
|
@@ -109,7 +109,7 @@ export type Vars = {
|
|
|
109
109
|
* applied when matching a request to its respective performance entry. A higher value might increase match frequency at
|
|
110
110
|
* the cost of potential incorrect matches. Matching is performed based on request timing and url.
|
|
111
111
|
*
|
|
112
|
-
* @default
|
|
112
|
+
* @default 50
|
|
113
113
|
*/
|
|
114
114
|
maxToleranceForResourceTimingsMillis: number;
|
|
115
115
|
/**
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
startSpan,
|
|
13
13
|
} from "../../utils/otel";
|
|
14
14
|
import {
|
|
15
|
+
ERROR_TYPE,
|
|
15
16
|
HTTP_REQUEST_METHOD,
|
|
16
17
|
HTTP_REQUEST_METHOD_ORIGINAL,
|
|
17
18
|
HTTP_RESPONSE_STATUS_CODE,
|
|
@@ -166,7 +167,10 @@ function tryCaptureHttpHeaders(headers: Headers, span: InProgressSpan, getAttrib
|
|
|
166
167
|
|
|
167
168
|
function addResponseData(span: InProgressSpan, response: Response) {
|
|
168
169
|
const status = response.status;
|
|
169
|
-
setSpanStatus(span, status >= 200 && status < 400 ? SPAN_STATUS_UNSET : SPAN_STATUS_ERROR
|
|
170
|
+
setSpanStatus(span, status >= 200 && status < 400 ? SPAN_STATUS_UNSET : SPAN_STATUS_ERROR);
|
|
171
|
+
if (status === 0) {
|
|
172
|
+
addAttribute(span.attributes, ERROR_TYPE, response.type);
|
|
173
|
+
}
|
|
170
174
|
addAttribute(span.attributes, HTTP_RESPONSE_STATUS_CODE, String(status));
|
|
171
175
|
tryCaptureHttpHeaders(response.headers, span, (k) => httpResponseHeaderKey(k));
|
|
172
176
|
}
|
|
@@ -31,6 +31,9 @@ export const EXCEPTION_MESSAGE = "exception.message";
|
|
|
31
31
|
export const EXCEPTION_TYPE = "exception.type";
|
|
32
32
|
export const EXCEPTION_STACKTRACE = "exception.stacktrace";
|
|
33
33
|
|
|
34
|
+
// Error Attribute Keys
|
|
35
|
+
export const ERROR_TYPE = "error.type";
|
|
36
|
+
|
|
34
37
|
// URL Attribute Keys
|
|
35
38
|
export const URL_DOMAIN = "url.domain";
|
|
36
39
|
export const URL_FRAGMENT = "url.fragment";
|
package/src/utils/performance.ts
CHANGED
|
@@ -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
|
-
|
|
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 (
|
|
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:
|
|
125
|
+
opts.onEnd({ resource, duration: resource.duration });
|
|
120
126
|
} else {
|
|
121
|
-
opts.onEnd({ resource, duration:
|
|
127
|
+
opts.onEnd({ resource, duration: endTime - startTime });
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
function onResourceFound(list: PerformanceObserverEntryList) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
entry
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
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:
|
|
163
|
+
maxToleranceForResourceTimingsMillis: 50,
|
|
164
164
|
headersToCapture: [],
|
|
165
165
|
pageViewInstrumentation: {
|
|
166
166
|
trackVirtualPageViews: true,
|