@zerolith/otel 0.1.0-alpha.5 → 0.1.0-alpha.6
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/index.d.ts +15 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -9
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +37 -9
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ const api = createClient<Api>(apiRoutes, { transport: tracedTransport(undefined,
|
|
|
12
12
|
tracedHydrate(App(router), "#app", tracer);
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
- `navigationObserver` opens a span when a navigation starts and ends it on commit or
|
|
15
|
+
- `navigationObserver` opens a span when a navigation starts and ends it on commit, failure, or cancel, with the route name, the path, and the mode as attributes, and `zerolith.navigation.canceled` on a canceled one.
|
|
16
16
|
- `tracedHydrate` wraps `hydrate` in a span and records the number of mismatches.
|
|
17
17
|
- `tracedTransport` wraps a client transport in a client span per request, with the method, URL, and status, and injects a `traceparent` header from the active span so the server's trace continues the browser's.
|
|
18
18
|
|
package/dist/index.d.ts
CHANGED
|
@@ -11,9 +11,13 @@ import type { Transport } from "@zerolith/client";
|
|
|
11
11
|
import type { NavigationEvent } from "@zerolith/router";
|
|
12
12
|
/**
|
|
13
13
|
* The `observe` function for `createRouter`: a span named `navigate`
|
|
14
|
-
* opens when a navigation starts and ends when it commits
|
|
15
|
-
* `zerolith.route`, `url.path`,
|
|
16
|
-
*
|
|
14
|
+
* opens when a navigation starts and ends when it commits, fails, or is
|
|
15
|
+
* canceled by a newer navigation, with `zerolith.route`, `url.path`,
|
|
16
|
+
* `url.query` when the URL has a query, and `zerolith.navigation.mode` as
|
|
17
|
+
* attributes, the loader's error recorded on failure, and
|
|
18
|
+
* `zerolith.navigation.canceled` set on a canceled one, whose status stays
|
|
19
|
+
* unset. The values of the query keys the URL conventions redact, such as
|
|
20
|
+
* `sig`, are recorded as `REDACTED`.
|
|
17
21
|
*/
|
|
18
22
|
export declare function navigationObserver(tracer?: Tracer): (event: NavigationEvent) => void;
|
|
19
23
|
/** `hydrate` inside a span named `hydrate`, with the number of mismatches as `zerolith.hydration.mismatches`. */
|
|
@@ -22,9 +26,14 @@ export declare function tracedHydrate(view: unknown, container: MountTarget, tra
|
|
|
22
26
|
export declare function traceparent(active?: Context): string | undefined;
|
|
23
27
|
/**
|
|
24
28
|
* Wraps a transport, or `fetch` when none is given, so every request runs
|
|
25
|
-
* in a client span named
|
|
26
|
-
* `url.full`, and `http.response.status_code`,
|
|
27
|
-
* header from that span, and records a failed
|
|
29
|
+
* in a client span named by its method, as `GET`, with
|
|
30
|
+
* `http.request.method`, `url.full`, and `http.response.status_code`,
|
|
31
|
+
* carries a `traceparent` header from that span, and records a failed
|
|
32
|
+
* request as an error. The span is not named by the request's path, which
|
|
33
|
+
* would give every note its own span name; the HTTP conventions name a
|
|
34
|
+
* client span by its method when no route template is known. `url.full`
|
|
35
|
+
* records the values of the query keys the conventions redact, such as
|
|
36
|
+
* `sig`, as `REDACTED`.
|
|
28
37
|
*/
|
|
29
38
|
export declare function tracedTransport(transport: Transport | undefined, tracer?: Tracer): Transport;
|
|
30
39
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,OAAO,EAAuC,KAAK,MAAM,EAAkB,MAAM,oBAAoB,CAAC;AACpH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAW,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,OAAO,EAAuC,KAAK,MAAM,EAAkB,MAAM,oBAAoB,CAAC;AACpH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAW,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAqBxD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,MAAwB,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAsCrG;AAED,iHAAiH;AACjH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAE,MAAwB,GAAG,aAAa,CAsBpH;AAED,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,MAAM,GAAE,OAA0B,GAAG,MAAM,GAAG,SAAS,CAQlF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,GAAE,MAAwB,GAAG,SAAS,CA8B7G"}
|
package/dist/index.js
CHANGED
|
@@ -11,19 +11,33 @@ import { hydrate } from "zerolith";
|
|
|
11
11
|
function defaultTracer() {
|
|
12
12
|
return trace.getTracer("@zerolith/otel");
|
|
13
13
|
}
|
|
14
|
+
/** The query keys whose values the URL conventions redact by default, such as a signed URL's signature. */
|
|
15
|
+
const SENSITIVE_QUERY = new Set(["X-Amz-Signature", "X-Amz-Credential", "X-Amz-Security-Token", "sig", "X-Goog-Signature"]);
|
|
16
|
+
/** A URL's query component without its `?`, with the value of each key the conventions redact replaced by `REDACTED`. */
|
|
17
|
+
function redactedQuery(search) {
|
|
18
|
+
return search.replace(/^\?/, "").split("&").map((pair) => {
|
|
19
|
+
const key = pair.split("=", 1)[0] ?? "";
|
|
20
|
+
return SENSITIVE_QUERY.has(key) ? `${key}=REDACTED` : pair;
|
|
21
|
+
}).join("&");
|
|
22
|
+
}
|
|
14
23
|
/**
|
|
15
24
|
* The `observe` function for `createRouter`: a span named `navigate`
|
|
16
|
-
* opens when a navigation starts and ends when it commits
|
|
17
|
-
* `zerolith.route`, `url.path`,
|
|
18
|
-
*
|
|
25
|
+
* opens when a navigation starts and ends when it commits, fails, or is
|
|
26
|
+
* canceled by a newer navigation, with `zerolith.route`, `url.path`,
|
|
27
|
+
* `url.query` when the URL has a query, and `zerolith.navigation.mode` as
|
|
28
|
+
* attributes, the loader's error recorded on failure, and
|
|
29
|
+
* `zerolith.navigation.canceled` set on a canceled one, whose status stays
|
|
30
|
+
* unset. The values of the query keys the URL conventions redact, such as
|
|
31
|
+
* `sig`, are recorded as `REDACTED`.
|
|
19
32
|
*/
|
|
20
33
|
export function navigationObserver(tracer = defaultTracer()) {
|
|
21
34
|
const open = new Map();
|
|
22
35
|
return (event) => {
|
|
23
36
|
if (event.type === "start") {
|
|
37
|
+
const query = redactedQuery(event.url.search);
|
|
24
38
|
const span = tracer.startSpan("navigate", {
|
|
25
39
|
kind: SpanKind.INTERNAL,
|
|
26
|
-
attributes: { "url.path": event.url.pathname, "url.query":
|
|
40
|
+
attributes: { "url.path": event.url.pathname, ...(query === "" ? {} : { "url.query": query }), "zerolith.navigation.mode": event.mode }
|
|
27
41
|
});
|
|
28
42
|
open.set(event.id, span);
|
|
29
43
|
return;
|
|
@@ -37,6 +51,9 @@ export function navigationObserver(tracer = defaultTracer()) {
|
|
|
37
51
|
span.setAttribute("zerolith.route", event.name ?? "");
|
|
38
52
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
39
53
|
}
|
|
54
|
+
else if (event.type === "cancel") {
|
|
55
|
+
span.setAttribute("zerolith.navigation.canceled", true);
|
|
56
|
+
}
|
|
40
57
|
else {
|
|
41
58
|
const error = event.error;
|
|
42
59
|
span.recordException(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -74,15 +91,21 @@ export function traceparent(active = context.active()) {
|
|
|
74
91
|
}
|
|
75
92
|
/**
|
|
76
93
|
* Wraps a transport, or `fetch` when none is given, so every request runs
|
|
77
|
-
* in a client span named
|
|
78
|
-
* `url.full`, and `http.response.status_code`,
|
|
79
|
-
* header from that span, and records a failed
|
|
94
|
+
* in a client span named by its method, as `GET`, with
|
|
95
|
+
* `http.request.method`, `url.full`, and `http.response.status_code`,
|
|
96
|
+
* carries a `traceparent` header from that span, and records a failed
|
|
97
|
+
* request as an error. The span is not named by the request's path, which
|
|
98
|
+
* would give every note its own span name; the HTTP conventions name a
|
|
99
|
+
* client span by its method when no route template is known. `url.full`
|
|
100
|
+
* records the values of the query keys the conventions redact, such as
|
|
101
|
+
* `sig`, as `REDACTED`.
|
|
80
102
|
*/
|
|
81
103
|
export function tracedTransport(transport, tracer = defaultTracer()) {
|
|
82
104
|
const send = transport ?? ((request) => fetch(request));
|
|
83
105
|
return (request) => {
|
|
84
|
-
const
|
|
85
|
-
|
|
106
|
+
const full = new URL(request.url);
|
|
107
|
+
full.search = redactedQuery(full.search);
|
|
108
|
+
return tracer.startActiveSpan(request.method, { kind: SpanKind.CLIENT, attributes: { "http.request.method": request.method, "url.full": full.href } }, async (span) => {
|
|
86
109
|
try {
|
|
87
110
|
const header = traceparent();
|
|
88
111
|
const traced = header === undefined ? request : new Request(request, { headers: withHeader(request.headers, "traceparent", header) });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAA2B,QAAQ,EAAE,cAAc,EAAe,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACpH,OAAO,EAAwC,OAAO,EAAE,MAAM,UAAU,CAAC;AAIzE,wHAAwH;AACxH,SAAS,aAAa;IAElB,OAAO,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;AAC7C,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerolith/otel",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.6",
|
|
4
4
|
"description": "OpenTelemetry spans for zerolith applications through @opentelemetry/api: a span per navigation, per hydration, and per request of the typed client with traceparent propagation",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "molexxxx",
|
|
@@ -41,17 +41,17 @@
|
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@opentelemetry/api": ">=1.9.0 <2.0.0",
|
|
44
|
-
"@zerolith/client": "^0.1.0-alpha.
|
|
45
|
-
"@zerolith/router": "^0.1.0-alpha.
|
|
46
|
-
"zerolith": "^0.1.0-alpha.
|
|
44
|
+
"@zerolith/client": "^0.1.0-alpha.6",
|
|
45
|
+
"@zerolith/router": "^0.1.0-alpha.6",
|
|
46
|
+
"zerolith": "^0.1.0-alpha.6"
|
|
47
47
|
},
|
|
48
48
|
"peerDependenciesMeta": {
|
|
49
49
|
"@zerolith/client": { "optional": true },
|
|
50
50
|
"@zerolith/router": { "optional": true }
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@zerolith/client": "^0.1.0-alpha.
|
|
54
|
-
"@zerolith/router": "^0.1.0-alpha.
|
|
55
|
-
"zerolith": "^0.1.0-alpha.
|
|
53
|
+
"@zerolith/client": "^0.1.0-alpha.6",
|
|
54
|
+
"@zerolith/router": "^0.1.0-alpha.6",
|
|
55
|
+
"zerolith": "^0.1.0-alpha.6"
|
|
56
56
|
}
|
|
57
57
|
}
|
package/src/index.ts
CHANGED
|
@@ -16,11 +16,28 @@ function defaultTracer(): Tracer
|
|
|
16
16
|
return trace.getTracer("@zerolith/otel");
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** The query keys whose values the URL conventions redact by default, such as a signed URL's signature. */
|
|
20
|
+
const SENSITIVE_QUERY = new Set(["X-Amz-Signature", "X-Amz-Credential", "X-Amz-Security-Token", "sig", "X-Goog-Signature"]);
|
|
21
|
+
|
|
22
|
+
/** A URL's query component without its `?`, with the value of each key the conventions redact replaced by `REDACTED`. */
|
|
23
|
+
function redactedQuery(search: string): string
|
|
24
|
+
{
|
|
25
|
+
return search.replace(/^\?/, "").split("&").map((pair) =>
|
|
26
|
+
{
|
|
27
|
+
const key = pair.split("=", 1)[0] ?? "";
|
|
28
|
+
return SENSITIVE_QUERY.has(key) ? `${key}=REDACTED` : pair;
|
|
29
|
+
}).join("&");
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
/**
|
|
20
33
|
* The `observe` function for `createRouter`: a span named `navigate`
|
|
21
|
-
* opens when a navigation starts and ends when it commits
|
|
22
|
-
* `zerolith.route`, `url.path`,
|
|
23
|
-
*
|
|
34
|
+
* opens when a navigation starts and ends when it commits, fails, or is
|
|
35
|
+
* canceled by a newer navigation, with `zerolith.route`, `url.path`,
|
|
36
|
+
* `url.query` when the URL has a query, and `zerolith.navigation.mode` as
|
|
37
|
+
* attributes, the loader's error recorded on failure, and
|
|
38
|
+
* `zerolith.navigation.canceled` set on a canceled one, whose status stays
|
|
39
|
+
* unset. The values of the query keys the URL conventions redact, such as
|
|
40
|
+
* `sig`, are recorded as `REDACTED`.
|
|
24
41
|
*/
|
|
25
42
|
export function navigationObserver(tracer: Tracer = defaultTracer()): (event: NavigationEvent) => void
|
|
26
43
|
{
|
|
@@ -29,9 +46,10 @@ export function navigationObserver(tracer: Tracer = defaultTracer()): (event: Na
|
|
|
29
46
|
{
|
|
30
47
|
if (event.type === "start")
|
|
31
48
|
{
|
|
49
|
+
const query = redactedQuery(event.url.search);
|
|
32
50
|
const span = tracer.startSpan("navigate", {
|
|
33
51
|
kind: SpanKind.INTERNAL,
|
|
34
|
-
attributes: { "url.path": event.url.pathname, "url.query":
|
|
52
|
+
attributes: { "url.path": event.url.pathname, ...(query === "" ? {} : { "url.query": query }), "zerolith.navigation.mode": event.mode }
|
|
35
53
|
});
|
|
36
54
|
open.set(event.id, span);
|
|
37
55
|
return;
|
|
@@ -47,6 +65,10 @@ export function navigationObserver(tracer: Tracer = defaultTracer()): (event: Na
|
|
|
47
65
|
span.setAttribute("zerolith.route", event.name ?? "");
|
|
48
66
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
49
67
|
}
|
|
68
|
+
else if (event.type === "cancel")
|
|
69
|
+
{
|
|
70
|
+
span.setAttribute("zerolith.navigation.canceled", true);
|
|
71
|
+
}
|
|
50
72
|
else
|
|
51
73
|
{
|
|
52
74
|
const error = event.error;
|
|
@@ -95,17 +117,23 @@ export function traceparent(active: Context = context.active()): string | undefi
|
|
|
95
117
|
|
|
96
118
|
/**
|
|
97
119
|
* Wraps a transport, or `fetch` when none is given, so every request runs
|
|
98
|
-
* in a client span named
|
|
99
|
-
* `url.full`, and `http.response.status_code`,
|
|
100
|
-
* header from that span, and records a failed
|
|
120
|
+
* in a client span named by its method, as `GET`, with
|
|
121
|
+
* `http.request.method`, `url.full`, and `http.response.status_code`,
|
|
122
|
+
* carries a `traceparent` header from that span, and records a failed
|
|
123
|
+
* request as an error. The span is not named by the request's path, which
|
|
124
|
+
* would give every note its own span name; the HTTP conventions name a
|
|
125
|
+
* client span by its method when no route template is known. `url.full`
|
|
126
|
+
* records the values of the query keys the conventions redact, such as
|
|
127
|
+
* `sig`, as `REDACTED`.
|
|
101
128
|
*/
|
|
102
129
|
export function tracedTransport(transport: Transport | undefined, tracer: Tracer = defaultTracer()): Transport
|
|
103
130
|
{
|
|
104
131
|
const send: Transport = transport ?? ((request) => fetch(request));
|
|
105
132
|
return (request) =>
|
|
106
133
|
{
|
|
107
|
-
const
|
|
108
|
-
|
|
134
|
+
const full = new URL(request.url);
|
|
135
|
+
full.search = redactedQuery(full.search);
|
|
136
|
+
return tracer.startActiveSpan(request.method, { kind: SpanKind.CLIENT, attributes: { "http.request.method": request.method, "url.full": full.href } }, async (span) =>
|
|
109
137
|
{
|
|
110
138
|
try
|
|
111
139
|
{
|