fix(logger): respect configured logger in auth request context (#10939)

This commit is contained in:
Taesu
2026-08-25 08:37:57 +00:00
committed by GitHub
parent 557e19bfad
commit e1d40116e2
4 changed files with 138 additions and 22 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@better-auth/core": patch
---
Logs emitted while handling auth requests now respect the configured custom logger, log level, and disabled setting.
@@ -1,5 +1,7 @@
import type { BetterAuthOptions } from "@better-auth/core";
import { logger } from "@better-auth/core/env";
import type { AuthEndpointContext } from "@better-auth/core/context";
import { runWithEndpointContext } from "@better-auth/core/context";
import { createLogger } from "@better-auth/core/env";
import type { GoogleProfile } from "@better-auth/core/social-providers";
import { safeJSONParse } from "@better-auth/core/utils/json";
import { base64Url } from "@better-auth/utils/base64";
@@ -15,6 +17,7 @@ import {
describe,
expect,
it,
onTestFinished,
vi,
} from "vitest";
import {
@@ -1274,7 +1277,7 @@ describe("Cookie Cache Field Filtering", () => {
/**
* @see https://github.com/better-auth/better-auth/issues/10902
*/
it("should warn when a signed compact cookie has an invalid payload", async () => {
it("should use the configured logger for an invalid signed compact cookie", async () => {
const secret = "test-secret";
const bakeCookie = async (emailVerified: boolean | null) => {
const now = new Date().toISOString();
@@ -1313,25 +1316,35 @@ describe("Cookie Cache Field Filtering", () => {
isSecure: false,
secret,
};
const warn = vi.spyOn(logger, "warn").mockImplementation(() => {});
try {
const validCache = await getCookieCache(
new Headers({ cookie: `p.session_data=${await bakeCookie(false)}` }),
config,
);
expect(validCache).not.toBeNull();
expect(warn).not.toHaveBeenCalled();
const log = vi.fn();
const defaultWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
onTestFinished(() => defaultWarn.mockRestore());
const endpointContext = {
context: { logger: createLogger({ log }) },
} as unknown as AuthEndpointContext;
const validCookie = await bakeCookie(false);
const invalidCache = await getCookieCache(
new Headers({ cookie: `p.session_data=${await bakeCookie(null)}` }),
const validCache = await runWithEndpointContext(endpointContext, () =>
getCookieCache(
new Headers({ cookie: `p.session_data=${validCookie}` }),
config,
);
),
);
expect(validCache).not.toBeNull();
expect(log).not.toHaveBeenCalled();
const invalidCookie = await bakeCookie(null);
expect(invalidCache).toBeNull();
expect(warn).toHaveBeenCalledTimes(1);
} finally {
warn.mockRestore();
}
const invalidCache = await runWithEndpointContext(endpointContext, () =>
getCookieCache(
new Headers({ cookie: `p.session_data=${invalidCookie}` }),
config,
),
);
expect(invalidCache).toBeNull();
expect(log).toHaveBeenCalledTimes(1);
expect(log.mock.calls[0]?.[0]).toBe("warn");
expect(defaultWarn).not.toHaveBeenCalled();
});
it("should return null for invalid JWT token", async () => {
+80 -3
View File
@@ -1,6 +1,9 @@
import { describe, expect, it } from "vitest";
import type { LogLevel } from "./logger";
import { shouldPublishLog } from "./logger";
import { describe, expect, it, onTestFinished, vi } from "vitest";
import type { AuthEndpointContext } from "../context";
import { runWithEndpointContext } from "../context";
import { safeJSONParse } from "../utils/json";
import type { InternalLogger, LogLevel } from "./logger";
import { createLogger, logger, shouldPublishLog } from "./logger";
describe("shouldPublishLog", () => {
const testCases: {
@@ -32,3 +35,77 @@ describe("shouldPublishLog", () => {
});
});
});
describe("logger", () => {
const endpointContext = (logger: InternalLogger) =>
({ context: { logger } }) as unknown as AuthEndpointContext;
it("uses the default logger outside an auth context", () => {
const defaultWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
onTestFinished(() => defaultWarn.mockRestore());
logger.warn("default warning");
expect(defaultWarn).toHaveBeenCalledTimes(1);
});
it("delegates to the logger in the current auth context", async () => {
const defaultWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
onTestFinished(() => defaultWarn.mockRestore());
const firstLog = vi.fn();
const secondLog = vi.fn();
const firstLogger = createLogger({ level: "info", log: firstLog });
const secondLogger = createLogger({ level: "error", log: secondLog });
await Promise.all([
runWithEndpointContext(endpointContext(firstLogger), async () => {
await Promise.resolve();
expect(logger.level).toBe("info");
logger.warn("first warning");
}),
runWithEndpointContext(endpointContext(secondLogger), async () => {
await Promise.resolve();
expect(logger.level).toBe("error");
logger.warn("second warning");
}),
]);
expect(firstLog).toHaveBeenCalledWith("warn", "first warning");
expect(secondLog).not.toHaveBeenCalled();
expect(defaultWarn).not.toHaveBeenCalled();
});
it("routes utility logs through the current auth context", async () => {
const defaultError = vi
.spyOn(console, "error")
.mockImplementation(() => {});
onTestFinished(() => defaultError.mockRestore());
const log = vi.fn();
const configuredLogger = createLogger({ log });
await runWithEndpointContext(endpointContext(configuredLogger), () => {
expect(safeJSONParse("{")).toBeNull();
});
expect(log).toHaveBeenCalledWith(
"error",
"Error parsing JSON",
expect.objectContaining({ error: expect.any(SyntaxError) }),
);
expect(defaultError).not.toHaveBeenCalled();
});
it("honors disabled logging in the current auth context", async () => {
const defaultWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
onTestFinished(() => defaultWarn.mockRestore());
const log = vi.fn();
const configuredLogger = createLogger({ disabled: true, log });
await runWithEndpointContext(endpointContext(configuredLogger), () => {
logger.warn("hidden warning");
});
expect(log).not.toHaveBeenCalled();
expect(defaultWarn).not.toHaveBeenCalled();
});
});
+22 -1
View File
@@ -1,3 +1,5 @@
import type { AuthEndpointContext } from "../context/endpoint-context";
import { __getCurrentEndpointContext } from "../context/global";
import { getColorDepth } from "./color-depth";
export const TTY_COLORS = {
@@ -142,4 +144,23 @@ export const createLogger = (options?: Logger | undefined): InternalLogger => {
};
};
export const logger = createLogger();
const defaultLogger = createLogger();
const getCurrentLogger = (): InternalLogger => {
const currentLogger =
__getCurrentEndpointContext<AuthEndpointContext>()?.context.logger;
return currentLogger && currentLogger !== logger
? currentLogger
: defaultLogger;
};
export const logger: InternalLogger = {
debug: (...params) => getCurrentLogger().debug(...params),
info: (...params) => getCurrentLogger().info(...params),
success: (...params) => getCurrentLogger().success(...params),
warn: (...params) => getCurrentLogger().warn(...params),
error: (...params) => getCurrentLogger().error(...params),
get level() {
return getCurrentLogger().level;
},
};