src/internal/utils/log.ts (111 lines of code) (raw):

// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { hasOwn } from './values'; import { type BaseAnthropic } from '../../client'; import { RequestOptions } from '../request-options'; type LogFn = (message: string, ...rest: unknown[]) => void; export type Logger = { error: LogFn; warn: LogFn; info: LogFn; debug: LogFn; }; export type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug'; const levelNumbers = { off: 0, error: 200, warn: 300, info: 400, debug: 500, }; export const parseLogLevel = ( maybeLevel: string | undefined, sourceName: string, client: BaseAnthropic, ): LogLevel | undefined => { if (!maybeLevel) { return undefined; } if (hasOwn(levelNumbers, maybeLevel)) { return maybeLevel; } loggerFor(client).warn( `${sourceName} was set to ${JSON.stringify(maybeLevel)}, expected one of ${JSON.stringify( Object.keys(levelNumbers), )}`, ); return undefined; }; function noop() {} function makeLogFn(fnLevel: keyof Logger, logger: Logger | undefined, logLevel: LogLevel) { if (!logger || levelNumbers[fnLevel] > levelNumbers[logLevel]) { return noop; } else { // Don't wrap logger functions, we want the stacktrace intact! return logger[fnLevel].bind(logger); } } const noopLogger = { error: noop, warn: noop, info: noop, debug: noop, }; let cachedLoggers = new WeakMap<Logger, [LogLevel, Logger]>(); export function loggerFor(client: BaseAnthropic): Logger { const logger = client.logger; const logLevel = client.logLevel ?? 'off'; if (!logger) { return noopLogger; } const cachedLogger = cachedLoggers.get(logger); if (cachedLogger && cachedLogger[0] === logLevel) { return cachedLogger[1]; } const levelLogger = { error: makeLogFn('error', logger, logLevel), warn: makeLogFn('warn', logger, logLevel), info: makeLogFn('info', logger, logLevel), debug: makeLogFn('debug', logger, logLevel), }; cachedLoggers.set(logger, [logLevel, levelLogger]); return levelLogger; } export const formatRequestDetails = (details: { options?: RequestOptions | undefined; headers?: Headers | Record<string, string> | undefined; retryOfRequestLogID?: string | undefined; retryOf?: string | undefined; url?: string | undefined; status?: number | undefined; method?: string | undefined; durationMs?: number | undefined; message?: unknown; body?: unknown; }) => { if (details.options) { details.options = { ...details.options }; delete details.options['headers']; // redundant + leaks internals } if (details.headers) { details.headers = Object.fromEntries( (details.headers instanceof Headers ? [...details.headers] : Object.entries(details.headers)).map( ([name, value]) => [ name, ( name.toLowerCase() === 'x-api-key' || name.toLowerCase() === 'authorization' || name.toLowerCase() === 'cookie' || name.toLowerCase() === 'set-cookie' ) ? '***' : value, ], ), ); } if ('retryOfRequestLogID' in details) { if (details.retryOfRequestLogID) { details.retryOf = details.retryOfRequestLogID; } delete details.retryOfRequestLogID; } return details; };