Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions core/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,14 @@ export type {
} from './tools/vertex_ai_search_tool.js';
export {VertexRagRetrievalTool} from './tools/vertex_rag_retrieval_tool.js';
export {getClientLabels, runWithClientLabel} from './utils/client_labels.js';
export {LogLevel, getLogger, setLogLevel, setLogger} from './utils/logger.js';
export type {Logger} from './utils/logger.js';
export {
LogLevel,
WinstonLogger,
getLogger,
setLogLevel,
setLogger,
} from './utils/logger.js';
export type {Logger, WinstonLoggerOptions} from './utils/logger.js';
export {isGemini2OrAbove, isGemini3xFlashLive} from './utils/model_name.js';
export {zodObjectToSchema} from './utils/simple_zod_to_json.js';
export {Task} from './utils/task.js';
Expand Down
115 changes: 94 additions & 21 deletions core/src/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,68 @@ export interface Logger {
setLogLevel(level: LogLevel): void;
}

class SimpleLogger implements Logger {
/**
* Winston format options for a {@link WinstonLogger}.
*/
export interface WinstonLoggerOptions {
label: string;
colorize?: {
level?: boolean;
all?: boolean;
message?: boolean;
colors?: {
[level: string]: string;
};
};
timestamp?: boolean;
printFormat?: (info: {
message: unknown;
label?: string;
level?: string;
timestamp?: string;
}) => string;
}

/** Process-wide default level for loggers that were never explicitly pinned. */
let defaultLogLevel: LogLevel = LogLevel.INFO;

/**
* A level-gated {@link Logger} backed by winston.
*
* The level is resolved on every log call as the instance pin when
* {@link WinstonLogger.setLogLevel} was called on this instance, and the
* process-wide default set by {@link setLogLevel} otherwise. Resolving it at
* log time is what lets a single `setLogLevel` call reach loggers that were
* already constructed.
*/
export class WinstonLogger implements Logger {
private readonly logger: winston.Logger;
private logLevel: LogLevel = LogLevel.INFO;
private logLevel?: LogLevel;

constructor(options: WinstonLoggerOptions) {
const formats = [
winston.format.label({
label: options.label,
message: options.colorize?.all,
}),
winston.format((info) => {
info.level = info.level.toUpperCase();
return info;
})(),
];

if (options.colorize) {
formats.push(winston.format.colorize(options.colorize));
}
if (options.timestamp) {
formats.push(winston.format.timestamp());
}
if (options.printFormat) {
formats.push(winston.format.printf(options.printFormat));
} else {
formats.push(winston.format.printf((info) => info.message as string));
}

constructor() {
this.logger = winston.createLogger({
levels: {
'debug': LogLevel.DEBUG,
Expand All @@ -43,67 +100,78 @@ class SimpleLogger implements Logger {
'error': LogLevel.ERROR,
},
level: 'error',
format: winston.format.combine(
winston.format.label({label: 'ADK'}),
winston.format((info) => {
info.level = info.level.toUpperCase();
return info;
})(),
winston.format.colorize(),
winston.format.timestamp(),
winston.format.printf((info) => {
return `${info.level}: [${info.label}] ${info.timestamp} ${info.message}`;
}),
),
format: winston.format.combine(...formats),
transports: [new winston.transports.Console()],
});
}

/** Pins this instance's level, overriding the process-wide default. */
setLogLevel(level: LogLevel): void {
this.logLevel = level;
}

private isEnabled(level: LogLevel): boolean {
return (this.logLevel ?? defaultLogLevel) <= level;
}

log(level: LogLevel, ...messages: unknown[]): void {
if (this.logLevel > level) {
if (!this.isEnabled(level)) {
return;
}

this.logger.log(level.toString(), messages.join(' '));
// The winston level names are the LogLevel member names, lowercased; see
// the `levels` map above.
this.logger.log(LogLevel[level].toLowerCase(), messages.join(' '));
}

debug(...messages: unknown[]): void {
if (this.logLevel > LogLevel.DEBUG) {
if (!this.isEnabled(LogLevel.DEBUG)) {
return;
}

this.logger.debug(messages.join(' '));
}

info(...messages: unknown[]): void {
if (this.logLevel > LogLevel.INFO) {
if (!this.isEnabled(LogLevel.INFO)) {
return;
}

this.logger.info(messages.join(' '));
}

warn(...messages: unknown[]): void {
if (this.logLevel > LogLevel.WARN) {
if (!this.isEnabled(LogLevel.WARN)) {
return;
}

this.logger.warn(messages.join(' '));
}

error(...messages: unknown[]): void {
if (this.logLevel > LogLevel.ERROR) {
if (!this.isEnabled(LogLevel.ERROR)) {
return;
}

this.logger.error(messages.join(' '));
}
}

/** Format options for the built-in ADK logger. */
const DEFAULT_LOGGER_OPTIONS: WinstonLoggerOptions = {
label: 'ADK',
colorize: {level: true},
timestamp: true,
printFormat: (info) =>
`${info.level}: [${info.label}] ${info.timestamp} ${info.message}`,
};

class SimpleLogger extends WinstonLogger {
constructor() {
super(DEFAULT_LOGGER_OPTIONS);
}
}

/**
* A no-op logger that discards all log messages.
*/
Expand Down Expand Up @@ -141,8 +209,13 @@ export function resetLogger(): void {

/**
* Sets the log level for the logger.
*
* This sets the process-wide default that every built-in ADK logger follows,
* including loggers already constructed in other ADK packages, unless that
* logger was pinned with its own `setLogLevel` call.
*/
export function setLogLevel(level: LogLevel) {
defaultLogLevel = level;
logger.setLogLevel(level);
}

Expand Down
Loading
Loading