• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb-js / mongodb-mcp-server / 16135806470

08 Jul 2025 06:34AM UTC coverage: 75.613% (+1.4%) from 74.187%
16135806470

Pull #340

github

web-flow
Merge 76e006853 into 5b7ba55e0
Pull Request #340: chore: avoid logging files on disk and move them to console for containers

237 of 397 branches covered (59.7%)

Branch coverage included in aggregate %.

15 of 22 new or added lines in 3 files covered. (68.18%)

3 existing lines in 2 files now uncovered.

842 of 1030 relevant lines covered (81.75%)

58.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.74
/src/logger.ts
1
import fs from "fs/promises";
2
import { mongoLogId, MongoLogId, MongoLogManager, MongoLogWriter } from "mongodb-log-writer";
3
import redact from "mongodb-redact";
4
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
import { LoggingMessageNotification } from "@modelcontextprotocol/sdk/types.js";
6

7
export type LogLevel = LoggingMessageNotification["params"]["level"];
8

9
export const LogId = {
37✔
10
    serverStartFailure: mongoLogId(1_000_001),
11
    serverInitialized: mongoLogId(1_000_002),
12
    serverCloseRequested: mongoLogId(1_000_003),
13
    serverClosed: mongoLogId(1_000_004),
14
    serverCloseFailure: mongoLogId(1_000_005),
15

16
    atlasCheckCredentials: mongoLogId(1_001_001),
17
    atlasDeleteDatabaseUserFailure: mongoLogId(1_001_002),
18
    atlasConnectFailure: mongoLogId(1_001_003),
19
    atlasInspectFailure: mongoLogId(1_001_004),
20

21
    telemetryDisabled: mongoLogId(1_002_001),
22
    telemetryEmitFailure: mongoLogId(1_002_002),
23
    telemetryEmitStart: mongoLogId(1_002_003),
24
    telemetryEmitSuccess: mongoLogId(1_002_004),
25
    telemetryMetadataError: mongoLogId(1_002_005),
26
    telemetryDeviceIdFailure: mongoLogId(1_002_006),
27
    telemetryDeviceIdTimeout: mongoLogId(1_002_007),
28

29
    toolExecute: mongoLogId(1_003_001),
30
    toolExecuteFailure: mongoLogId(1_003_002),
31
    toolDisabled: mongoLogId(1_003_003),
32

33
    mongodbConnectFailure: mongoLogId(1_004_001),
34
    mongodbDisconnectFailure: mongoLogId(1_004_002),
35

36
    toolUpdateFailure: mongoLogId(1_005_001),
37
} as const;
38

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

42
    info(id: MongoLogId, context: string, message: string): void {
43
        this.log("info", id, context, message);
134✔
44
    }
45

46
    error(id: MongoLogId, context: string, message: string): void {
47
        this.log("error", id, context, message);
35✔
48
    }
49
    debug(id: MongoLogId, context: string, message: string): void {
50
        this.log("debug", id, context, message);
286✔
51
    }
52

53
    notice(id: MongoLogId, context: string, message: string): void {
54
        this.log("notice", id, context, message);
×
55
    }
56

57
    warning(id: MongoLogId, context: string, message: string): void {
58
        this.log("warning", id, context, message);
×
59
    }
60

61
    critical(id: MongoLogId, context: string, message: string): void {
62
        this.log("critical", id, context, message);
×
63
    }
64

65
    alert(id: MongoLogId, context: string, message: string): void {
66
        this.log("alert", id, context, message);
×
67
    }
68

69
    emergency(id: MongoLogId, context: string, message: string): void {
70
        this.log("emergency", id, context, message);
×
71
    }
72
}
73

74
class ConsoleLogger extends LoggerBase {
75
    log(level: LogLevel, id: MongoLogId, context: string, message: string): void {
76
        message = redact(message);
10✔
77
        console.error(`[${level.toUpperCase()}] ${id.__value} - ${context}: ${message}`);
10✔
78
    }
79
}
80

81
class DiskLogger extends LoggerBase {
82
    private constructor(private logWriter: MongoLogWriter) {
33✔
83
        super();
33✔
84
    }
85

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

89
        const manager = new MongoLogManager({
33✔
90
            directory: logPath,
91
            retentionDays: 30,
92
            onwarn: console.warn,
93
            onerror: console.error,
94
            gzip: false,
95
            retentionGB: 1,
96
        });
97

98
        await manager.cleanupOldLogFiles();
33✔
99

100
        const logWriter = await manager.createLogWriter();
33✔
101

102
        return new DiskLogger(logWriter);
33✔
103
    }
104

105
    log(level: LogLevel, id: MongoLogId, context: string, message: string): void {
106
        message = redact(message);
445✔
107
        const mongoDBLevel = this.mapToMongoDBLogLevel(level);
445✔
108

109
        this.logWriter[mongoDBLevel]("MONGODB-MCP", id, context, message);
445✔
110
    }
111

112
    private mapToMongoDBLogLevel(level: LogLevel): "info" | "warn" | "error" | "debug" | "fatal" {
113
        switch (level) {
445!
114
            case "info":
115
                return "info";
132✔
116
            case "warning":
117
                return "warn";
×
118
            case "error":
119
                return "error";
35✔
120
            case "notice":
121
            case "debug":
122
                return "debug";
278✔
123
            case "critical":
124
            case "alert":
125
            case "emergency":
126
                return "fatal";
×
127
            default:
128
                return "info";
×
129
        }
130
    }
131
}
132

133
class McpLogger extends LoggerBase {
134
    constructor(private server: McpServer) {
33✔
135
        super();
33✔
136
    }
137

138
    log(level: LogLevel, _: MongoLogId, context: string, message: string): void {
139
        // Only log if the server is connected
140
        if (!this.server?.isConnected()) {
445✔
141
            return;
45✔
142
        }
143

144
        void this.server.server.sendLoggingMessage({
400✔
145
            level,
146
            data: `[${context}]: ${message}`,
147
        });
148
    }
149
}
150

151
class CompositeLogger extends LoggerBase {
152
    private loggers: LoggerBase[];
153

154
    constructor(...loggers: LoggerBase[]) {
155
        super();
37✔
156

157
        if (loggers.length === 0) {
37✔
158
            // default to ConsoleLogger
159
            this.loggers = [new ConsoleLogger()];
37✔
160
            return;
37✔
161
        }
162

163
        this.loggers = [...loggers];
×
164
    }
165

166
    setLoggers(...loggers: LoggerBase[]): void {
167
        if (loggers.length === 0) {
33!
168
            throw new Error("At least one logger must be provided");
×
169
        }
170
        this.loggers = [...loggers];
33✔
171
    }
172

173
    log(level: LogLevel, id: MongoLogId, context: string, message: string): void {
174
        for (const logger of this.loggers) {
455✔
175
            logger.log(level, id, context, message);
900✔
176
        }
177
    }
178
}
179

180
const logger = new CompositeLogger();
37✔
181
export default logger;
182

183
export async function setStdioPreset(server: McpServer, logPath: string): Promise<void> {
184
    const diskLogger = await DiskLogger.fromPath(logPath);
33✔
185
    const mcpLogger = new McpLogger(server);
33✔
186

187
    logger.setLoggers(mcpLogger, diskLogger);
33✔
188
}
189

190
export function setContainerPreset(server: McpServer): void {
NEW
191
    const mcpLogger = new McpLogger(server);
×
NEW
192
    const consoleLogger = new ConsoleLogger();
×
193

NEW
194
    logger.setLoggers(mcpLogger, consoleLogger);
×
195
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc