| 1 | import { Effect } from "effect" |
| 2 | import * as Headers from "effect/http/Headers" |
| 3 | import * as HttpMiddleware from "effect/http/HttpMiddleware" |
| 4 | import * as HttpServerRequest from "effect/http/HttpServerRequest" |
| 5 | import * as HttpServerResponse from "effect/http/HttpServerResponse" |
| 6 | import * as HttpTraceContext from "effect/http/HttpTraceContext" |
| 7 | import * as Tracer from "effect/Tracer" |
| 8 | import { Bench } from "tinybench" |
| 9 | |
| 10 | const bench = new Bench() |
| 11 | |
| 12 | const noopTracer = Tracer.make({ |
| 13 | span: (options) => |
| 14 | ({ |
| 15 | _tag: "Span", |
| 16 | spanId: "spanid0123456789", |
| 17 | traceId: "traceid0123456789traceid01234567", |
| 18 | sampled: true, |
| 19 | name: options.name, |
| 20 | parent: options.parent, |
| 21 | annotations: options.annotations, |
| 22 | links: options.links, |
| 23 | kind: options.kind, |
| 24 | status: { _tag: "Started", startTime: options.startTime }, |
| 25 | attributes: new Map(), |
| 26 | attribute() {}, |
| 27 | event() {}, |
| 28 | end() {}, |
| 29 | addLinks() {} |
| 30 | }) as Tracer.Span |
| 31 | }) |
| 32 | |
| 33 | const request = HttpServerRequest.fromWeb( |
| 34 | new Request("http://localhost:3000/some/path?foo=bar", { |
| 35 | headers: { |
| 36 | "user-agent": "bench/1.0", |
| 37 | "accept": "application/json", |
| 38 | "accept-encoding": "gzip, br", |
| 39 | "authorization": "Bearer secret", |
| 40 | "cookie": "session=abc", |
| 41 | "x-request-id": "12345678", |
| 42 | "host": "localhost:3000" |
| 43 | } |
| 44 | }) |
| 45 | ) |
| 46 | |
| 47 | const requestWithTraceparent = HttpServerRequest.fromWeb( |
| 48 | new Request("http://localhost:3000/some/path?foo=bar", { |
| 49 | headers: { |
| 50 | "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", |
| 51 | "host": "localhost:3000" |
| 52 | } |
| 53 | }) |
| 54 | ) |
| 55 | |
| 56 | const app = Effect.succeed(HttpServerResponse.text("ok")) |
| 57 | |
| 58 | const tracedApp = HttpMiddleware.tracer(app).pipe( |
| 59 | Effect.provideService(HttpServerRequest.HttpServerRequest, request), |
| 60 | Effect.provideService(Tracer.Tracer, noopTracer) |
| 61 | ) |
| 62 | |
| 63 | const tracedAppNative = HttpMiddleware.tracer(app).pipe( |
| 64 | Effect.provideService(HttpServerRequest.HttpServerRequest, request) |
| 65 | ) |
| 66 | |
| 67 | const tracedAppDisabled = HttpMiddleware.tracer(app).pipe( |
| 68 | Effect.provideService(HttpServerRequest.HttpServerRequest, request), |
| 69 | Effect.withTracerEnabled(false) |
| 70 | ) |
| 71 | |
| 72 | const bareApp = app.pipe( |
| 73 | Effect.provideService(HttpServerRequest.HttpServerRequest, request) |
| 74 | ) |
| 75 | |
| 76 | const withSpanApp = Effect.void.pipe( |
| 77 | Effect.withSpan("bench"), |
| 78 | Effect.provideService(Tracer.Tracer, noopTracer) |
| 79 | ) |
| 80 | |
| 81 | const withSpanAppNoStack = Effect.void.pipe( |
| 82 | Effect.withSpan("bench", undefined, { captureStackTrace: false }), |
| 83 | Effect.provideService(Tracer.Tracer, noopTracer) |
| 84 | ) |
| 85 | |
| 86 | const withSpanDisabled = Effect.void.pipe( |
| 87 | Effect.withSpan("bench"), |
| 88 | Effect.withTracerEnabled(false) |
| 89 | ) |
| 90 | |
| 91 | bench |
| 92 | .add("bare app (no middleware)", async () => { |
| 93 | await Effect.runPromise(bareApp) |
| 94 | }) |
| 95 | .add("tracer middleware (noop tracer)", async () => { |
| 96 | await Effect.runPromise(tracedApp) |
| 97 | }) |
| 98 | .add("tracer middleware (NativeSpan)", async () => { |
| 99 | await Effect.runPromise(tracedAppNative) |
| 100 | }) |
| 101 | .add("tracer middleware (tracing disabled)", async () => { |
| 102 | await Effect.runPromise(tracedAppDisabled) |
| 103 | }) |
| 104 | .add("withSpan reused (noop tracer)", async () => { |
| 105 | await Effect.runPromise(withSpanApp) |
| 106 | }) |
| 107 | .add("withSpan constructed per run (noop tracer)", async () => { |
| 108 | await Effect.runPromise( |
| 109 | Effect.void.pipe( |
| 110 | Effect.withSpan("bench"), |
| 111 | Effect.provideService(Tracer.Tracer, noopTracer) |
| 112 | ) |
| 113 | ) |
| 114 | }) |
| 115 | .add("withSpan constructed per run, no stack capture", async () => { |
| 116 | await Effect.runPromise( |
| 117 | Effect.void.pipe( |
| 118 | Effect.withSpan("bench", undefined, { captureStackTrace: false }), |
| 119 | Effect.provideService(Tracer.Tracer, noopTracer) |
| 120 | ) |
| 121 | ) |
| 122 | }) |
| 123 | .add("withSpan reused, no stack capture (noop tracer)", async () => { |
| 124 | await Effect.runPromise(withSpanAppNoStack) |
| 125 | }) |
| 126 | .add("withSpan reused (tracing disabled)", async () => { |
| 127 | await Effect.runPromise(withSpanDisabled) |
| 128 | }) |
| 129 | .add("fromHeaders: no trace headers", () => { |
| 130 | HttpTraceContext.fromHeaders(request.headers) |
| 131 | }) |
| 132 | .add("fromHeaders: traceparent", () => { |
| 133 | HttpTraceContext.fromHeaders(requestWithTraceparent.headers) |
| 134 | }) |
| 135 | .add("Headers.redact (7 headers, default names)", () => { |
| 136 | Headers.redact(request.headers, ["authorization", "cookie", "set-cookie", "x-api-key"]) |
| 137 | }) |
| 138 | .add("Request.toURL", () => { |
| 139 | HttpServerRequest.toURL(request) |
| 140 | }) |
| 141 | |
| 142 | await bench.run() |
| 143 | console.table(bench.table()) |