Logger not, well, logging #1455

Closed
opened 2026-03-13 08:40:56 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @irg1008 on GitHub (Jul 2, 2025).

The logger displays absolutely nothing on console

const authLogger: Logger = {
  level: 'info',
  log: (level, message, args) => {
    logger.error('Auth log')
  },
}
Originally created by @irg1008 on GitHub (Jul 2, 2025). The logger displays absolutely nothing on console ``` const authLogger: Logger = { level: 'info', log: (level, message, args) => { logger.error('Auth log') }, } ```
GiteaMirror added the bug label 2026-03-13 08:40:56 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jul 2, 2025):

Your logger isn't logging because the log function you provided always calls logger.error('Auth log'), which doesn't use the actual log message or level, and may cause recursion or reference an undefined logger if not in scope. Instead, you should use console.log (or console.error, etc.) directly in your custom log function. For example:

const authLogger = {
  level: 'info',
  log: (level, message, ...args) => {
    console.log(`[${level}]`, message, ...args);
  },
};

Also, keep in mind that Better-Auth's default logger only outputs messages at the 'error' level unless you explicitly set a lower log level. Even then, some log levels (like 'debug') may not produce output because debug logging isn't implemented for most endpoints yet (source).

If you still see nothing, try adding a direct console.log inside your log function to verify it's being called.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Jul 2, 2025): <!-- Answer --> Your logger isn't logging because the log function you provided always calls logger.error('Auth log'), which doesn't use the actual log message or level, and may cause recursion or reference an undefined logger if not in scope. Instead, you should use console.log (or console.error, etc.) directly in your custom log function. For example: ```ts const authLogger = { level: 'info', log: (level, message, ...args) => { console.log(`[${level}]`, message, ...args); }, }; ``` Also, keep in mind that Better-Auth's default logger only outputs messages at the 'error' level unless you explicitly set a lower log level. Even then, some log levels (like 'debug') may not produce output because debug logging isn't implemented for most endpoints yet ([source](https://github.com/better-auth/better-auth/issues/3169)). If you still see nothing, try adding a direct console.log inside your log function to verify it's being called. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/63b549c8-b1e7-404f-bf2e-1a0ce5d9e1ad?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/63b549c8-b1e7-404f-bf2e-1a0ce5d9e1ad?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/63b549c8-b1e7-404f-bf2e-1a0ce5d9e1ad?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/63b549c8-b1e7-404f-bf2e-1a0ce5d9e1ad?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/63b549c8-b1e7-404f-bf2e-1a0ce5d9e1ad?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/63b549c8-b1e7-404f-bf2e-1a0ce5d9e1ad?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/63b549c8-b1e7-404f-bf2e-1a0ce5d9e1ad?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3250)
Author
Owner

@irg1008 commented on GitHub (Jul 2, 2025):

No

@irg1008 commented on GitHub (Jul 2, 2025): No
Author
Owner

@TanishValesha commented on GitHub (Jul 5, 2025):

Can you share a demo video or a screenshot which explain the issue visually ?

@TanishValesha commented on GitHub (Jul 5, 2025): Can you share a demo video or a screenshot which explain the issue visually ?
Author
Owner

@ping-maxwell commented on GitHub (Jul 8, 2025):

It mostly depends on what's getting logged. For example, trying to sign-in using a social provider which isn't configured will work at displaying an error using the custom logger.

The reason behind the "sometimes" comes down to how the endpoints use the ctx (Endpoint Context). Our endpoint context provides a logger function which allows the developers to call the log function which will run additional logic such as checking if the auth config provides a custom logger. However in some cases where there isn't access to ctx such as in custom predefined functions it's most likely using our globally accessible logger which doesn't take into account of your auth config.

Can you tell me which log you're seeing, and after what action you performed? @irg1008

@ping-maxwell commented on GitHub (Jul 8, 2025): It mostly depends on what's getting logged. For example, trying to sign-in using a social provider which isn't configured will work at displaying an error using the custom logger. The reason behind the "sometimes" comes down to how the endpoints use the `ctx` (Endpoint Context). Our endpoint context provides a logger function which allows the developers to call the log function which will run additional logic such as checking if the auth config provides a custom logger. However in some cases where there isn't access to `ctx` such as in custom predefined functions it's most likely using our globally accessible `logger` which doesn't take into account of your auth config. Can you tell me which log you're seeing, and after what action you performed? @irg1008
Author
Owner

@KazimirPodolski commented on GitHub (Jul 27, 2025):

@ping-maxwell For example, even with log level "debug", throwing an error in a before hook is completely silent.

At this point one can only wonder what is logged at all. Which kinda defeats the purpose of logging, won't you agree?

@KazimirPodolski commented on GitHub (Jul 27, 2025): @ping-maxwell For example, even with log level "debug", throwing an error in a `before` hook is completely silent. At this point one can only wonder what is logged at all. Which kinda defeats the purpose of logging, won't you agree?
Author
Owner

@MendyLanda commented on GitHub (Aug 11, 2025):

@KazimirPodolski better auth currently logs across all plugins, exactly in 99 places
94 logger.error calls
4 logger.info calls
1 logger.warn call

@MendyLanda commented on GitHub (Aug 11, 2025): @KazimirPodolski better auth currently logs across all plugins, exactly in 99 places 94 logger.error calls 4 logger.info calls 1 logger.warn call
Author
Owner

@jtomaszewski commented on GitHub (Nov 17, 2025):

Here's how my logs look like currently:

Image

For such a logger configuration:

    logger: {
      disabled: false,
      level: "debug",
      log(level, message, ...args) {
        console.log("bbb auth", level, message, ...args);
      },
    },
    hooks: {
      before: authErrorRedirectHook(webUrl),
    },
    onAPIError: {
      onError: (error) => {
        console.error("better-auth error", error);
      },
      errorURL: `${webUrl}/auth-error`,
    },
    plugins: [
      authLoggerPlugin(),
    ],

And that's only because I wrote myself a plugin for logging requests and responses:

import type { BetterAuthPlugin } from "better-auth";
import { createAuthMiddleware } from "better-auth/plugins";
import { Logger } from "@nestjs/common";

interface LoggerPluginOptions {
  logger?: Logger;
  disabled?: boolean;
}

export const authLoggerPlugin = (
  options: LoggerPluginOptions = {},
): BetterAuthPlugin => {
  const logger = options.logger ?? new Logger("BetterAuth");
  const disabled = options.disabled ?? false;

  if (disabled) {
    return {
      id: "auth-logger",
    };
  }

  return {
    id: "auth-logger",
    hooks: {
      before: [
        {
          matcher: () => true,
          // eslint-disable-next-line @typescript-eslint/require-await
          handler: createAuthMiddleware(async (ctx) => {
            ctx.context.requestStartTime = Date.now();
          }),
        },
      ],
      after: [
        {
          matcher: () => true,
          // eslint-disable-next-line @typescript-eslint/require-await
          handler: createAuthMiddleware(async (ctx) => {
            const startTime = ctx.context.requestStartTime as
              | number
              | undefined;
            const responseTime = startTime ? Date.now() - startTime : undefined;

            const method = ctx.request?.method ?? "GET";
            const path = ctx.path;
            const statusCode =
              (ctx.context.returned as { status?: number })?.status ?? 200;

            const logLevel = determineLogLevel(statusCode);
            const message = `-> ${method} ${path} ${statusCode}${responseTime ? ` ${responseTime}ms` : ""}`;

            switch (logLevel) {
              case "error":
                logger.error(message);
                break;
              case "warn":
                logger.warn(message);
                break;
              default:
                logger.log(message);
            }
          }),
        },
      ],
    },
  };
};

function determineLogLevel(
  statusCode: number,
): "info" | "warn" | "error" | "debug" {
  if (statusCode >= 500) {
    return "error";
  } else if (statusCode >= 400) {
    return "warn";
  }
  return "info";
}

Unfortunately, I'm still not able to send actual errors that happen during e.g. google OAuth to my error monitoring systems (e.g. Sentry).

the ERROR [Better Auth]: DriverException: insert into (...) value too long for type character varying(10) log that is seen in the screenshot - I have no idea what code prints it (I have yet to debug this...), and seems like there's no way to hook yourself into such errors. logger.log nor onAPIError.onError help in anything.

@jtomaszewski commented on GitHub (Nov 17, 2025): Here's how my logs look like currently: <img width="1412" height="156" alt="Image" src="https://github.com/user-attachments/assets/b814e43a-bf2a-4f8f-b5d3-d668a0d71b32" /> For such a logger configuration: ```ts logger: { disabled: false, level: "debug", log(level, message, ...args) { console.log("bbb auth", level, message, ...args); }, }, hooks: { before: authErrorRedirectHook(webUrl), }, onAPIError: { onError: (error) => { console.error("better-auth error", error); }, errorURL: `${webUrl}/auth-error`, }, plugins: [ authLoggerPlugin(), ], ``` And that's only because I wrote myself a plugin for logging requests and responses: ```ts import type { BetterAuthPlugin } from "better-auth"; import { createAuthMiddleware } from "better-auth/plugins"; import { Logger } from "@nestjs/common"; interface LoggerPluginOptions { logger?: Logger; disabled?: boolean; } export const authLoggerPlugin = ( options: LoggerPluginOptions = {}, ): BetterAuthPlugin => { const logger = options.logger ?? new Logger("BetterAuth"); const disabled = options.disabled ?? false; if (disabled) { return { id: "auth-logger", }; } return { id: "auth-logger", hooks: { before: [ { matcher: () => true, // eslint-disable-next-line @typescript-eslint/require-await handler: createAuthMiddleware(async (ctx) => { ctx.context.requestStartTime = Date.now(); }), }, ], after: [ { matcher: () => true, // eslint-disable-next-line @typescript-eslint/require-await handler: createAuthMiddleware(async (ctx) => { const startTime = ctx.context.requestStartTime as | number | undefined; const responseTime = startTime ? Date.now() - startTime : undefined; const method = ctx.request?.method ?? "GET"; const path = ctx.path; const statusCode = (ctx.context.returned as { status?: number })?.status ?? 200; const logLevel = determineLogLevel(statusCode); const message = `-> ${method} ${path} ${statusCode}${responseTime ? ` ${responseTime}ms` : ""}`; switch (logLevel) { case "error": logger.error(message); break; case "warn": logger.warn(message); break; default: logger.log(message); } }), }, ], }, }; }; function determineLogLevel( statusCode: number, ): "info" | "warn" | "error" | "debug" { if (statusCode >= 500) { return "error"; } else if (statusCode >= 400) { return "warn"; } return "info"; } ``` Unfortunately, I'm still not able to send actual errors that happen during e.g. google OAuth to my error monitoring systems (e.g. Sentry). the `ERROR [Better Auth]: DriverException: insert into (...) value too long for type character varying(10)` log that is seen in the screenshot - I have no idea what code prints it (I have yet to debug this...), and seems like there's no way to hook yourself into such errors. `logger.log` nor `onAPIError.onError` help in anything.
Author
Owner

@jtomaszewski commented on GitHub (Nov 17, 2025):

Now looking at better-auth code, I see this:

Image

I guess we'd have to refactor all social-providers code so that they don't use a global logger reference (and remove the globally defined logger completely).

Do we have an issue/PR for that already?

@jtomaszewski commented on GitHub (Nov 17, 2025): Now looking at better-auth code, I see this: <img width="1271" height="1074" alt="Image" src="https://github.com/user-attachments/assets/b1e71eb2-7ddc-450d-a51f-a2f6188148f0" /> I guess we'd have to refactor all social-providers code so that they don't use a global `logger` reference (and remove the globally defined `logger` completely). Do we have an issue/PR for that already?
Author
Owner

@jtomaszewski commented on GitHub (Nov 17, 2025):

As a workaround, I've managed to capture all log levels of the better-auth's global logger, and also intercept any errors sent through its' logger.error function :

  const nestLogger = new Logger("BetterAuth");

  logger.debug = (message, ...args: unknown[]) => {
    nestLogger.debug(message, ...args);
  };
  logger.success = (message, ...args: unknown[]) => {
    nestLogger.log(message, ...args);
  };
  logger.info = (message, ...args: unknown[]) => {
    nestLogger.log(message, ...args);
  };
  logger.warn = (message, ...args: unknown[]) => {
    nestLogger.warn(message, ...args);
  };
  logger.error = (message: unknown, ...args: unknown[]) => {
    if (message instanceof Error) {
      Sentry.captureException(message);
    }
    nestLogger.error(message, ...args);
  };

  return betterAuth({
    logger: {
      disabled: process.env.NODE_ENV === "test",
      level: "debug",
      log(level, message, ...args: unknown[]) {
        const method =
          level === "error" ? "error" : level === "warn" ? "warn" : "log";
        nestLogger[method](message, ...args);
      },
    },
    onAPIError: {
      onError: (error) => {
        nestLogger.error(error);
        Sentry.captureException(error);
      },
    },
    plugins: [
      authLoggerPlugin({
        disabled: process.env.NODE_ENV === "test",
      }),
    ],

I'd be happy however to provide a PR for improving the interface of better-auth, so that such workarounds are not required. Given only you provide me some feedback whether u'd be interested in it, and whether u have any tips on how it should be done.

@jtomaszewski commented on GitHub (Nov 17, 2025): As a workaround, I've managed to capture all log levels of the better-auth's global logger, and also intercept any errors sent through its' `logger.error` function : ```ts const nestLogger = new Logger("BetterAuth"); logger.debug = (message, ...args: unknown[]) => { nestLogger.debug(message, ...args); }; logger.success = (message, ...args: unknown[]) => { nestLogger.log(message, ...args); }; logger.info = (message, ...args: unknown[]) => { nestLogger.log(message, ...args); }; logger.warn = (message, ...args: unknown[]) => { nestLogger.warn(message, ...args); }; logger.error = (message: unknown, ...args: unknown[]) => { if (message instanceof Error) { Sentry.captureException(message); } nestLogger.error(message, ...args); }; return betterAuth({ logger: { disabled: process.env.NODE_ENV === "test", level: "debug", log(level, message, ...args: unknown[]) { const method = level === "error" ? "error" : level === "warn" ? "warn" : "log"; nestLogger[method](message, ...args); }, }, onAPIError: { onError: (error) => { nestLogger.error(error); Sentry.captureException(error); }, }, plugins: [ authLoggerPlugin({ disabled: process.env.NODE_ENV === "test", }), ], ``` **I'd be happy however to provide a PR for improving the interface of better-auth, so that such workarounds are not required.** Given only you provide me some feedback whether u'd be interested in it, and whether u have any tips on how it should be done.
Author
Owner

@tanishqkancharla commented on GitHub (Jan 13, 2026):

Here are 5 files in the better-auth codebase that prove the issue still exists:

packages/core/src/env/logger.ts (Line 130)

Exports the global singleton logger that gets imported everywhere
packages/core/src/social-providers/google.ts (Lines 3, 68)

Social providers import and use the global logger directly instead of the configured one
packages/better-auth/src/adapters/drizzle-adapter/drizzle-adapter.ts (Lines 10, 368)

Database adapters bypass custom logger configuration
packages/better-auth/src/db/adapter-base.ts (Lines 4, 31)

Even though it receives BetterAuthOptions, still uses global logger
packages/core/src/oauth2/verify.ts (Lines 15, 177)

Core OAuth logic can't capture errors in custom logging systems
The root cause: these modules use import { logger } from "../env" instead of receiving the configured logger through context or function parameters.

@tanishqkancharla commented on GitHub (Jan 13, 2026): Here are 5 files in the better-auth codebase that prove the issue still exists: packages/core/src/env/logger.ts (Line 130) Exports the global singleton logger that gets imported everywhere packages/core/src/social-providers/google.ts (Lines 3, 68) Social providers import and use the global logger directly instead of the configured one packages/better-auth/src/adapters/drizzle-adapter/drizzle-adapter.ts (Lines 10, 368) Database adapters bypass custom logger configuration packages/better-auth/src/db/adapter-base.ts (Lines 4, 31) Even though it receives BetterAuthOptions, still uses global logger packages/core/src/oauth2/verify.ts (Lines 15, 177) Core OAuth logic can't capture errors in custom logging systems The root cause: these modules use import { logger } from "../env" instead of receiving the configured logger through context or function parameters.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1455