feat: configure call log pipeline artifacts (#1650)

Integrated into release/v3.7.2
This commit is contained in:
Randi
2026-04-27 06:12:34 -04:00
committed by GitHub
parent bc91fb9e54
commit 845e2b3d01
10 changed files with 297 additions and 42 deletions

View File

@@ -586,6 +586,16 @@ APP_LOG_TO_FILE=true
# Default: 100000
# CALL_LOGS_TABLE_MAX_ROWS=100000
# Whether call log pipeline capture stores stream chunks when enabled in settings.
# Only applies when call_log_pipeline_enabled=true.
# Default: true
# CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS=true
# Maximum call log artifact size for pipeline captures, in KB.
# Only applies when call_log_pipeline_enabled=true.
# Default: 512
# CALL_LOG_PIPELINE_MAX_SIZE_KB=512
# Maximum rows in the proxy_logs SQLite table.
# Default: 100000
# PROXY_LOGS_TABLE_MAX_ROWS=100000

View File

@@ -2050,6 +2050,7 @@ opencode
- `call_logs` in SQLite stores summary metadata for the Request Logs table and analytics views
- Detailed request/response payloads are written to `DATA_DIR/call_logs/` as one JSON artifact per request
- Enable pipeline capture from Dashboard → Logs → Request Logs if you need detailed per-stage payloads
- When pipeline capture is enabled, `CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS=false` skips stream chunks and `CALL_LOG_PIPELINE_MAX_SIZE_KB` controls the artifact cap in KB
- `Export Logs` reads the artifact files on demand, while `Export All` includes the `call_logs/` directory alongside `storage.sqlite`
- Set `APP_LOG_TO_FILE=true` if you also want application console logs in `logs/application/app.log`
- Adjust `APP_LOG_MAX_FILE_SIZE`, `APP_LOG_RETENTION_DAYS`, `APP_LOG_MAX_FILES`, and `CALL_LOG_MAX_ENTRIES` as needed

View File

@@ -464,19 +464,21 @@ REQUEST_TIMEOUT_MS (global override)
The logging system writes to both stdout and rotated log files. All configuration is read by `src/lib/logEnv.ts`.
| Variable | Default | Description |
| --------------------------- | -------------------------- | ---------------------------------------------------------------------------- |
| `APP_LOG_LEVEL` | `info` | Minimum log level: `debug`, `info`, `warn`, `error`. |
| `APP_LOG_FORMAT` | `text` | Output format: `text` (human-readable) or `json` (structured). |
| `APP_LOG_TO_FILE` | `true` | Write logs to file alongside stdout. |
| `APP_LOG_FILE_PATH` | `logs/application/app.log` | Log file path (relative to project root or `DATA_DIR`). |
| `APP_LOG_MAX_FILE_SIZE` | `50M` | Max file size before rotation. Accepts: `50M`, `1G`, `512K`, or plain bytes. |
| `APP_LOG_RETENTION_DAYS` | `7` | Days to keep rotated application log files. |
| `APP_LOG_MAX_FILES` | `20` | Maximum rotated log file backups. |
| `CALL_LOG_RETENTION_DAYS` | `7` | Days to keep request/call log entries in the database. |
| `CALL_LOG_MAX_ENTRIES` | `10000` | Max call log entries in the in-memory buffer. |
| `CALL_LOGS_TABLE_MAX_ROWS` | `100000` | Max rows in the `call_logs` SQLite table before pruning. |
| `PROXY_LOGS_TABLE_MAX_ROWS` | `100000` | Max rows in the `proxy_logs` SQLite table before pruning. |
| Variable | Default | Description |
| ----------------------------------------- | -------------------------- | -------------------------------------------------------------------------------- |
| `APP_LOG_LEVEL` | `info` | Minimum log level: `debug`, `info`, `warn`, `error`. |
| `APP_LOG_FORMAT` | `text` | Output format: `text` (human-readable) or `json` (structured). |
| `APP_LOG_TO_FILE` | `true` | Write logs to file alongside stdout. |
| `APP_LOG_FILE_PATH` | `logs/application/app.log` | Log file path (relative to project root or `DATA_DIR`). |
| `APP_LOG_MAX_FILE_SIZE` | `50M` | Max file size before rotation. Accepts: `50M`, `1G`, `512K`, or plain bytes. |
| `APP_LOG_RETENTION_DAYS` | `7` | Days to keep rotated application log files. |
| `APP_LOG_MAX_FILES` | `20` | Maximum rotated log file backups. |
| `CALL_LOG_RETENTION_DAYS` | `7` | Days to keep request/call log entries in the database. |
| `CALL_LOG_MAX_ENTRIES` | `10000` | Max call log entries in the in-memory buffer. |
| `CALL_LOGS_TABLE_MAX_ROWS` | `100000` | Max rows in the `call_logs` SQLite table before pruning. |
| `CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS` | `true` | Store stream chunks in pipeline artifacts when `call_log_pipeline_enabled=true`. |
| `CALL_LOG_PIPELINE_MAX_SIZE_KB` | `512` | Max pipeline call log artifact size in KB when `call_log_pipeline_enabled=true`. |
| `PROXY_LOGS_TABLE_MAX_ROWS` | `100000` | Max rows in the `proxy_logs` SQLite table before pruning. |
---

View File

@@ -191,6 +191,8 @@ curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,
Set `APP_LOG_TO_FILE=true` in your `.env` file. Application logs are written under `logs/`.
Request artifacts are stored under `${DATA_DIR}/call_logs/` when the call log pipeline is
enabled in settings.
When pipeline capture is enabled, set `CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS=false` to omit
stream chunk payloads, or tune `CALL_LOG_PIPELINE_MAX_SIZE_KB` to change the artifact cap in KB.
### Check Provider Health

View File

@@ -32,6 +32,7 @@ import {
} from "../services/errorClassifier.ts";
import { updateProviderConnection } from "@/lib/db/providers";
import { isDetailedLoggingEnabled } from "@/lib/db/detailedLogs";
import { getCallLogPipelineCaptureStreamChunks } from "@/lib/logEnv";
import { logAuditEvent } from "@/lib/compliance";
import { extractProviderWarnings } from "@/lib/compliance/providerAudit";
import { handleBypassRequest } from "../utils/bypassHandler.ts";
@@ -1129,6 +1130,8 @@ export async function handleChatCore({
}
const noLogEnabled = apiKeyInfo?.noLog === true;
const detailedLoggingEnabled = !noLogEnabled && (await isDetailedLoggingEnabled());
const capturePipelineStreamChunks =
detailedLoggingEnabled && getCallLogPipelineCaptureStreamChunks();
const skillRequestId = generateRequestId();
const pipelineSessionId =
(clientRawRequest?.headers && typeof clientRawRequest.headers.get === "function"
@@ -1310,7 +1313,7 @@ export async function handleChatCore({
// Create request logger for this session: sourceFormat_targetFormat_model
const reqLogger = await createRequestLogger(sourceFormat, targetFormat, model, {
enabled: detailedLoggingEnabled,
captureStreamChunks: detailedLoggingEnabled,
captureStreamChunks: capturePipelineStreamChunks,
});
// 0. Log client raw request (before format conversion)

View File

@@ -6,6 +6,7 @@ const DEFAULT_APP_LOG_MAX_SIZE = 50 * 1024 * 1024;
const DEFAULT_APP_LOG_MAX_FILES = 20;
const DEFAULT_CALL_LOG_MAX_ENTRIES = 10000;
const DEFAULT_CALL_LOGS_TABLE_MAX_ROWS = 100000;
const DEFAULT_CALL_LOG_PIPELINE_MAX_SIZE_KB = 512;
const DEFAULT_PROXY_LOGS_TABLE_MAX_ROWS = 100000;
const DEFAULT_APP_LOG_PATH = path.join(process.cwd(), "logs", "application", "app.log");
@@ -15,6 +16,14 @@ function parsePositiveInt(value: string | undefined, fallback: number): number {
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}
function parseBoolean(value: string | undefined, fallback: boolean): boolean {
if (!value) return fallback;
const normalized = value.trim().toLowerCase();
if (["1", "true", "yes", "on"].includes(normalized)) return true;
if (["0", "false", "no", "off"].includes(normalized)) return false;
return fallback;
}
export function parseFileSize(raw: string | undefined): number {
if (!raw) return DEFAULT_APP_LOG_MAX_SIZE;
const match = raw.match(/^(\d+)\s*(k|m|g|kb|mb|gb)?$/i);
@@ -68,6 +77,19 @@ export function getCallLogsTableMaxRows(): number {
return parsePositiveInt(process.env.CALL_LOGS_TABLE_MAX_ROWS, DEFAULT_CALL_LOGS_TABLE_MAX_ROWS);
}
export function getCallLogPipelineCaptureStreamChunks(): boolean {
return parseBoolean(process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS, true);
}
export function getCallLogPipelineMaxSizeBytes(): number {
return (
parsePositiveInt(
process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB,
DEFAULT_CALL_LOG_PIPELINE_MAX_SIZE_KB
) * 1024
);
}
export function getProxyLogsTableMaxRows(): number {
return parsePositiveInt(process.env.PROXY_LOGS_TABLE_MAX_ROWS, DEFAULT_PROXY_LOGS_TABLE_MAX_ROWS);
}

View File

@@ -2,6 +2,7 @@ import fs from "node:fs";
import path from "node:path";
import type { RequestPipelinePayloads } from "@omniroute/open-sse/utils/requestLogger.ts";
import { resolveDataDir } from "../dataPaths";
import { getCallLogPipelineMaxSizeBytes } from "../logEnv";
const isCloud = typeof globalThis.caches === "object" && globalThis.caches !== null;
const isBuildPhase = process.env.NEXT_PHASE === "phase-production-build";
@@ -10,6 +11,11 @@ const DATA_DIR = resolveDataDir({ isCloud });
export const CALL_LOGS_DIR = isCloud ? null : path.join(DATA_DIR, "call_logs");
export const MAX_CALL_LOG_ARTIFACT_BYTES = 512 * 1024;
const SIZE_LIMIT_EXCEEDED_REASON = "call_log_artifact_size_limit_exceeded";
const OMITTED_FOR_SIZE_LIMIT = "[omitted: call log artifact size limit exceeded]";
const STREAM_CHUNKS_OMITTED_FOR_SIZE_LIMIT =
"[stream chunks omitted: call log artifact size limit exceeded]";
export type CallLogDetailState = "none" | "ready" | "missing" | "corrupt" | "legacy-inline";
export type CallLogArtifact = {
@@ -83,13 +89,13 @@ function truncateArtifactForStorage(artifact: CallLogArtifact): CallLogArtifact
...pipeline,
streamChunks: {
provider: pipeline.streamChunks.provider?.length
? ["[stream chunks omitted: call log artifact size limit exceeded]"]
? [STREAM_CHUNKS_OMITTED_FOR_SIZE_LIMIT]
: undefined,
openai: pipeline.streamChunks.openai?.length
? ["[stream chunks omitted: call log artifact size limit exceeded]"]
? [STREAM_CHUNKS_OMITTED_FOR_SIZE_LIMIT]
: undefined,
client: pipeline.streamChunks.client?.length
? ["[stream chunks omitted: call log artifact size limit exceeded]"]
? [STREAM_CHUNKS_OMITTED_FOR_SIZE_LIMIT]
: undefined,
},
},
@@ -104,59 +110,77 @@ function omitOversizedPipeline(artifact: CallLogArtifact): CallLogArtifact {
pipeline: {
error: {
_omniroute_truncated: true,
reason: "call_log_artifact_size_limit_exceeded",
reason: SIZE_LIMIT_EXCEEDED_REASON,
},
},
};
}
function getArtifactMaxBytes(artifact: CallLogArtifact): number {
return artifact.pipeline ? getCallLogPipelineMaxSizeBytes() : MAX_CALL_LOG_ARTIFACT_BYTES;
}
function buildMinimalArtifactForSizeLimit(artifact: CallLogArtifact) {
return {
schemaVersion: artifact.schemaVersion,
summary: artifact.summary,
requestBody: OMITTED_FOR_SIZE_LIMIT,
responseBody: OMITTED_FOR_SIZE_LIMIT,
error: artifact.error ? OMITTED_FOR_SIZE_LIMIT : null,
pipeline: {
error: {
_omniroute_truncated: true,
reason: SIZE_LIMIT_EXCEEDED_REASON,
},
},
};
}
function serializeFinalSizeLimitFallback(artifact: CallLogArtifact, maxBytes: number): string {
const withSummary = JSON.stringify(buildMinimalArtifactForSizeLimit(artifact));
if (Buffer.byteLength(withSummary) <= maxBytes) {
return withSummary;
}
return JSON.stringify({
schemaVersion: artifact.schemaVersion,
_omniroute_truncated: true,
reason: SIZE_LIMIT_EXCEEDED_REASON,
});
}
function serializeArtifactForStorage(artifact: CallLogArtifact): string {
const maxBytes = getArtifactMaxBytes(artifact);
const serialized = JSON.stringify(artifact, null, 2);
if (Buffer.byteLength(serialized) <= MAX_CALL_LOG_ARTIFACT_BYTES) {
if (Buffer.byteLength(serialized) <= maxBytes) {
return serialized;
}
const truncated = JSON.stringify(truncateArtifactForStorage(artifact), null, 2);
if (Buffer.byteLength(truncated) <= MAX_CALL_LOG_ARTIFACT_BYTES) {
if (Buffer.byteLength(truncated) <= maxBytes) {
return truncated;
}
const withoutPipeline = JSON.stringify(omitOversizedPipeline(artifact), null, 2);
if (Buffer.byteLength(withoutPipeline) <= MAX_CALL_LOG_ARTIFACT_BYTES) {
if (Buffer.byteLength(withoutPipeline) <= maxBytes) {
return withoutPipeline;
}
const minimal = JSON.stringify(
{
...omitOversizedPipeline(artifact),
requestBody: "[omitted: call log artifact size limit exceeded]",
responseBody: "[omitted: call log artifact size limit exceeded]",
error: artifact.error ? "[omitted: call log artifact size limit exceeded]" : null,
requestBody: OMITTED_FOR_SIZE_LIMIT,
responseBody: OMITTED_FOR_SIZE_LIMIT,
error: artifact.error ? OMITTED_FOR_SIZE_LIMIT : null,
},
null,
2
);
if (Buffer.byteLength(minimal) <= MAX_CALL_LOG_ARTIFACT_BYTES) {
if (Buffer.byteLength(minimal) <= maxBytes) {
return minimal;
}
return JSON.stringify(
{
schemaVersion: artifact.schemaVersion,
summary: artifact.summary,
requestBody: "[omitted: call log artifact size limit exceeded]",
responseBody: "[omitted: call log artifact size limit exceeded]",
error: artifact.error ? "[omitted: call log artifact size limit exceeded]" : null,
pipeline: {
error: {
_omniroute_truncated: true,
reason: "call_log_artifact_size_limit_exceeded",
},
},
},
null,
2
);
return serializeFinalSizeLimitFallback(artifact, maxBytes);
}
export function writeCallArtifact(

View File

@@ -9,6 +9,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.CALL_LOG_RETENTION_DAYS = "3650";
process.env.CALL_LOG_MAX_ENTRIES = "100";
const ORIGINAL_CALL_LOG_PIPELINE_MAX_SIZE_KB = process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB;
const core = await import("../../src/lib/db/core.ts");
const callLogs = await import("../../src/lib/usage/callLogs.ts");
const detailedLogs = await import("../../src/lib/db/detailedLogs.ts");
@@ -19,6 +21,14 @@ async function resetStorage() {
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
function restorePipelineEnv() {
if (ORIGINAL_CALL_LOG_PIPELINE_MAX_SIZE_KB === undefined) {
delete process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB;
} else {
process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB = ORIGINAL_CALL_LOG_PIPELINE_MAX_SIZE_KB;
}
}
function insertCallLog(row) {
const db = core.getDbInstance();
db.prepare(
@@ -66,11 +76,13 @@ function insertCallLog(row) {
}
test.beforeEach(async () => {
restorePipelineEnv();
process.env.CALL_LOG_RETENTION_DAYS = "3650";
await resetStorage();
});
test.after(() => {
restorePipelineEnv();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
@@ -541,6 +553,122 @@ test("saveCallLog omits oversized non-stream pipeline payloads to enforce artifa
});
});
test("saveCallLog honors CALL_LOG_PIPELINE_MAX_SIZE_KB for pipeline artifacts", async () => {
process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB = "8";
const hugePayload = "x".repeat(32 * 1024);
await callLogs.saveCallLog({
id: "configured-pipeline-artifact-cap",
timestamp: "2026-03-31T10:07:00.000Z",
method: "POST",
path: "/v1/chat/completions",
status: 200,
model: "openai/gpt-4.1",
provider: "openai",
requestBody: { payload: "request" },
responseBody: { output: "response" },
pipelinePayloads: {
providerRequest: { body: hugePayload },
providerResponse: { body: hugePayload },
},
});
const db = core.getDbInstance();
const row = db
.prepare(
`
SELECT artifact_relpath, artifact_size_bytes, detail_state
FROM call_logs WHERE id = ?
`
)
.get("configured-pipeline-artifact-cap");
assert.equal((row as any).detail_state, "ready");
assert.ok((row as any).artifact_size_bytes <= 8 * 1024);
const artifactPath = path.join(TEST_DATA_DIR, "call_logs", (row as any).artifact_relpath);
const artifact = JSON.parse(fs.readFileSync(artifactPath, "utf8"));
assert.deepEqual(artifact.pipeline, {
error: {
_omniroute_truncated: true,
reason: "call_log_artifact_size_limit_exceeded",
},
});
});
test("saveCallLog falls back to a compact sentinel when the configured cap is very small", async () => {
process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB = "1";
const hugePayload = "x".repeat(32 * 1024);
await callLogs.saveCallLog({
id: "tiny-pipeline-artifact-cap",
timestamp: "2026-03-31T10:07:30.000Z",
method: "POST",
path: "/v1/chat/completions",
status: 200,
model: `openai/${"gpt".repeat(512)}`,
provider: "openai",
requestBody: { payload: "request" },
responseBody: { output: "response" },
pipelinePayloads: {
providerRequest: { body: hugePayload },
providerResponse: { body: hugePayload },
},
});
const db = core.getDbInstance();
const row = db
.prepare(
`
SELECT artifact_relpath, artifact_size_bytes, detail_state
FROM call_logs WHERE id = ?
`
)
.get("tiny-pipeline-artifact-cap");
assert.equal((row as any).detail_state, "ready");
assert.ok((row as any).artifact_size_bytes <= 1024);
const artifactPath = path.join(TEST_DATA_DIR, "call_logs", (row as any).artifact_relpath);
const artifact = JSON.parse(fs.readFileSync(artifactPath, "utf8"));
assert.deepEqual(artifact, {
schemaVersion: 4,
_omniroute_truncated: true,
reason: "call_log_artifact_size_limit_exceeded",
});
});
test("CALL_LOG_PIPELINE_MAX_SIZE_KB does not cap artifacts without pipeline details", async () => {
process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB = "8";
const requestBody = { payload: "x".repeat(16 * 1024) };
await callLogs.saveCallLog({
id: "non-pipeline-artifact-ignores-pipeline-cap",
timestamp: "2026-03-31T10:08:00.000Z",
method: "POST",
path: "/v1/chat/completions",
status: 200,
model: "openai/gpt-4.1",
provider: "openai",
requestBody,
responseBody: { output: "response" },
});
const db = core.getDbInstance();
const row = db
.prepare(
`
SELECT artifact_relpath, artifact_size_bytes, detail_state
FROM call_logs WHERE id = ?
`
)
.get("non-pipeline-artifact-ignores-pipeline-cap");
assert.equal((row as any).detail_state, "ready");
assert.ok((row as any).artifact_size_bytes > 8 * 1024);
const artifactPath = path.join(TEST_DATA_DIR, "call_logs", (row as any).artifact_relpath);
const artifact = JSON.parse(fs.readFileSync(artifactPath, "utf8"));
assert.equal(artifact.requestBody.payload.length, requestBody.payload.length);
});
test("saveCallLog logs and returns when sqlite persistence throws unexpectedly", async () => {
const db = core.getDbInstance();
const originalPrepare = db.prepare;

View File

@@ -48,6 +48,8 @@ const originalFetch = globalThis.fetch;
const originalResponsesToOpenAI = getRequestTranslator(FORMATS.OPENAI_RESPONSES, FORMATS.OPENAI);
const originalSetTimeout = globalThis.setTimeout;
const originalBackgroundConfig = getBackgroundDegradationConfig();
const originalCallLogPipelineCaptureStreamChunks =
process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS;
function noopLog() {
return {
@@ -58,6 +60,15 @@ function noopLog() {
};
}
function restorePipelineCaptureEnv() {
if (originalCallLogPipelineCaptureStreamChunks === undefined) {
delete process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS;
} else {
process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS =
originalCallLogPipelineCaptureStreamChunks;
}
}
function toPlainHeaders(headers) {
if (!headers) return {};
if (headers instanceof Headers) return Object.fromEntries(headers.entries());
@@ -353,6 +364,7 @@ async function invokeChatCore({
test.afterEach(async () => {
globalThis.fetch = originalFetch;
restorePipelineCaptureEnv();
resetAccountSemaphores();
await waitForAsyncSideEffects();
await resetStorage();
@@ -360,12 +372,36 @@ test.afterEach(async () => {
test.after(async () => {
globalThis.fetch = originalFetch;
restorePipelineCaptureEnv();
resetAccountSemaphores();
await waitForAsyncSideEffects();
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("chatCore can disable pipeline stream chunk capture through environment", async () => {
process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS = "false";
await settingsDb.updateSettings({ call_log_pipeline_enabled: true });
const { result } = await invokeChatCore({
accept: "text/event-stream",
body: {
model: "gpt-4o-mini",
stream: true,
messages: [{ role: "user", content: "stream without chunk logging" }],
},
});
assert.equal(result.success, true);
await result.response.text();
await waitForAsyncSideEffects();
const detail = await waitFor(getLatestCallLog);
assert.ok(detail, "expected call log detail to be persisted");
assert.ok(detail.pipelinePayloads, "expected pipeline payloads when capture is enabled");
assert.equal((detail.pipelinePayloads as any).streamChunks, undefined);
});
test("chatCore keeps Responses-native Codex payloads in native passthrough mode", async () => {
const { call, result } = await invokeChatCore({
provider: "codex",

View File

@@ -187,3 +187,30 @@ test("getCallLogsTableMaxRows returns configured value", async () => {
assert.equal(getCallLogsTableMaxRows(), 5);
assert.equal(getProxyLogsTableMaxRows(), 5);
});
test("call log pipeline env helpers parse stream chunk flag and size cap", async () => {
const originalCapture = process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS;
const originalMaxSize = process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB;
const { getCallLogPipelineCaptureStreamChunks, getCallLogPipelineMaxSizeBytes } =
await import("../../src/lib/logEnv.ts");
try {
process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS = "false";
process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB = "256";
assert.equal(getCallLogPipelineCaptureStreamChunks(), false);
assert.equal(getCallLogPipelineMaxSizeBytes(), 256 * 1024);
} finally {
if (originalCapture === undefined) {
delete process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS;
} else {
process.env.CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS = originalCapture;
}
if (originalMaxSize === undefined) {
delete process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB;
} else {
process.env.CALL_LOG_PIPELINE_MAX_SIZE_KB = originalMaxSize;
}
}
});