Skip to content

refactor: split loggers #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 25, 2025
Merged
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
93 changes: 66 additions & 27 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type LogLevel = LoggingMessageNotification["params"]["level"];

abstract class LoggerBase {
abstract log(level: LogLevel, id: MongoLogId, context: string, message: string): void;

info(id: MongoLogId, context: string, message: string): void {
this.log("info", id, context, message);
}
Expand Down Expand Up @@ -47,22 +48,35 @@ class ConsoleLogger extends LoggerBase {
}
}

class Logger extends LoggerBase {
constructor(
private logWriter: MongoLogWriter,
private server: McpServer
) {
class DiskLogger extends LoggerBase {
private constructor(private logWriter: MongoLogWriter) {
super();
}

static async fromPath(logPath: string): Promise<DiskLogger> {
await fs.mkdir(logPath, { recursive: true });

const manager = new MongoLogManager({
directory: logPath,
retentionDays: 30,
onwarn: console.warn,
onerror: console.error,
gzip: false,
retentionGB: 1,
});

await manager.cleanupOldLogFiles();

const logWriter = await manager.createLogWriter();

return new DiskLogger(logWriter);
}

log(level: LogLevel, id: MongoLogId, context: string, message: string): void {
message = redact(message);
const mongoDBLevel = this.mapToMongoDBLogLevel(level);

this.logWriter[mongoDBLevel]("MONGODB-MCP", id, context, message);
void this.server.server.sendLoggingMessage({
level,
data: `[${context}]: ${message}`,
});
}

private mapToMongoDBLogLevel(level: LogLevel): "info" | "warn" | "error" | "debug" | "fatal" {
Expand All @@ -86,31 +100,56 @@ class Logger extends LoggerBase {
}
}

class ProxyingLogger extends LoggerBase {
private internalLogger: LoggerBase = new ConsoleLogger();
class McpLogger extends LoggerBase {
constructor(private server: McpServer) {
super();
}

log(level: LogLevel, _: MongoLogId, context: string, message: string): void {
void this.server.server.sendLoggingMessage({
level,
data: `[${context}]: ${message}`,
});
}
}

class CompositeLogger extends LoggerBase {
private loggers: LoggerBase[];

constructor(...loggers: LoggerBase[]) {
super();

if (loggers.length === 0) {
// default to ConsoleLogger
this.loggers = [new ConsoleLogger()];
return;
}

this.loggers = [...loggers];
}

setLoggers(...loggers: LoggerBase[]): void {
if (loggers.length === 0) {
throw new Error("At least one logger must be provided");
}
this.loggers = [...loggers];
}

log(level: LogLevel, id: MongoLogId, context: string, message: string): void {
this.internalLogger.log(level, id, context, message);
for (const logger of this.loggers) {
logger.log(level, id, context, message);
}
}
}

const logger = new ProxyingLogger();
const logger = new CompositeLogger();
export default logger;

export async function initializeLogger(server: McpServer, logPath: string): Promise<void> {
await fs.mkdir(logPath, { recursive: true });

const manager = new MongoLogManager({
directory: logPath,
retentionDays: 30,
onwarn: console.warn,
onerror: console.error,
gzip: false,
retentionGB: 1,
});
export async function initializeLogger(server: McpServer, logPath: string): Promise<LoggerBase> {
const diskLogger = await DiskLogger.fromPath(logPath);
const mcpLogger = new McpLogger(server);

await manager.cleanupOldLogFiles();
logger.setLoggers(mcpLogger, diskLogger);

const logWriter = await manager.createLogWriter();
logger["internalLogger"] = new Logger(logWriter, server);
return logger;
}
Loading