Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { ILogger } from '@theia/core';
import { ContainerModule } from '@theia/core/shared/inversify';
import { CodeCompletionAgent, CodeCompletionAgentImpl } from './code-completion-agent';
import { AIFrontendApplicationContribution } from './ai-code-frontend-application-contribution';
Expand All @@ -25,10 +24,6 @@ import { AICodeInlineCompletionsProvider } from './ai-code-inline-completion-pro
import { CodeCompletionPostProcessor, DefaultCodeCompletionPostProcessor } from './code-completion-postprocessor';

export default new ContainerModule(bind => {
bind(ILogger).toDynamicValue(ctx => {
const parentLogger = ctx.container.get<ILogger>(ILogger);
return parentLogger.child('code-completion-agent');
}).inSingletonScope().whenTargetNamed('code-completion-agent');
bind(CodeCompletionAgentImpl).toSelf().inSingletonScope();
bind(CodeCompletionAgent).toService(CodeCompletionAgentImpl);
bind(Agent).toService(CodeCompletionAgentImpl);
Expand Down
6 changes: 0 additions & 6 deletions packages/ai-history/src/browser/ai-history-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { CommunicationRecordingService } from '@theia/ai-core';
import { ContainerModule } from '@theia/core/shared/inversify';
import { DefaultCommunicationRecordingService } from '../common/communication-recording-service';
import { bindViewContribution, WidgetFactory } from '@theia/core/lib/browser';
import { ILogger } from '@theia/core';
import { AIHistoryViewContribution } from './ai-history-contribution';
import { AIHistoryView } from './ai-history-widget';
import '../../src/browser/style/ai-history.css';
Expand All @@ -27,11 +26,6 @@ export default new ContainerModule(bind => {
bind(DefaultCommunicationRecordingService).toSelf().inSingletonScope();
bind(CommunicationRecordingService).toService(DefaultCommunicationRecordingService);

bind(ILogger).toDynamicValue(ctx => {
const parentLogger = ctx.container.get<ILogger>(ILogger);
return parentLogger.child('llm-communication-recorder');
}).inSingletonScope().whenTargetNamed('llm-communication-recorder');

bindViewContribution(bind, AIHistoryViewContribution);

bind(AIHistoryView).toSelf();
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/browser/logger-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@

import { ContainerModule, Container } from 'inversify';
import { ILoggerServer, loggerPath, ConsoleLogger } from '../common/logger-protocol';
import { ILogger, Logger, LoggerFactory, setRootLogger, LoggerName, rootLoggerName } from '../common/logger';
import { ILogger, Logger, LoggerFactory, setRootLogger, LoggerName } from '../common/logger';
import { LoggerWatcher } from '../common/logger-watcher';
import { WebSocketConnectionProvider } from './messaging';
import { FrontendApplicationContribution } from './frontend-application-contribution';
import { EncodingError } from '../common/message-rpc/rpc-message-encoder';
import { bindCommonLogger } from '../common/logger-binding';

export const loggerFrontendModule = new ContainerModule(bind => {
bind(FrontendApplicationContribution).toDynamicValue(ctx => ({
Expand All @@ -29,9 +30,7 @@ export const loggerFrontendModule = new ContainerModule(bind => {
}
}));

bind(LoggerName).toConstantValue(rootLoggerName);
bind(ILogger).to(Logger).inSingletonScope().whenTargetIsDefault();
bind(LoggerWatcher).toSelf().inSingletonScope();
bindCommonLogger(bind);
bind(ILoggerServer).toDynamicValue(ctx => {
const loggerWatcher = ctx.container.get(LoggerWatcher);
const connection = ctx.container.get(WebSocketConnectionProvider);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
// Copyright (C) 2025 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -14,17 +14,21 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { interfaces } from '@theia/core/shared/inversify';
import { ILogger } from '@theia/core';
import { interfaces } from 'inversify';
import { ILogger, Logger, LoggerName, rootLoggerName } from './logger';
import { LoggerWatcher } from './logger-watcher';

/**
* Create the bindings common to node and browser.
*
* @param bind The bind function from inversify.
*/
export function createCommonBindings(bind: interfaces.Bind): void {
export function bindCommonLogger(bind: interfaces.Bind): void {
bind(LoggerName).toConstantValue(rootLoggerName);
bind(ILogger).to(Logger).inSingletonScope().when(request => getName(request) === undefined);
bind(ILogger).toDynamicValue(ctx => {
const logger = ctx.container.get<ILogger>(ILogger);
return logger.child('terminal');
}).inSingletonScope().whenTargetNamed('terminal');
return logger.child(getName(ctx.currentRequest)!);
}).when(request => getName(request) !== undefined);
bind(LoggerWatcher).toSelf().inSingletonScope();
}

function getName(request: interfaces.Request): string | undefined {
const named = request.target.metadata.find(e => e.key === 'named');
return named ? named.value?.toString() : undefined;
}
6 changes: 4 additions & 2 deletions packages/core/src/common/logger-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ export namespace ConsoleLogger {
console.trace = consoles.get(LogLevel.TRACE)!;
console.log = originalConsoleLog;
}
export function log(name: string, logLevel: number, message: string, params: any[]): void {
export function log(name: string, logLevel: number, message: string, params: any[]): string {
const console = consoles.get(logLevel) || originalConsoleLog;
const severity = (LogLevel.strings.get(logLevel) || 'unknown').toUpperCase();
const now = new Date();
console(`${now.toISOString()} ${name} ${severity} ${message}`, ...params);
const formattedMessage = `${now.toISOString()} ${name} ${severity} ${message}`;
console(formattedMessage, ...params);
return formattedMessage;
}
}
11 changes: 10 additions & 1 deletion packages/core/src/common/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ export class Logger implements ILogger {
@inject(LoggerFactory) protected readonly factory: LoggerFactory;
@inject(LoggerName) protected name: string;

protected cache = new Map<string, ILogger>();

@postConstruct()
protected init(): void {
if (this.name !== rootLoggerName) {
Expand Down Expand Up @@ -384,6 +386,13 @@ export class Logger implements ILogger {
}

child(name: string): ILogger {
return this.factory(name);
const existing = this.cache.get(name);
if (existing) {
return existing;
} else {
const child = this.factory(name);
this.cache.set(name, child);
return child;
}
}
}
25 changes: 23 additions & 2 deletions packages/core/src/node/console-logger-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,23 @@ import { inject, injectable, postConstruct } from 'inversify';
import { LoggerWatcher } from '../common/logger-watcher';
import { LogLevelCliContribution } from './logger-cli-contribution';
import { ILoggerServer, ILoggerClient, ConsoleLogger, rootLoggerName } from '../common/logger-protocol';
import { format } from 'util';
import { EOL } from 'os';
import * as fs from 'fs/promises';

@injectable()
export class ConsoleLoggerServer implements ILoggerServer {

protected client: ILoggerClient | undefined = undefined;
protected client?: ILoggerClient;

@inject(LoggerWatcher)
protected watcher: LoggerWatcher;

@inject(LogLevelCliContribution)
protected cli: LogLevelCliContribution;

protected failedLogFileWrite = false;

@postConstruct()
protected init(): void {
this.setLogLevel(rootLoggerName, this.cli.defaultLogLevel);
Expand Down Expand Up @@ -59,7 +64,23 @@ export class ConsoleLoggerServer implements ILoggerServer {
async log(name: string, logLevel: number, message: string, params: any[]): Promise<void> {
const configuredLogLevel = await this.getLogLevel(name);
if (logLevel >= configuredLogLevel) {
ConsoleLogger.log(name, logLevel, message, params);
const fullMessage = ConsoleLogger.log(name, logLevel, message, params);
await this.logToFile(fullMessage, params);
}
}

protected async logToFile(message: string, params: any[]): Promise<void> {
if (this.cli.logFile) {
try {
const formatted = format(message, ...params) + EOL;
await fs.appendFile(this.cli.logFile, formatted);
} catch (error) {
// Avoid spamming the console with errors in case the log file is not writable
if (!this.failedLogFileWrite) {
console.error(`Failed to write to log file: ${error}`);
this.failedLogFileWrite = true;
}
}
}
}

Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/node/logger-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,19 @@

import { ContainerModule, Container, interfaces } from 'inversify';
import { ConnectionHandler, RpcConnectionHandler } from '../common/messaging';
import { ILogger, LoggerFactory, Logger, setRootLogger, LoggerName, rootLoggerName } from '../common/logger';
import { ILogger, LoggerFactory, Logger, setRootLogger, LoggerName } from '../common/logger';
import { ILoggerServer, ILoggerClient, loggerPath, DispatchingLoggerClient } from '../common/logger-protocol';
import { ConsoleLoggerServer } from './console-logger-server';
import { LoggerWatcher } from '../common/logger-watcher';
import { BackendApplicationContribution } from './backend-application';
import { CliContribution } from './cli';
import { LogLevelCliContribution } from './logger-cli-contribution';
import { bindCommonLogger } from '../common/logger-binding';

export function bindLogger(bind: interfaces.Bind, props?: {
onLoggerServerActivation?: (context: interfaces.Context, server: ILoggerServer) => void
}): void {
bind(LoggerName).toConstantValue(rootLoggerName);
bind(ILogger).to(Logger).inSingletonScope().whenTargetIsDefault();
bind(LoggerWatcher).toSelf().inSingletonScope();
bindCommonLogger(bind);
bind<ILoggerServer>(ILoggerServer).to(ConsoleLoggerServer).inSingletonScope().onActivation((context, server) => {
if (props && props.onLoggerServerActivation) {
props.onLoggerServerActivation(context, server);
Expand Down
40 changes: 33 additions & 7 deletions packages/core/src/node/logger-cli-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export class LogLevelCliContribution implements CliContribution {
*/
protected _defaultLogLevel: LogLevel = LogLevel.INFO;

protected _logFile?: string;

protected logConfigChangedEvent: Emitter<void> = new Emitter<void>();

get defaultLogLevel(): LogLevel {
Expand All @@ -53,6 +55,10 @@ export class LogLevelCliContribution implements CliContribution {
return this._logLevels;
}

get logFile(): string | undefined {
return this._logFile;
}

configure(conf: yargs.Argv): void {
conf.option('log-level', {
description: 'Sets the default log level',
Expand All @@ -65,6 +71,12 @@ export class LogLevelCliContribution implements CliContribution {
type: 'string',
nargs: 1,
});

conf.option('log-file', {
description: 'Path to the log file',
type: 'string',
nargs: 1
});
}

async setArguments(args: yargs.Arguments): Promise<void> {
Expand All @@ -87,22 +99,36 @@ export class LogLevelCliContribution implements CliContribution {
console.error(`Error reading log config file ${filename}: ${e}`);
}
}

if (args['log-file'] !== undefined) {
let filename: string = args['log-file'] as string;
try {
filename = path.resolve(filename);
await fs.writeFile(filename, '');
this._logFile = filename;
} catch (e) {
console.error(`Error creating log file ${filename}: ${e}`);
}
}
}

protected async watchLogConfigFile(filename: string): Promise<void> {
await subscribe(filename, async (err, events) => {
const dir = path.dirname(filename);
await subscribe(dir, async (err, events) => {
if (err) {
console.log(`Error during log file watching ${filename}: ${err}`);
return;
}
try {
for (const event of events) {
switch (event.type) {
case 'create':
case 'update':
await this.slurpLogConfigFile(filename);
this.logConfigChangedEvent.fire(undefined);
break;
if (event.path === filename) {
switch (event.type) {
case 'create':
case 'update':
await this.slurpLogConfigFile(filename);
this.logConfigChangedEvent.fire(undefined);
break;
}
}
}
} catch (e) {
Expand Down
6 changes: 1 addition & 5 deletions packages/debug/src/node/debug-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { bindContributionProvider, ILogger } from '@theia/core/lib/common';
import { bindContributionProvider } from '@theia/core/lib/common';
import { ContainerModule } from '@theia/core/shared/inversify';
import {
DebugPath,
Expand Down Expand Up @@ -50,8 +50,4 @@ export default new ContainerModule(bind => {
bind(DebugAdapterFactory).to(LaunchBasedDebugAdapterFactory).inSingletonScope();
bind(DebugAdapterSessionManager).toSelf().inSingletonScope();
bind(MessagingService.Contribution).toService(DebugAdapterSessionManager);

bind(ILogger).toDynamicValue(({ container }) =>
container.get<ILogger>(ILogger).child('debug')
).inSingletonScope().whenTargetNamed('debug');
});
5 changes: 0 additions & 5 deletions packages/process/src/node/process-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@ import { TerminalProcess, TerminalProcessOptions, TerminalProcessFactory } from
import { TaskTerminalProcess, TaskTerminalProcessFactory } from './task-terminal-process';
import { BackendApplicationContribution } from '@theia/core/lib/node';
import { ProcessManager } from './process-manager';
import { ILogger } from '@theia/core/lib/common';
import { MultiRingBuffer, MultiRingBufferOptions } from './multi-ring-buffer';

export default new ContainerModule(bind => {
bind(RawProcess).toSelf().inTransientScope();
bind(ProcessManager).toSelf().inSingletonScope();
bind(BackendApplicationContribution).toService(ProcessManager);
bind(ILogger).toDynamicValue(ctx => {
const parentLogger = ctx.container.get<ILogger>(ILogger);
return parentLogger.child('process');
}).inSingletonScope().whenTargetNamed('process');
bind(RawProcessFactory).toFactory(ctx =>
(options: RawProcessOptions | RawForkOptions) => {
const child = new Container({ defaultScope: 'Singleton' });
Expand Down
7 changes: 0 additions & 7 deletions packages/task/src/common/task-common-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// *****************************************************************************

import { interfaces } from '@theia/core/shared/inversify';
import { ILogger } from '@theia/core';
import { TaskWatcher } from './task-watcher';

/**
Expand All @@ -24,11 +23,5 @@ import { TaskWatcher } from './task-watcher';
* @param bind The bind function from inversify.
*/
export function createCommonBindings(bind: interfaces.Bind): void {

bind(ILogger).toDynamicValue(ctx => {
const logger = ctx.container.get<ILogger>(ILogger);
return logger.child('task');
}).inSingletonScope().whenTargetNamed('task');

bind(TaskWatcher).toSelf().inSingletonScope();
}
3 changes: 0 additions & 3 deletions packages/terminal/src/browser/terminal-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { TerminalWidget, TerminalWidgetOptions } from './base/terminal-widget';
import { ITerminalServer, terminalPath } from '../common/terminal-protocol';
import { TerminalWatcher } from '../common/terminal-watcher';
import { IShellTerminalServer, shellTerminalPath, ShellTerminalServerProxy } from '../common/shell-terminal-protocol';
import { createCommonBindings } from '../common/terminal-common-module';
import { TerminalService } from './base/terminal-service';
import { bindTerminalPreferences } from './terminal-preferences';
import { TerminalContribution } from './terminal-contribution';
Expand Down Expand Up @@ -104,8 +103,6 @@ export default new ContainerModule(bind => {
}).inSingletonScope();
bind(IShellTerminalServer).toService(ShellTerminalServerProxy);

createCommonBindings(bind);

bindContributionProvider(bind, TerminalContribution);

// terminal link provider contribution point
Expand Down
3 changes: 0 additions & 3 deletions packages/terminal/src/node/terminal-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { TerminalServer } from './terminal-server';
import { IShellTerminalServer, shellTerminalPath } from '../common/shell-terminal-protocol';
import { ShellTerminalServer } from '../node/shell-terminal-server';
import { TerminalWatcher } from '../common/terminal-watcher';
import { createCommonBindings } from '../common/terminal-common-module';
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';

export function bindTerminalServer(bind: interfaces.Bind, { path, identifier, constructor }: {
Expand Down Expand Up @@ -77,6 +76,4 @@ export default new ContainerModule(bind => {
identifier: IShellTerminalServer,
constructor: ShellTerminalServer
});

createCommonBindings(bind);
});
Loading