fix(session-telemetry-otel): keep gzip on the fetch transport
The previous commit moved the OTLP exporter to the SDK's `fetch` delegate and refused `exporter.compression` at load, on the belief that nothing shipped enabled it. `packages/bundle/base/cordis.patch.yml` does, so the refusal broke every test that boots the shipped bundle — the snapshot, e2e, and Windows observational jobs all failed on that one load error. Dropping gzip was the wrong trade anyway: a realistic OTLP batch measures 6.4x smaller with it, so trading it for proxy support would have charged every deployment to fix one. The `fetch` transport has no compression hook, but serialization is the seam before the body reaches it — the plugin now gzips there and declares `Content-Encoding` itself. `keepAlive` and `httpAgentOptions` have no such seam, since they configure a connection pool `fetch` does not expose, so those two stay refused at load rather than accepted and ignored. `compression` is typed as the two values this package can actually apply rather than the SDK's wider enum, and a third value fails loud at load.
This commit is contained in:
1 parent
bdd294dc47
commit
b518286735
8 files changed
+179
-29
No files matched your search
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.md
|
||||
2026-08-27-outbound-proxy-policy.md: 88bfe542d322d2caee5f5e220ed211e0169fa614
|
||||
2026-08-27-outbound-proxy-policy.zh.md: f5afee9de40e2032cfd4eda3bc9bfb1c627e71aa
|
||||
2026-08-27-outbound-proxy-policy.md: 9c58dfd00d9b6c0b4438ad3dd4da58d8706f718f
|
||||
2026-08-27-outbound-proxy-policy.zh.md: 1aabc716c1bdf348229b260c4d8580fb4edc8637
|
||||
@@ -50,7 +50,9 @@ This accepts a documented seam. Such a context matches bypass entries by Node's
|
||||
|
||||
The exporter now composes `OTLPExporterBase` with `createLegacyOtlpBrowserExportDelegate` — a published entry point of the same SDK package, and the one that posts through `fetch`. E2B is handed `route.proxy` from `proxyRouteFor`, the same call `web-fetch-http` makes.
|
||||
|
||||
Switching the exporter to `fetch` costs `compression`: gzip belongs to the SDK's Node transport, and a realistic OTLP batch measured 6.4x smaller with it. Nothing shipped enabled it, and telemetry that ignores the proxy simply fails inside a corporate network, so routing wins. What the exporter would silently ignore, the plugin now refuses at load — `exporter.compression`, `exporter.keepAlive`, and `exporter.httpAgentOptions` throw with the reason, so no deployment pays the difference without seeing it. In exchange the Node-version floor disappears: `proxyEnv` on an `http.Agent` needs 22.21 or 24.5, inside the engines range, so telemetry used to stay direct on 22.19, 22.20, and 24.0–24.4.
|
||||
The `fetch` transport has no compression, and the shipped `base` bundle enables gzip — a realistic OTLP batch measures 6.4x smaller with it. Dropping it to gain proxy support would have traded one deployment's problem for every deployment's, and the first attempt did exactly that: it refused `exporter.compression` at load, which broke every test that boots the shipped bundle. This package gzips at the serializer instead, the one seam before the body reaches the transport, and declares `Content-Encoding` itself. `keepAlive` and `httpAgentOptions` have no such seam — they configure a connection pool `fetch` does not expose — so those two are refused at load rather than accepted and ignored.
|
||||
|
||||
In exchange the Node-version floor disappears: `proxyEnv` on an `http.Agent` needs 22.21 or 24.5, inside the engines range, so telemetry used to stay direct on 22.19, 22.20, and 24.0–24.4.
|
||||
|
||||
**Every call site carries an egress test, because reading the code was not enough.** `egress.spec.ts` in each owning package drives that site's real code path at an unresolvable `.invalid` host through a fake proxy and asserts the proxy saw the request. Nine of them cover the search backends, pi-ai discovery, MCP over HTTP, telemetry, E2B, a spawned child Node, and a worker thread. The gate below cannot see inside a dependency; these can, and they are what turns "an SDK changed its transport" from a silent regression into a failing test.
|
||||
|
||||
|
||||
@@ -50,7 +50,9 @@ URL 层策略未受影响:仅 `http(s)`、禁止内嵌凭据、长度上限与
|
||||
|
||||
导出器改为用 `OTLPExporterBase` 组合 `createLegacyOtlpBrowserExportDelegate`——同一个 SDK 包的公开入口,也是通过 `fetch` 投递的那一个。E2B 则接收 `proxyRouteFor` 给出的 `route.proxy`,与 `web-fetch-http` 调的是同一个函数。
|
||||
|
||||
把导出器换到 `fetch` 的代价是 `compression`:gzip 属于该 SDK 的 Node 传输,实测一批真实规模的 OTLP 数据启用后体积只有 1/6.4。目前没有任何随附配置启用它,而在企业代理网络里,不遵循代理的遥测干脆发不出去,因此路由优先。导出器本会静默忽略的选项,现在由插件在加载期拒绝——`exporter.compression`、`exporter.keepAlive` 与 `exporter.httpAgentOptions` 会带着原因抛错,任何部署都不会在看不见的情况下承担这个差价。换来的是 Node 版本下限消失:`http.Agent` 的 `proxyEnv` 需要 22.21 或 24.5,而这落在 engines 范围之内,因此遥测过去在 22.19、22.20 与 24.0–24.4 上一直是直连。
|
||||
`fetch` 传输没有压缩能力,而随附的 `base` bundle 启用了 gzip——实测一批真实规模的 OTLP 数据启用后体积只有 1/6.4。为了拿到代理支持而丢掉它,等于用每个部署的代价去换一个部署的问题;第一版正是这么做的:它在加载期拒绝 `exporter.compression`,结果凡是启动随附 bundle 的测试全部失败。改为由本包在 serializer 处 gzip——那是请求体抵达传输前的唯一接缝——并自行声明 `Content-Encoding`。`keepAlive` 与 `httpAgentOptions` 没有这样的接缝,它们配置的是 `fetch` 不暴露的连接池,因此这两个仍在加载期拒绝,而不是被接受后忽略。
|
||||
|
||||
换来的是 Node 版本下限消失:`http.Agent` 的 `proxyEnv` 需要 22.21 或 24.5,而这落在 engines 范围之内,因此遥测过去在 22.19、22.20 与 24.0–24.4 上一直是直连。
|
||||
|
||||
**每个出网点都配一份出网测试,因为读代码不够。** 各所属包中的 `egress.spec.ts` 驱动该点的真实代码路径,目标是无法解析的 `.invalid` 主机,穿过一个假代理,并断言代理确实收到了请求。九份测试覆盖搜索后端、pi-ai 发现、走 HTTP 的 MCP、遥测、E2B、派生的子 Node 与 worker 线程。下面那条门禁看不进依赖内部;这些能,它们把「某个 SDK 换了传输」从静默回归变成失败的测试。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
||||
config-catalog.md: 6fdd34855e4456237464bfb225b65b55b86fbf2e
|
||||
config-catalog.zh.md: 9ca1423556ecee7f839c0ef66b5628fd036b6f94
|
||||
config-catalog.md: e451d76631252a1457bfc225784adc096c9ad548
|
||||
config-catalog.zh.md: b54d57d3a63693d8f6e2e83081c7116b6c5ced5c
|
||||
+13
-4
@@ -1971,13 +1971,16 @@ export interface Config {
|
||||
* `concurrencyLimit`, …), owned and documented by the SDK. `url` is the
|
||||
* one field this package requires and validates itself.
|
||||
*
|
||||
* The transport is the SDK's `fetch` one, so the three options that exist
|
||||
* only for its `node:http` transport — `compression`, `keepAlive`, and
|
||||
* `httpAgentOptions` — are refused at load rather than ignored.
|
||||
* The transport is the SDK's `fetch` one, so `keepAlive` and
|
||||
* `httpAgentOptions` — which configure its `node:http` transport — are
|
||||
* refused at load rather than ignored. `compression` is honored by this
|
||||
* package instead of by that transport.
|
||||
*/
|
||||
exporter?: OTLPExporterConfigBase & {
|
||||
/** Full logs endpoint (e.g. `https://collector.example.com/v1/logs`). Required outside `DISABLED`; validated at load. */
|
||||
url?: string
|
||||
/** Request body compression, applied by this package rather than by the SDK transport. @default 'none' */
|
||||
compression?: SupportedCompression
|
||||
}
|
||||
/**
|
||||
* Passed verbatim to `BatchLogRecordProcessor` (minus the exporter slot,
|
||||
@@ -1994,11 +1997,17 @@ export enum SessionTelemetryMode {
|
||||
FEEDBACK_ONLY = 'FEEDBACK_ONLY',
|
||||
DISABLED = 'DISABLED',
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body encodings this package applies. Narrower than the SDK's `CompressionAlgorithm`,
|
||||
* which also spells `deflate`: the `fetch` transport offers no seam to apply that one.
|
||||
*/
|
||||
export type SupportedCompression = 'gzip' | 'none'
|
||||
```
|
||||
|
||||
Depends on: `BatchLogRecordProcessorOptions` (`@opentelemetry/sdk-logs`) · `OTLPExporterConfigBase` (`@opentelemetry/otlp-exporter-base`)
|
||||
|
||||
Source: [`packages/session/session-telemetry-otel/src/index.ts:94`](../packages/session/session-telemetry-otel/src/index.ts)
|
||||
Source: [`packages/session/session-telemetry-otel/src/index.ts:96`](../packages/session/session-telemetry-otel/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-session-title"></a>
|
||||
|
||||
|
||||
@@ -1973,13 +1973,16 @@ export interface Config {
|
||||
* `concurrencyLimit`, …), owned and documented by the SDK. `url` is the
|
||||
* one field this package requires and validates itself.
|
||||
*
|
||||
* The transport is the SDK's `fetch` one, so the three options that exist
|
||||
* only for its `node:http` transport — `compression`, `keepAlive`, and
|
||||
* `httpAgentOptions` — are refused at load rather than ignored.
|
||||
* The transport is the SDK's `fetch` one, so `keepAlive` and
|
||||
* `httpAgentOptions` — which configure its `node:http` transport — are
|
||||
* refused at load rather than ignored. `compression` is honored by this
|
||||
* package instead of by that transport.
|
||||
*/
|
||||
exporter?: OTLPExporterConfigBase & {
|
||||
/** Full logs endpoint (e.g. `https://collector.example.com/v1/logs`). Required outside `DISABLED`; validated at load. */
|
||||
url?: string
|
||||
/** Request body compression, applied by this package rather than by the SDK transport. @default 'none' */
|
||||
compression?: SupportedCompression
|
||||
}
|
||||
/**
|
||||
* Passed verbatim to `BatchLogRecordProcessor` (minus the exporter slot,
|
||||
@@ -1996,11 +1999,17 @@ export enum SessionTelemetryMode {
|
||||
FEEDBACK_ONLY = 'FEEDBACK_ONLY',
|
||||
DISABLED = 'DISABLED',
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body encodings this package applies. Narrower than the SDK's `CompressionAlgorithm`,
|
||||
* which also spells `deflate`: the `fetch` transport offers no seam to apply that one.
|
||||
*/
|
||||
export type SupportedCompression = 'gzip' | 'none'
|
||||
```
|
||||
|
||||
依赖:`BatchLogRecordProcessorOptions`(`@opentelemetry/sdk-logs`)· `OTLPExporterConfigBase`(`@opentelemetry/otlp-exporter-base`)
|
||||
|
||||
来源:[`packages/session/session-telemetry-otel/src/index.ts:94`](../packages/session/session-telemetry-otel/src/index.ts)
|
||||
来源:[`packages/session/session-telemetry-otel/src/index.ts:96`](../packages/session/session-telemetry-otel/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-session-title"></a>
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
import { createRequire } from 'node:module'
|
||||
import { gzipSync } from 'node:zlib'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-command-feedback'
|
||||
@@ -34,6 +35,7 @@ import {
|
||||
import { OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'
|
||||
import { createLegacyOtlpBrowserExportDelegate } from '@opentelemetry/otlp-exporter-base/browser-http'
|
||||
import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'
|
||||
import type { ISerializer } from '@opentelemetry/otlp-transformer'
|
||||
import type { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'
|
||||
import type { ReadableLogRecord } from '@opentelemetry/sdk-logs'
|
||||
import { SeverityNumber, type AnyValue, type Logger } from '@opentelemetry/api-logs'
|
||||
@@ -100,13 +102,16 @@ export interface Config {
|
||||
* `concurrencyLimit`, …), owned and documented by the SDK. `url` is the
|
||||
* one field this package requires and validates itself.
|
||||
*
|
||||
* The transport is the SDK's `fetch` one, so the three options that exist
|
||||
* only for its `node:http` transport — `compression`, `keepAlive`, and
|
||||
* `httpAgentOptions` — are refused at load rather than ignored.
|
||||
* The transport is the SDK's `fetch` one, so `keepAlive` and
|
||||
* `httpAgentOptions` — which configure its `node:http` transport — are
|
||||
* refused at load rather than ignored. `compression` is honored by this
|
||||
* package instead of by that transport.
|
||||
*/
|
||||
exporter?: OTLPExporterConfigBase & {
|
||||
/** Full logs endpoint (e.g. `https://collector.example.com/v1/logs`). Required outside `DISABLED`; validated at load. */
|
||||
url?: string
|
||||
/** Request body compression, applied by this package rather than by the SDK transport. @default 'none' */
|
||||
compression?: SupportedCompression
|
||||
}
|
||||
/**
|
||||
* Passed verbatim to `BatchLogRecordProcessor` (minus the exporter slot,
|
||||
@@ -142,7 +147,45 @@ const MAX_TIMER_DELAY_MILLIS = 2_147_483_647
|
||||
* Exporter options the SDK defines only for its `node:http` transport. They reach the `fetch`
|
||||
* transport this package uses, which silently ignores every one of them.
|
||||
*/
|
||||
const NODE_TRANSPORT_ONLY_EXPORTER_OPTIONS = ['compression', 'keepAlive', 'httpAgentOptions'] as const
|
||||
const NODE_TRANSPORT_ONLY_EXPORTER_OPTIONS = ['keepAlive', 'httpAgentOptions'] as const
|
||||
|
||||
/** The one encoding {@link gzipSerializer} applies, spelled as the OTLP `Content-Encoding` spells it. */
|
||||
const GZIP = 'gzip'
|
||||
|
||||
/**
|
||||
* Request body encodings this package applies. Narrower than the SDK's `CompressionAlgorithm`,
|
||||
* which also spells `deflate`: the `fetch` transport offers no seam to apply that one.
|
||||
*/
|
||||
export type SupportedCompression = 'gzip' | 'none'
|
||||
|
||||
/** {@link SupportedCompression} as values, for the load-time check on a configuration typed `any`. */
|
||||
const SUPPORTED_COMPRESSION: readonly string[] = [GZIP, 'none'] satisfies SupportedCompression[]
|
||||
|
||||
/**
|
||||
* Wrap a serializer so every batch it produces is gzipped.
|
||||
*
|
||||
* The SDK compresses in its `node:http` transport, which the `fetch` transport this package uses
|
||||
* does not have; serialization is the one seam before the body reaches that transport. The shipped
|
||||
* profile enables gzip, and a realistic batch measures over six times smaller with it, so dropping
|
||||
* compression to gain proxy support would trade one deployment's problem for every deployment's.
|
||||
*
|
||||
* `gzipSync` runs on the export path, but a batch is bounded by `maxExportBatchSize` and exports are
|
||||
* already off the request path — the batch processor schedules them.
|
||||
*
|
||||
* @param serializer - the SDK serializer producing the uncompressed request body.
|
||||
* @returns a serializer producing the gzipped body, deserializing responses unchanged.
|
||||
*/
|
||||
function gzipSerializer<Request, Response>(serializer: ISerializer<Request, Response>): ISerializer<Request, Response> {
|
||||
return {
|
||||
...serializer,
|
||||
serializeRequest: (request) => {
|
||||
const serialized = serializer.serializeRequest(request)
|
||||
// The SDK returns nothing for a batch it could not serialize. Gzipping that would post an
|
||||
// empty frame the collector accepts as a valid, empty export.
|
||||
return serialized === undefined ? undefined : gzipSync(serialized)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/** Severity mapping from the Service Definition's three-level vocabulary to OTel severity numbers. */
|
||||
const SEVERITY: Record<SessionTelemetrySeverity, { severityNumber: SeverityNumber; severityText: string }> = {
|
||||
@@ -195,12 +238,19 @@ export class OpenTelemetrySessionBackend extends SessionTelemetryBackend {
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
||||
throw new Error(`session-telemetry-otel: exporter.url must be http(s), got ${parsed.protocol}`)
|
||||
}
|
||||
// Options that exist only for the SDK's `node:http` transport, which this package no longer
|
||||
// uses. The exporter would accept and ignore each one, so a deployment that asked for gzip
|
||||
// would quietly send uncompressed batches; refusing at load is what makes the change visible.
|
||||
// `keepAlive` and `httpAgentOptions` configure the SDK's `node:http` transport, which this
|
||||
// package does not use — the `fetch` transport is what reaches a configured proxy. The exporter
|
||||
// would accept and ignore them, so a deployment would believe it had tuned a connection it had
|
||||
// not. `compression` is the third such option and is honored instead of refused, below.
|
||||
const nodeOnly = NODE_TRANSPORT_ONLY_EXPORTER_OPTIONS.filter(name => name in exporter)
|
||||
if (nodeOnly.length > 0) {
|
||||
throw new Error(`session-telemetry-otel: exporter.${nodeOnly.join(', exporter.')} not supported: telemetry is exported through fetch so a configured proxy carries it, and the node:http transport those options belong to would need an http.Agent this package no longer builds`)
|
||||
throw new Error(`session-telemetry-otel: exporter.${nodeOnly.join(', exporter.')} not supported: telemetry is exported through fetch, whose connections Node owns; ${nodeOnly.length === 1 ? 'that option belongs' : 'those options belong'} to the node:http transport this package no longer builds an agent for`)
|
||||
}
|
||||
// Compared as strings because that is what arrives: the schema validates this object as `any`,
|
||||
// so a cordis.yml may name any algorithm, including one the SDK's enum does not spell.
|
||||
const compression: string = exporter.compression ?? 'none'
|
||||
if (!SUPPORTED_COMPRESSION.includes(compression)) {
|
||||
throw new Error(`session-telemetry-otel: exporter.compression must be one of ${SUPPORTED_COMPRESSION.map(value => JSON.stringify(value)).join(', ')}, got ${JSON.stringify(compression)}`)
|
||||
}
|
||||
// The one processor field checked beyond the SDK's own validation: the
|
||||
// SDK accepts a non-positive batch size, but its shutdown drain then
|
||||
@@ -251,9 +301,12 @@ export class OpenTelemetrySessionBackend extends SessionTelemetryBackend {
|
||||
// oxlint-disable-next-line typescript/no-deprecated -- the SDK exports its replacement from no public subpath at 0.220.
|
||||
createLegacyOtlpBrowserExportDelegate(
|
||||
exporter,
|
||||
JsonLogsSerializer,
|
||||
compression === GZIP ? gzipSerializer(JsonLogsSerializer) : JsonLogsSerializer,
|
||||
'v1/logs',
|
||||
{ 'Content-Type': 'application/json' },
|
||||
{
|
||||
'Content-Type': 'application/json',
|
||||
...compression === GZIP ? { 'Content-Encoding': GZIP } : {},
|
||||
},
|
||||
),
|
||||
),
|
||||
}),
|
||||
|
||||
@@ -12,6 +12,7 @@ import { mkdtempSync, rmSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { gunzipSync } from 'node:zlib'
|
||||
import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
@@ -255,17 +256,91 @@ describe('OpenTelemetrySessionBackend wire', () => {
|
||||
expect(types).toContain('turn/start')
|
||||
})
|
||||
|
||||
it('gzips the batch when the shipped profile asks for it', async () => {
|
||||
const { url, captures } = await mockCollector()
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
// The shipped `base` bundle sets this, and a realistic batch is over six times smaller with it.
|
||||
// The SDK compresses in its `node:http` transport, which the `fetch` transport used here does
|
||||
// not have, so this package gzips at the serializer and declares the encoding itself.
|
||||
const fiber = await ctx.plugin(OpenTelemetrySessionBackend, {
|
||||
mode: SessionTelemetryMode.FULL,
|
||||
exporter: { url, compression: 'gzip' },
|
||||
})
|
||||
const session = ctx.sessions.create(SessionId('gzip'), { meta: {} })
|
||||
session.append('turn/start', { turn: 1 })
|
||||
await fiber.dispose()
|
||||
|
||||
expect(captures.length).toBeGreaterThan(0)
|
||||
expect(captures[0]!.headers['content-encoding']).toBe('gzip')
|
||||
// The collector gunzips the body it received, so the header is not merely asserted alongside a
|
||||
// plaintext payload the encoding would have misdescribed.
|
||||
const types = allRecords(captures).flatMap(({ record }) =>
|
||||
record.attributes?.flatMap(a => a.key === 'event.type' ? [a.value.stringValue] : []) ?? [])
|
||||
expect(types).toContain('turn/start')
|
||||
})
|
||||
|
||||
it('sends the batch uncompressed when no compression is configured', async () => {
|
||||
const { url, captures } = await mockCollector()
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
const fiber = await ctx.plugin(OpenTelemetrySessionBackend, { mode: SessionTelemetryMode.FULL, exporter: { url } })
|
||||
ctx.sessions.create(SessionId('plain'), { meta: {} }).append('turn/start', { turn: 1 })
|
||||
await fiber.dispose()
|
||||
|
||||
expect(captures.length).toBeGreaterThan(0)
|
||||
expect(captures[0]!.headers['content-encoding']).toBeUndefined()
|
||||
})
|
||||
|
||||
it('sends nothing when the SDK cannot serialize the batch, rather than an empty gzip frame', async () => {
|
||||
const { url, captures } = await mockCollector()
|
||||
const serialize = vi.spyOn(JsonLogsSerializer, 'serializeRequest').mockReturnValue(undefined)
|
||||
try {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
const fiber = await ctx.plugin(OpenTelemetrySessionBackend, {
|
||||
mode: SessionTelemetryMode.FULL,
|
||||
exporter: { url, compression: 'gzip' },
|
||||
})
|
||||
ctx.sessions.create(SessionId('unserializable'), { meta: {} }).append('turn/start', { turn: 1 })
|
||||
await fiber.dispose()
|
||||
expect(serialize).toHaveBeenCalled()
|
||||
expect(captures).toEqual([])
|
||||
} finally {
|
||||
serialize.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it('names every node:http option a configuration set, not just the first', async () => {
|
||||
const { url } = await mockCollector()
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await expect(ctx.plugin(OpenTelemetrySessionBackend, {
|
||||
mode: SessionTelemetryMode.FULL,
|
||||
exporter: { url, keepAlive: true, httpAgentOptions: {} },
|
||||
} as unknown as Config)).rejects.toThrow(/exporter\.keepAlive, exporter\.httpAgentOptions not supported/)
|
||||
})
|
||||
|
||||
it('refuses an exporter option that belongs to the node:http transport', async () => {
|
||||
const { url } = await mockCollector()
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
// Telemetry goes through `fetch` so a configured proxy carries it, and that transport ignores
|
||||
// `compression`. Accepting the option would send uncompressed batches while the configuration
|
||||
// said gzip; the deployment has to see the trade rather than pay it silently.
|
||||
// `keepAlive` tunes a `node:http` connection pool this package no longer builds. Accepting it
|
||||
// would let a deployment believe it had tuned a connection that does not exist.
|
||||
await expect(ctx.plugin(OpenTelemetrySessionBackend, {
|
||||
mode: SessionTelemetryMode.FULL,
|
||||
exporter: { url, compression: 'gzip' },
|
||||
} as unknown as Config)).rejects.toThrow(/exporter\.compression not supported/)
|
||||
exporter: { url, keepAlive: true },
|
||||
} as unknown as Config)).rejects.toThrow(/exporter\.keepAlive not supported/)
|
||||
})
|
||||
|
||||
it('refuses a compression algorithm it cannot apply', async () => {
|
||||
const { url } = await mockCollector()
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await expect(ctx.plugin(OpenTelemetrySessionBackend, {
|
||||
mode: SessionTelemetryMode.FULL,
|
||||
exporter: { url, compression: 'deflate' },
|
||||
} as unknown as Config)).rejects.toThrow(/exporter\.compression must be one of "gzip", "none"/)
|
||||
})
|
||||
|
||||
it('maps warn severity from record policy and leaves the seam flush hint unimplemented', async () => {
|
||||
|
||||
Reference in new issue
Block a user