next-auth
Version:
Authentication for Next.js
118 lines (106 loc) • 3.29 kB
text/typescript
import { UnknownError } from "../core/errors"
// TODO: better typing
/** Makes sure that error is always serializable */
function formatError(o: unknown): unknown {
if (o instanceof Error && !(o instanceof UnknownError)) {
return { message: o.message, stack: o.stack, name: o.name }
}
if (hasErrorProperty(o)) {
o.error = formatError(o.error) as Error
o.message = o.message ?? o.error.message
}
return o
}
function hasErrorProperty(
x: unknown
): x is { error: Error; [key: string]: unknown } {
return !!(x as any)?.error
}
export type WarningCode =
| "NEXTAUTH_URL"
| "NO_SECRET"
| "TWITTER_OAUTH_2_BETA"
| "DEBUG_ENABLED"
/**
* Override any of the methods, and the rest will use the default logger.
*
* [Documentation](https://next-auth.js.org/configuration/options#logger)
*/
export interface LoggerInstance extends Record<string, Function> {
warn: (code: WarningCode) => void
error: (
code: string,
/**
* Either an instance of (JSON serializable) Error
* or an object that contains some debug information.
* (Error is still available through `metadata.error`)
*/
metadata: Error | { error: Error; [key: string]: unknown }
) => void
debug: (code: string, metadata: unknown) => void
}
const _logger: LoggerInstance = {
error(code, metadata) {
metadata = formatError(metadata) as Error
console.error(
`[next-auth][error][${code}]`,
`\nhttps://next-auth.js.org/errors#${code.toLowerCase()}`,
metadata.message,
metadata
)
},
warn(code) {
console.warn(
`[next-auth][warn][${code}]`,
`\nhttps://next-auth.js.org/warnings#${code.toLowerCase()}`
)
},
debug(code, metadata) {
console.log(`[next-auth][debug][${code}]`, metadata)
},
}
/**
* Override the built-in logger with user's implementation.
* Any `undefined` level will use the default logger.
*/
export function setLogger(
newLogger: Partial<LoggerInstance> = {},
debug?: boolean
) {
// Turn off debug logging if `debug` isn't set to `true`
if (!debug) _logger.debug = () => {}
if (newLogger.error) _logger.error = newLogger.error
if (newLogger.warn) _logger.warn = newLogger.warn
if (newLogger.debug) _logger.debug = newLogger.debug
}
export default _logger
/** Serializes client-side log messages and sends them to the server */
export function proxyLogger(
logger: LoggerInstance = _logger,
basePath?: string
): LoggerInstance {
try {
if (typeof window === "undefined") {
return logger
}
const clientLogger: Record<string, unknown> = {}
for (const level in logger) {
clientLogger[level] = async (code: string, metadata: Error) => {
_logger[level](code, metadata) // Logs to console
if (level === "error") {
metadata = formatError(metadata) as Error
}
;(metadata as any).client = true
const url = `${basePath}/_log`
const body = new URLSearchParams({ level, code, ...(metadata as any) })
if (navigator.sendBeacon) {
return navigator.sendBeacon(url, body)
}
return await fetch(url, { method: "POST", body, keepalive: true })
}
}
return clientLogger as unknown as LoggerInstance
} catch {
return _logger
}
}