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: 10 additions & 0 deletions core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/

import {installNodeLogger} from './utils/logger_node.js';

// The Node entry point installs the winston-backed logger. `utils/logger.ts`
// itself must stay free of Node-only imports so that the browser entry point
// can reach it; see https://github.com/google/adk-js/issues/611.
// This call runs after the modules re-exported below are evaluated, so a module
// must log through the `logger` facade instead of holding the result of
// `getLogger()`.
installNodeLogger();

export {AGENT_CARD_PATH, RemoteA2AAgent} from './a2a/a2a_remote_agent.js';
export type {
A2AStreamEventData,
Expand Down
4 changes: 1 addition & 3 deletions core/src/tools/load_artifacts_tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import {FunctionDeclaration, Part, Type} from '@google/genai';

import {Context} from '../agents/context.js';
import {appendInstructions, LlmRequest} from '../models/llm_request.js';
import {getLogger} from '../utils/logger.js';
import {logger} from '../utils/logger.js';
import {
BaseTool,
RunAsyncToolRequest,
ToolProcessLlmRequest,
} from './base_tool.js';

const logger = getLogger();

const GEMINI_SUPPORTED_INLINE_MIME_PREFIXES = ['image/', 'audio/', 'video/'];
const GEMINI_SUPPORTED_INLINE_MIME_TYPES = new Set(['application/pdf']);
const TEXT_LIKE_MIME_TYPES = new Set([
Expand Down
4 changes: 1 addition & 3 deletions core/src/tools/vertex_ai_search_tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@

import {GenerateContentConfig, Tool} from '@google/genai';
import {ReadonlyContext} from '../agents/readonly_context.js';
import {getLogger} from '../utils/logger.js';
import {logger} from '../utils/logger.js';
import {
isGemini1Model,
isGeminiModel,
isGeminiModelIdCheckDisabled,
} from '../utils/model_name.js';
import {BaseTool, ToolProcessLlmRequest} from './base_tool.js';

const logger = getLogger();

export interface VertexAISearchDataStoreSpec {
dataStore?: string;
}
Expand Down
74 changes: 25 additions & 49 deletions core/src/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import * as winston from 'winston';

/** Log levels for the logger. */
export enum LogLevel {
Expand All @@ -30,35 +29,24 @@ export interface Logger {
setLogLevel(level: LogLevel): void;
}

/** The `console` method each level is written with. */
const CONSOLE_METHOD = {
[LogLevel.DEBUG]: 'debug',
[LogLevel.INFO]: 'info',
[LogLevel.WARN]: 'warn',
[LogLevel.ERROR]: 'error',
} as const;

/**
* The default logger. Writes through `console` so that it works unchanged in
* Node and in the browser. This module is reachable from the browser entry
* point, so it must not name a Node-only package; the Node entry point
* installs the winston-backed logger instead.
* See https://github.com/google/adk-js/issues/611.
*/
class SimpleLogger implements Logger {
private readonly logger: winston.Logger;
private logLevel: LogLevel = LogLevel.INFO;

constructor() {
this.logger = winston.createLogger({
levels: {
'debug': LogLevel.DEBUG,
'info': LogLevel.INFO,
'warn': LogLevel.WARN,
'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}`;
}),
),
transports: [new winston.transports.Console()],
});
}

setLogLevel(level: LogLevel): void {
this.logLevel = level;
}
Expand All @@ -68,39 +56,26 @@ class SimpleLogger implements Logger {
return;
}

this.logger.log(level.toString(), messages.join(' '));
const timestamp = new Date().toISOString();
const line = `${LogLevel[level]}: [ADK] ${timestamp} ${messages.join(' ')}`;

console[CONSOLE_METHOD[level]](line);
}

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

this.logger.debug(messages.join(' '));
this.log(LogLevel.DEBUG, ...messages);
}

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

this.logger.info(messages.join(' '));
this.log(LogLevel.INFO, ...messages);
}

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

this.logger.warn(messages.join(' '));
this.log(LogLevel.WARN, ...messages);
}

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

this.logger.error(messages.join(' '));
this.log(LogLevel.ERROR, ...messages);
}
}

Expand Down Expand Up @@ -133,7 +108,8 @@ export function getLogger(): Logger {
}

/**
* Resets the logger to the default SimpleLogger.
* Resets the logger to the built-in console logger. On Node this replaces the
* winston-backed logger that the Node entry point installs.
*/
export function resetLogger(): void {
currentLogger = new SimpleLogger();
Expand Down
98 changes: 98 additions & 0 deletions core/src/utils/logger_node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* The Node-only logger implementation.
*
* This module is the only place under `core/src` that may name `winston`.
* `winston` needs Node built-ins, so no browser bundler can resolve it: this
* module must never become reachable from `core/src/index_web.ts`. The Node
* entry point (`core/src/index.ts`) wires it in through `setLogger`.
* See https://github.com/google/adk-js/issues/611.
*/

import * as winston from 'winston';
import {Logger, LogLevel, setLogger} from './logger.js';

/** The default logger on Node. Writes through winston. */
export class WinstonLogger implements Logger {
private readonly logger: winston.Logger;
private logLevel: LogLevel = LogLevel.INFO;

constructor() {
this.logger = winston.createLogger({
levels: {
'debug': LogLevel.DEBUG,
'info': LogLevel.INFO,
'warn': LogLevel.WARN,
'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}`;
}),
),
transports: [new winston.transports.Console()],
});
}

setLogLevel(level: LogLevel): void {
this.logLevel = level;
}

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

this.logger.log(level.toString(), messages.join(' '));
}

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

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

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

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

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

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

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

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

/** Makes the winston-backed logger the current ADK logger. */
export function installNodeLogger(): void {
setLogger(new WinstonLogger());
}
Loading
Loading