@xeonr/telemetry 0.0.0-stage → 0.2.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.
Files changed (56) hide show
  1. package/README.md +128 -2
  2. package/dist/baggage.d.ts +21 -0
  3. package/dist/baggage.d.ts.map +1 -0
  4. package/dist/baggage.js +117 -0
  5. package/dist/baggage.js.map +1 -0
  6. package/dist/boot.d.ts +7 -0
  7. package/dist/boot.d.ts.map +1 -0
  8. package/dist/boot.js +10 -0
  9. package/dist/boot.js.map +1 -0
  10. package/dist/connect.d.ts +63 -0
  11. package/dist/connect.d.ts.map +1 -0
  12. package/dist/connect.js +200 -0
  13. package/dist/connect.js.map +1 -0
  14. package/dist/fetch.d.ts +19 -0
  15. package/dist/fetch.d.ts.map +1 -0
  16. package/dist/fetch.js +41 -0
  17. package/dist/fetch.js.map +1 -0
  18. package/dist/identity.d.ts +79 -0
  19. package/dist/identity.d.ts.map +1 -0
  20. package/dist/identity.js +173 -0
  21. package/dist/identity.js.map +1 -0
  22. package/dist/index.d.ts +22 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +22 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/job.d.ts +38 -0
  27. package/dist/job.d.ts.map +1 -0
  28. package/dist/job.js +107 -0
  29. package/dist/job.js.map +1 -0
  30. package/dist/keys.d.ts +80 -0
  31. package/dist/keys.d.ts.map +1 -0
  32. package/dist/keys.js +90 -0
  33. package/dist/keys.js.map +1 -0
  34. package/dist/keys.json +127 -0
  35. package/dist/log.d.ts +43 -0
  36. package/dist/log.d.ts.map +1 -0
  37. package/dist/log.js +139 -0
  38. package/dist/log.js.map +1 -0
  39. package/dist/processor.d.ts +79 -0
  40. package/dist/processor.d.ts.map +1 -0
  41. package/dist/processor.js +163 -0
  42. package/dist/processor.js.map +1 -0
  43. package/dist/register.d.ts +2 -0
  44. package/dist/register.d.ts.map +1 -0
  45. package/dist/register.js +34 -0
  46. package/dist/register.js.map +1 -0
  47. package/dist/sanitize.d.ts +17 -0
  48. package/dist/sanitize.d.ts.map +1 -0
  49. package/dist/sanitize.js +52 -0
  50. package/dist/sanitize.js.map +1 -0
  51. package/dist/sdk.d.ts +50 -0
  52. package/dist/sdk.d.ts.map +1 -0
  53. package/dist/sdk.js +217 -0
  54. package/dist/sdk.js.map +1 -0
  55. package/package.json +97 -3
  56. package/register.cjs +3 -0
package/README.md CHANGED
@@ -1,3 +1,129 @@
1
- # Temporary Holding Version
1
+ # @xeonr/telemetry
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Telemetry for Xeonr's Node services: the Node mirror of go-platform's
4
+ `telemetry` package, and the way a Node service meets Plumb's
5
+ `obs.node-telemetry`.
6
+
7
+ ```bash
8
+ node --import @xeonr/telemetry/register dist/server.js # ES modules
9
+ node --require @xeonr/telemetry/register dist/server.cjs # CommonJS
10
+ ```
11
+
12
+ `register` starts OpenTelemetry before the app loads. The SDK itself is
13
+ [`@lntrn/otel`](https://www.npmjs.com/package/@lntrn/otel), Lantern's add-on
14
+ for the stock `NodeSDK`. This package makes the Xeonr choices on top:
15
+
16
+ - **OTLP gRPC** to the cluster collector, from `OTEL_EXPORTER_OTLP_ENDPOINT`
17
+ (Go-style `host:port` or a URL). In-cluster hosts and
18
+ `OTEL_EXPORTER_OTLP_INSECURE=true` are cleartext. With no endpoint nothing
19
+ is sent: one `telemetry export disabled` line, and spans still get ids so
20
+ log lines still carry them.
21
+ - **Resource** from `OTEL_SERVICE_NAME`, `OTEL_SERVICE_NAMESPACE`,
22
+ `OTEL_SERVICE_VERSION`, `ENV` (else `NODE_ENV`, as `prod`/`dev`) as
23
+ `deployment.environment`, and the pod name as `service.instance.id`.
24
+ - **AlwaysOn** sampling. **W3C trace context** is the only propagator:
25
+ baggage is sent only by this package's in-cluster helpers.
26
+ - **A fixed instrumentation list**, never `auto-instrumentations-node`: http,
27
+ undici (fetch), ioredis, mysql2 (statements masked), pg, aws-sdk,
28
+ runtime-node, and pino (trace ids on lines, never log export). `/healthz`
29
+ is not traced. Client spans need a parent: one without becomes nothing
30
+ rather than a root (`xeonr_telemetry_rootless_client_spans_total` counts
31
+ the ones dropped).
32
+ - **Runtime metrics**: runtime-node's event loop, heap and GC, and
33
+ host-metrics' `process.cpu.*` and `process.memory.*` (the pod, not the node).
34
+ - **No log export**: logs go to stdout for the Lantern agent. No HTTP RED
35
+ histograms: spanmetrics derives RED from the spans.
36
+ - **Identity on every span**, from the request or job it belongs to; URL
37
+ queries and credentials stripped from URL attributes.
38
+
39
+ ## Identity (§4.2)
40
+
41
+ ```ts
42
+ import { setIdentity, setTeam, setResource } from "@xeonr/telemetry";
43
+
44
+ // where the service verifies a Xeonr caller itself (its auth interceptor)
45
+ setIdentity({ subject: principal.sub, orgId, clientId, authMethod: "bearer", scopes });
46
+ // in a handler acting on an owned resource
47
+ setTeam(bucket.teamUrn);
48
+ setResource("bucket", bucket.id, bucket.urn);
49
+ ```
50
+
51
+ The keys are generated from go-platform's `telemetry/keys.json`
52
+ (`src/keys.json`, copied byte for byte; `pnpm gen-keys`).
53
+
54
+ ## Connect
55
+
56
+ ```ts
57
+ import { clientInterceptor, peerContextValues, serverInterceptor } from "@xeonr/telemetry/connect";
58
+
59
+ app.register(fastifyConnectPlugin, {
60
+ routes,
61
+ interceptors: [serverInterceptor(), authInterceptor], // telemetry first
62
+ contextValues: peerContextValues, // the TCP peer, for trust
63
+ });
64
+ const transport = createConnectTransport({ baseUrl, interceptors: [clientInterceptor({ peer: "upl-im-api" })] });
65
+ ```
66
+
67
+ The server continues a caller's `traceparent` and honours its origin baggage
68
+ only from a trusted peer (`TRUSTED_PROXY_CIDR`, default `10.0.0.0/8`, as
69
+ go-platform); anyone else's trace becomes a link. Status follows otelconnect:
70
+ only a server-class code is an error.
71
+
72
+ ## Jobs
73
+
74
+ ```ts
75
+ import { runJob, startJob, detach } from "@xeonr/telemetry";
76
+
77
+ await runJob("upl.trace.reconcile", () => reconcile(), { trigger: "cron" });
78
+ const job = startJob("upl.parse_upload", { trigger: "queue", links });
79
+ // … context.with(job.ctx, …) …
80
+ job.end(err);
81
+ void runJob("upl.index", () => index(id), { ctx: detach(), trigger: "request" }); // from a handler
82
+ ```
83
+
84
+ A job is a root span with `xeonr.job.*` and system identity (or the caller's,
85
+ when started from a request), and counts `xeonr_job_runs_total` and
86
+ `xeonr_job_duration_seconds`.
87
+
88
+ ## Outbound HTTP
89
+
90
+ ```ts
91
+ import { fetchWith, withPeer, baggageFor } from "@xeonr/telemetry";
92
+
93
+ const discord = fetchWith({ peer: "discord" }); // fetch with peer.service, no baggage
94
+ const api = fetchWith({ peer: "upl-im-api" }); // in-cluster: origin baggage too
95
+ await withPeer("s3", () => s3.send(cmd)); // any other client
96
+ ```
97
+
98
+ ## Logging
99
+
100
+ ```ts
101
+ import { logger, withContext } from "@xeonr/telemetry/log";
102
+
103
+ logger.info({ upload_id }, "parsed");
104
+ ```
105
+
106
+ pino, JSON on stdout as logrus writes it (`level`, `time`, `msg`), with
107
+ `trace_id`, `span_id`, `trace_flags`, the §4.2 fields, `procedure` and `ip`
108
+ from the active context; credentials redacted by key; errors as
109
+ `{type, message, stack, code}` with tokens, emails and URL queries removed.
110
+ An Error line inside a span is mirrored as a span event.
111
+
112
+ ## Shutting down
113
+
114
+ `register` flushes when the event loop empties. A service that handles
115
+ SIGTERM itself calls `shutdown()` last, after its servers and workers have
116
+ drained; one that does not gets a flush and the signal re-raised.
117
+
118
+ ## Run-time span processors
119
+
120
+ `addSpanProcessor(p)` adds a processor after boot (the upl-im pipeline's
121
+ run-trace store). It sees the spans started after it. A span event made with
122
+ `localEvent(attrs)` reaches those processors but is never exported: for
123
+ payloads a product shows its own users, which must not reach Lantern.
124
+
125
+ ## Releasing
126
+
127
+ Bump `"version"` and merge to main: CI (`.xeonr-ci.yaml`, app `telemetry`)
128
+ tests, builds and publishes any version the registry hasn't got, and tags it
129
+ `telemetry-vX.Y.Z`.
@@ -0,0 +1,21 @@
1
+ import { type Context } from "@opentelemetry/api";
2
+ export declare const HEADER_BAGGAGE = "baggage";
3
+ /** go-platform's connectmw.DefaultTrustedProxyCIDR. */
4
+ export declare const DEFAULT_TRUSTED_PROXY_CIDR = "10.0.0.0/8";
5
+ /** Whether `host` (a host or host:port) is a cluster-internal name: `*.svc`, `*.cluster.local`, or a bare single-label name. IPs and localhost are not. */
6
+ export declare function inCluster(host: string): boolean;
7
+ /** The `baggage` header value for the origin members of `ctx`'s identity, or "" when there are none. */
8
+ export declare function originBaggage(ctx?: Context): string;
9
+ /** Parses a `baggage` header into the origin members, ignoring every other member. */
10
+ export declare function parseOriginBaggage(header: string | null | undefined): Record<string, string>;
11
+ /** Decides peer trust from CIDRs, like go-platform's ClientIPResolver.PeerTrusted. */
12
+ export declare class PeerTrust {
13
+ #private;
14
+ /** No CIDRs → DEFAULT_TRUSTED_PROXY_CIDR. An invalid entry throws: a bad trust range must fail at boot. */
15
+ constructor(cidrs?: readonly string[]);
16
+ /** From TRUSTED_PROXY_CIDR (comma-separated), as the Go services read it. */
17
+ static fromEnv(env?: Record<string, string | undefined>): PeerTrust;
18
+ /** Whether the TCP peer (an IP, "ip:port" or "[v6]:port") is trusted. An unparseable address is not. */
19
+ trusted(remoteAddress: string | undefined): boolean;
20
+ }
21
+ //# sourceMappingURL=baggage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baggage.d.ts","sourceRoot":"","sources":["../src/baggage.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAI3D,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,uDAAuD;AACvD,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,2JAA2J;AAC3J,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQ/C;AAED,wGAAwG;AACxG,wBAAgB,aAAa,CAAC,GAAG,GAAE,OAA0B,GAAG,MAAM,CAmBrE;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAiB5F;AAED,sFAAsF;AACtF,qBAAa,SAAS;;IAGrB,2GAA2G;gBAC/F,KAAK,GAAE,SAAS,MAAM,EAAO;IAazC,6EAA6E;IAC7E,MAAM,CAAC,OAAO,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,SAAS;IAIhF,wGAAwG;IACxG,OAAO,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO;CAUnD"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Origin baggage and peer trust (observability plan §5.4), as go-platform's
3
+ * telemetry/baggage.go and connectmw.ClientIPResolver decide them.
4
+ *
5
+ * - Members: xeonr.origin.subject, xeonr.origin.service, xeonr.org.id. Nothing else.
6
+ * - Sent only toward in-cluster hosts; any other destination gets none.
7
+ * - Honoured only from a trusted TCP peer (TRUSTED_PROXY_CIDR, default
8
+ * 10.0.0.0/8: the edge and in-cluster callers). From anyone else it is
9
+ * ignored, and so is their traceparent.
10
+ */
11
+ import { BlockList, isIP } from "node:net";
12
+ import { context } from "@opentelemetry/api";
13
+ import { stateFrom } from "./identity.js";
14
+ import { BAGGAGE_KEYS } from "./keys.js";
15
+ export const HEADER_BAGGAGE = "baggage";
16
+ /** go-platform's connectmw.DefaultTrustedProxyCIDR. */
17
+ export const DEFAULT_TRUSTED_PROXY_CIDR = "10.0.0.0/8";
18
+ /** Whether `host` (a host or host:port) is a cluster-internal name: `*.svc`, `*.cluster.local`, or a bare single-label name. IPs and localhost are not. */
19
+ export function inCluster(host) {
20
+ let h = host.trim().toLowerCase();
21
+ if (h.startsWith("["))
22
+ h = h.slice(1, h.indexOf("]"));
23
+ else if (h.split(":").length === 2)
24
+ h = h.split(":")[0] ?? "";
25
+ h = h.replace(/\.$/, "");
26
+ if (!h || h === "localhost" || isIP(h))
27
+ return false;
28
+ if (h.endsWith(".svc") || h.endsWith(".svc.cluster.local") || h.endsWith(".cluster.local"))
29
+ return true;
30
+ return !h.includes(".");
31
+ }
32
+ /** The `baggage` header value for the origin members of `ctx`'s identity, or "" when there are none. */
33
+ export function originBaggage(ctx = context.active()) {
34
+ const id = stateFrom(ctx)?.identity();
35
+ if (!id)
36
+ return "";
37
+ const values = {
38
+ "xeonr.origin.subject": id.originSubject,
39
+ "xeonr.origin.service": id.originService,
40
+ "xeonr.org.id": id.orgId,
41
+ };
42
+ const entries = {};
43
+ for (const k of BAGGAGE_KEYS) {
44
+ const v = values[k];
45
+ if (v)
46
+ entries[k] = { value: v };
47
+ }
48
+ if (Object.keys(entries).length === 0)
49
+ return "";
50
+ // The global propagator is W3C trace context only (§5.4): baggage is never
51
+ // injected by an instrumentation, only by these helpers.
52
+ return Object.entries(entries)
53
+ .map(([k, { value }]) => `${k}=${encodeURIComponent(value)}`)
54
+ .join(",");
55
+ }
56
+ /** Parses a `baggage` header into the origin members, ignoring every other member. */
57
+ export function parseOriginBaggage(header) {
58
+ const out = {};
59
+ if (!header)
60
+ return out;
61
+ for (const part of header.split(",")) {
62
+ const [kv] = part.split(";");
63
+ const i = kv?.indexOf("=") ?? -1;
64
+ if (!kv || i < 0)
65
+ continue;
66
+ const k = kv.slice(0, i).trim();
67
+ if (!BAGGAGE_KEYS.includes(k))
68
+ continue;
69
+ try {
70
+ const v = decodeURIComponent(kv.slice(i + 1).trim());
71
+ if (v)
72
+ out[k] = v;
73
+ }
74
+ catch {
75
+ // A malformed member is dropped, as Go's baggage.Parse would drop the header.
76
+ }
77
+ }
78
+ return out;
79
+ }
80
+ /** Decides peer trust from CIDRs, like go-platform's ClientIPResolver.PeerTrusted. */
81
+ export class PeerTrust {
82
+ #list = new BlockList();
83
+ /** No CIDRs → DEFAULT_TRUSTED_PROXY_CIDR. An invalid entry throws: a bad trust range must fail at boot. */
84
+ constructor(cidrs = []) {
85
+ const valid = cidrs.map((c) => c.trim()).filter(Boolean);
86
+ for (const c of valid.length ? valid : [DEFAULT_TRUSTED_PROXY_CIDR]) {
87
+ const [addr, bits] = c.split("/");
88
+ const family = addr && isIP(addr);
89
+ const prefix = Number(bits);
90
+ if (!addr || !family || bits === undefined || !Number.isInteger(prefix) || prefix < 0 || prefix > (family === 4 ? 32 : 128)) {
91
+ throw new Error(`@xeonr/telemetry: trusted proxy CIDR ${JSON.stringify(c)} is invalid`);
92
+ }
93
+ this.#list.addSubnet(addr, prefix, family === 4 ? "ipv4" : "ipv6");
94
+ }
95
+ }
96
+ /** From TRUSTED_PROXY_CIDR (comma-separated), as the Go services read it. */
97
+ static fromEnv(env = process.env) {
98
+ return new PeerTrust((env.TRUSTED_PROXY_CIDR ?? "").split(","));
99
+ }
100
+ /** Whether the TCP peer (an IP, "ip:port" or "[v6]:port") is trusted. An unparseable address is not. */
101
+ trusted(remoteAddress) {
102
+ if (!remoteAddress)
103
+ return false;
104
+ let ip = remoteAddress.trim();
105
+ if (ip.startsWith("["))
106
+ ip = ip.slice(1, ip.indexOf("]"));
107
+ else if (!isIP(ip) && ip.split(":").length === 2)
108
+ ip = ip.split(":")[0] ?? "";
109
+ if (ip.startsWith("::ffff:") && isIP(ip.slice(7)) === 4)
110
+ ip = ip.slice(7);
111
+ const family = isIP(ip);
112
+ if (!family)
113
+ return false;
114
+ return this.#list.check(ip, family === 4 ? "ipv4" : "ipv6");
115
+ }
116
+ }
117
+ //# sourceMappingURL=baggage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baggage.js","sourceRoot":"","sources":["../src/baggage.ts"],"names":[],"mappings":"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"}
package/dist/boot.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The boot lines `init` writes, in the logger's shape, without loading pino:
3
+ * `register` runs before the app, and pino must be imported after the
4
+ * instrumentations are enabled for the pino instrumentation to see it.
5
+ */
6
+ export declare function bootLine(level: "info" | "warn" | "error", msg: string, fields?: Record<string, unknown>): void;
7
+ //# sourceMappingURL=boot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boot.d.ts","sourceRoot":"","sources":["../src/boot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI,CAGlH"}
package/dist/boot.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The boot lines `init` writes, in the logger's shape, without loading pino:
3
+ * `register` runs before the app, and pino must be imported after the
4
+ * instrumentations are enabled for the pino instrumentation to see it.
5
+ */
6
+ export function bootLine(level, msg, fields = {}) {
7
+ const clean = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined && v !== ""));
8
+ process.stdout.write(JSON.stringify({ level, time: new Date().toISOString(), msg, ...clean }) + "\n");
9
+ }
10
+ //# sourceMappingURL=boot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boot.js","sourceRoot":"","sources":["../src/boot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAgC,EAAE,GAAW,EAAE,SAAkC,EAAE;IAC3G,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACxG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AACvG,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `@xeonr/telemetry/connect`: Connect ^2 interceptors, the Node counterparts
3
+ * of go-platform's telemetry.ServerInterceptor (with otelconnect and
4
+ * RemoteParent) and telemetry.ClientInterceptor.
5
+ *
6
+ * Server: one SERVER span per call named `pkg.Service/Method`, with
7
+ * `rpc.system=connect_rpc`, `rpc.service` and `rpc.method` = the full
8
+ * procedure (as Lantern shows `rpc.method` for the Go services, and what
9
+ * dashboards and monitors filter on), and otelconnect's status rule (only a server-class code is an error; the code is in
10
+ * `rpc.connect_rpc.error_code`). A remote traceparent is continued only from a
11
+ * trusted peer (TRUSTED_PROXY_CIDR, default 10.0.0.0/8); anyone else's becomes
12
+ * a link. Origin baggage is honoured only from a trusted peer too. The call
13
+ * runs with a fresh identity record: the service's auth layer fills it with
14
+ * `setIdentity`, and it is stamped on the server span when the call ends.
15
+ *
16
+ * Client: one CLIENT span per call, `traceparent` on the request, origin
17
+ * baggage only toward an in-cluster host, `peer.service` from the option.
18
+ *
19
+ * Put the server interceptor first, ahead of auth, so a refused call is still
20
+ * a span with `subject.kind=anonymous`.
21
+ */
22
+ import { type ContextValues, type Interceptor } from "@connectrpc/connect";
23
+ import { PeerTrust } from "./baggage.ts";
24
+ /** The TCP peer of the call, for the trust decision. Set it with `peerContextValues`. */
25
+ export declare const kPeerAddress: import("@connectrpc/connect").ContextKey<string | undefined>;
26
+ type RequestLike = {
27
+ socket?: {
28
+ remoteAddress?: string | undefined;
29
+ } | undefined;
30
+ raw?: {
31
+ socket?: {
32
+ remoteAddress?: string | undefined;
33
+ } | undefined;
34
+ };
35
+ };
36
+ /**
37
+ * Context values carrying the TCP peer, for the Connect adapter's
38
+ * `contextValues` option (`@connectrpc/connect-fastify`, `connect-node`,
39
+ * `connect-express` all pass the request):
40
+ *
41
+ * ```ts
42
+ * app.register(fastifyConnectPlugin, { routes, interceptors, contextValues: peerContextValues });
43
+ * ```
44
+ */
45
+ export declare function peerContextValues(req: RequestLike): ContextValues;
46
+ export interface ServerInterceptorOptions {
47
+ /** Who may continue a trace and send baggage. Default: TRUSTED_PROXY_CIDR, else 10.0.0.0/8. */
48
+ trust?: PeerTrust;
49
+ /** `service.name` for the origin of a trace this service starts. Default OTEL_SERVICE_NAME. */
50
+ serviceName?: string;
51
+ }
52
+ export declare function serverInterceptor(opts?: ServerInterceptorOptions): Interceptor;
53
+ export interface ClientInterceptorOptions {
54
+ /** `peer.service`: the service being called (`upl-im-api`). */
55
+ peer?: string;
56
+ /** Which hosts are in-cluster and get origin baggage. Default `inCluster`. */
57
+ inCluster?: (host: string) => boolean;
58
+ }
59
+ export declare function clientInterceptor(opts?: ClientInterceptorOptions): Interceptor;
60
+ /** For tests and adapters without `contextValues`: values carrying a given peer. */
61
+ export declare function contextValuesWithPeer(peer: string): ContextValues;
62
+ export {};
63
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAKN,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAgE,SAAS,EAAE,MAAM,cAAc,CAAC;AAWvG,yFAAyF;AACzF,eAAO,MAAM,YAAY,8DAA0G,CAAC;AAEpI,KAAK,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;IAAC,GAAG,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE;YAAE,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SAAE,GAAG,SAAS,CAAA;KAAE,CAAA;CAAE,CAAC;AAE1J;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,aAAa,CAIjE;AAED,MAAM,WAAW,wBAAwB;IACxC,+FAA+F;IAC/F,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,wBAA6B,GAAG,WAAW,CA2DlF;AAED,MAAM,WAAW,wBAAwB;IACxC,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,wBAA6B,GAAG,WAAW,CAsClF;AA4CD,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAIjE"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * `@xeonr/telemetry/connect`: Connect ^2 interceptors, the Node counterparts
3
+ * of go-platform's telemetry.ServerInterceptor (with otelconnect and
4
+ * RemoteParent) and telemetry.ClientInterceptor.
5
+ *
6
+ * Server: one SERVER span per call named `pkg.Service/Method`, with
7
+ * `rpc.system=connect_rpc`, `rpc.service` and `rpc.method` = the full
8
+ * procedure (as Lantern shows `rpc.method` for the Go services, and what
9
+ * dashboards and monitors filter on), and otelconnect's status rule (only a server-class code is an error; the code is in
10
+ * `rpc.connect_rpc.error_code`). A remote traceparent is continued only from a
11
+ * trusted peer (TRUSTED_PROXY_CIDR, default 10.0.0.0/8); anyone else's becomes
12
+ * a link. Origin baggage is honoured only from a trusted peer too. The call
13
+ * runs with a fresh identity record: the service's auth layer fills it with
14
+ * `setIdentity`, and it is stamped on the server span when the call ends.
15
+ *
16
+ * Client: one CLIENT span per call, `traceparent` on the request, origin
17
+ * baggage only toward an in-cluster host, `peer.service` from the option.
18
+ *
19
+ * Put the server interceptor first, ahead of auth, so a refused call is still
20
+ * a span with `subject.kind=anonymous`.
21
+ */
22
+ import { Code, ConnectError, createContextKey as createConnectContextKey, createContextValues, } from "@connectrpc/connect";
23
+ import { context, propagation, ROOT_CONTEXT, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
24
+ import { HEADER_BAGGAGE, inCluster, originBaggage, parseOriginBaggage, PeerTrust } from "./baggage.js";
25
+ import { attributes, State, withState } from "./identity.js";
26
+ import { KeyScopes } from "./keys.js";
27
+ import { KEY_PEER_SERVICE } from "./processor.js";
28
+ import { sanitize } from "./sanitize.js";
29
+ const TRACER = "@xeonr/telemetry/connect";
30
+ /** otelconnect's server-class codes: only these mark a server span as an error. */
31
+ const SERVER_ERROR_CODES = new Set([Code.Unknown, Code.DeadlineExceeded, Code.Unimplemented, Code.Internal, Code.Unavailable, Code.DataLoss]);
32
+ /** The TCP peer of the call, for the trust decision. Set it with `peerContextValues`. */
33
+ export const kPeerAddress = createConnectContextKey(undefined, { description: "xeonr.telemetry.peer_address" });
34
+ /**
35
+ * Context values carrying the TCP peer, for the Connect adapter's
36
+ * `contextValues` option (`@connectrpc/connect-fastify`, `connect-node`,
37
+ * `connect-express` all pass the request):
38
+ *
39
+ * ```ts
40
+ * app.register(fastifyConnectPlugin, { routes, interceptors, contextValues: peerContextValues });
41
+ * ```
42
+ */
43
+ export function peerContextValues(req) {
44
+ const values = createContextValues();
45
+ values.set(kPeerAddress, req.socket?.remoteAddress ?? req.raw?.socket?.remoteAddress);
46
+ return values;
47
+ }
48
+ export function serverInterceptor(opts = {}) {
49
+ const trust = opts.trust ?? PeerTrust.fromEnv();
50
+ const service = opts.serviceName ?? process.env.OTEL_SERVICE_NAME?.trim() ?? "";
51
+ return (next) => async (req) => {
52
+ const procedure = `${req.service.typeName}/${req.method.name}`;
53
+ const peer = req.contextValues.get(kPeerAddress);
54
+ const trusted = trust.trusted(peer);
55
+ const s = new State();
56
+ s.service = service;
57
+ s.procedure = procedure;
58
+ s.ip = (trusted ? req.header.get("x-real-ip")?.trim() : stripPort(peer)) ?? "";
59
+ decideOrigin(s, req.header.get(HEADER_BAGGAGE), trusted);
60
+ // Under an HTTP server span (connect-node on HTTP/1.1, which the http
61
+ // instrumentation sees), the call is its child. Otherwise (HTTP/2, which
62
+ // it does not see) the remote parent is decided here.
63
+ const links = [];
64
+ let parent = context.active();
65
+ const local = trace.getSpanContext(parent);
66
+ if (!local || !trace.isSpanContextValid(local)) {
67
+ const getter = { get: (h, k) => h.get(k) ?? undefined, keys: (h) => [...h.keys()] };
68
+ const remoteSc = trace.getSpanContext(propagation.extract(ROOT_CONTEXT, req.header, getter));
69
+ if (remoteSc && trace.isSpanContextValid(remoteSc)) {
70
+ if (trusted)
71
+ parent = trace.setSpanContext(parent, remoteSc);
72
+ else
73
+ links.push({ context: remoteSc });
74
+ }
75
+ }
76
+ parent = withState(parent, s);
77
+ const span = trace.getTracer(TRACER).startSpan(procedure, {
78
+ kind: SpanKind.SERVER,
79
+ links,
80
+ attributes: { "rpc.system": "connect_rpc", "rpc.service": req.service.typeName, "rpc.method": procedure },
81
+ }, parent);
82
+ s.root = span;
83
+ const ctx = trace.setSpan(parent, span);
84
+ const finish = (err) => {
85
+ const id = s.identity();
86
+ span.setAttributes(attributes(id));
87
+ if (id.scopes?.length)
88
+ span.setAttribute(KeyScopes, id.scopes);
89
+ if (err !== undefined)
90
+ serverError(span, err);
91
+ span.end();
92
+ };
93
+ try {
94
+ const res = await context.with(ctx, () => next(req));
95
+ if (res.stream)
96
+ return { ...res, message: endWhenDone(res.message, finish) };
97
+ finish();
98
+ return res;
99
+ }
100
+ catch (err) {
101
+ finish(err);
102
+ throw err;
103
+ }
104
+ };
105
+ }
106
+ export function clientInterceptor(opts = {}) {
107
+ const isInCluster = opts.inCluster ?? inCluster;
108
+ return (next) => async (req) => {
109
+ const procedure = `${req.service.typeName}/${req.method.name}`;
110
+ const attrs = { "rpc.system": "connect_rpc", "rpc.service": req.service.typeName, "rpc.method": procedure };
111
+ if (opts.peer)
112
+ attrs[KEY_PEER_SERVICE] = opts.peer;
113
+ const span = trace.getTracer(TRACER).startSpan(procedure, { kind: SpanKind.CLIENT, attributes: attrs });
114
+ const ctx = trace.setSpan(context.active(), span);
115
+ propagation.inject(ctx, req.header, { set: (h, k, v) => h.set(k, v) });
116
+ let host = "";
117
+ try {
118
+ host = new URL(req.url).host;
119
+ }
120
+ catch {
121
+ // An unparseable URL is not in-cluster.
122
+ }
123
+ const b = isInCluster(host) ? originBaggage(ctx) : "";
124
+ if (b)
125
+ req.header.set(HEADER_BAGGAGE, b);
126
+ else
127
+ req.header.delete(HEADER_BAGGAGE);
128
+ const finish = (err) => {
129
+ if (err !== undefined) {
130
+ const ce = ConnectError.from(err);
131
+ span.setAttribute("rpc.connect_rpc.error_code", Code[ce.code]);
132
+ span.setStatus({ code: SpanStatusCode.ERROR, message: sanitize(ce.rawMessage) });
133
+ }
134
+ span.end();
135
+ };
136
+ try {
137
+ const res = await context.with(ctx, () => next(req));
138
+ if (res.stream)
139
+ return { ...res, message: endWhenDone(res.message, finish) };
140
+ finish();
141
+ return res;
142
+ }
143
+ catch (err) {
144
+ finish(err);
145
+ throw err;
146
+ }
147
+ };
148
+ }
149
+ function serverError(span, err) {
150
+ // What the client sees: anything not a ConnectError goes out as internal.
151
+ const ce = err instanceof ConnectError ? err : ConnectError.from(err, Code.Internal);
152
+ span.setAttribute("rpc.connect_rpc.error_code", Code[ce.code]);
153
+ if (!SERVER_ERROR_CODES.has(ce.code))
154
+ return;
155
+ const message = sanitize(ce.rawMessage);
156
+ span.addEvent("exception", { "exception.type": err instanceof Error ? err.name : "ConnectError", "exception.message": message });
157
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
158
+ }
159
+ async function* endWhenDone(messages, finish) {
160
+ try {
161
+ yield* messages;
162
+ finish();
163
+ }
164
+ catch (err) {
165
+ finish(err);
166
+ throw err;
167
+ }
168
+ }
169
+ /** go-platform's state.decideOrigin: the first hop with no trusted origin baggage is the entry. */
170
+ function decideOrigin(s, header, trusted) {
171
+ s.originDecided = true;
172
+ s.entry = true;
173
+ if (!trusted || !header)
174
+ return;
175
+ const b = parseOriginBaggage(header);
176
+ const subject = b["xeonr.origin.subject"];
177
+ const service = b["xeonr.origin.service"];
178
+ if (subject || service) {
179
+ s.entry = false;
180
+ if (subject)
181
+ s.id.originSubject = subject;
182
+ if (service)
183
+ s.id.originService = service;
184
+ }
185
+ s.baggageOrgId = b["xeonr.org.id"] ?? "";
186
+ }
187
+ function stripPort(addr) {
188
+ if (!addr)
189
+ return undefined;
190
+ if (addr.startsWith("["))
191
+ return addr.slice(1, addr.indexOf("]"));
192
+ return addr.split(":").length === 2 ? addr.split(":")[0] : addr;
193
+ }
194
+ /** For tests and adapters without `contextValues`: values carrying a given peer. */
195
+ export function contextValuesWithPeer(peer) {
196
+ const values = createContextValues();
197
+ values.set(kPeerAddress, peer);
198
+ return values;
199
+ }
200
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface FetchOptions {
2
+ /** `peer.service`: who is being called (`discord`, `upl-im-api`). */
3
+ peer?: string;
4
+ /** Send origin baggage when the host is in-cluster (go-platform's WithOriginBaggage). Default true. */
5
+ baggage?: boolean;
6
+ /** Which hosts are in-cluster. Default `inCluster`. */
7
+ inCluster?: (host: string) => boolean;
8
+ /** The fetch to wrap. Default `globalThis.fetch`. */
9
+ fetch?: typeof fetch;
10
+ }
11
+ /** A `fetch` for one peer. */
12
+ export declare function fetchWith(opts?: FetchOptions): typeof fetch;
13
+ /**
14
+ * The `baggage` header for a request to `url`: the origin members when the
15
+ * host is in-cluster, else "". For HTTP clients other than fetch (axios,
16
+ * got): set it when non-empty, and run the call inside `withPeer`.
17
+ */
18
+ export declare function baggageFor(url: string, isInCluster?: (host: string) => boolean): string;
19
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC5B,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uGAAuG;IACvG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACrB;AAED,8BAA8B;AAC9B,wBAAgB,SAAS,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,KAAK,CAW/D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAmB,GAAG,MAAM,CAQlG"}