@@ -50,44 +50,96 @@ export const LogId = {
50
50
streamableHttpTransportCloseFailure : mongoLogId ( 1_006_006 ) ,
51
51
} as const ;
52
52
53
+ interface LogPayload {
54
+ id : MongoLogId ;
55
+ context : string ;
56
+ message : string ;
57
+ noRedaction ?: boolean | LoggerType | LoggerType [ ] ;
58
+ }
59
+
60
+ export type LoggerType = "console" | "disk" | "mcp" ;
61
+
53
62
export abstract class LoggerBase {
54
- abstract log ( level : LogLevel , id : MongoLogId , context : string , message : string ) : void ;
63
+ private defaultUnredactedLogger : LoggerType = "mcp" ;
55
64
56
- info ( id : MongoLogId , context : string , message : string ) : void {
57
- this . log ( "info" , id , context , message ) ;
65
+ public log ( level : LogLevel , payload : LogPayload ) : void {
66
+ // If no explicit value is supplied for unredacted loggers, default to "mcp"
67
+ const noRedaction = payload . noRedaction !== undefined ? payload . noRedaction : this . defaultUnredactedLogger ;
68
+
69
+ this . logCore ( level , {
70
+ ...payload ,
71
+ message : this . redactIfNecessary ( payload . message , noRedaction ) ,
72
+ } ) ;
58
73
}
59
74
60
- error ( id : MongoLogId , context : string , message : string ) : void {
61
- this . log ( "error" , id , context , message ) ;
75
+ protected abstract type : LoggerType ;
76
+
77
+ protected abstract logCore ( level : LogLevel , payload : LogPayload ) : void ;
78
+
79
+ private redactIfNecessary ( message : string , noRedaction : LogPayload [ "noRedaction" ] ) : string {
80
+ if ( typeof noRedaction === "boolean" && noRedaction ) {
81
+ // If the consumer has supplied noRedaction: true, we don't redact the log message
82
+ // regardless of the logger type
83
+ return message ;
84
+ }
85
+
86
+ if ( typeof noRedaction === "string" && noRedaction === this . type ) {
87
+ // If the consumer has supplied noRedaction: logger-type, we skip redacting if
88
+ // our logger type is the same as what the consumer requested
89
+ return message ;
90
+ }
91
+
92
+ if (
93
+ typeof noRedaction === "object" &&
94
+ Array . isArray ( noRedaction ) &&
95
+ this . type !== null &&
96
+ noRedaction . indexOf ( this . type ) !== - 1
97
+ ) {
98
+ // If the consumer has supplied noRedaction: array, we skip redacting if our logger
99
+ // type is included in that array
100
+ return message ;
101
+ }
102
+
103
+ return redact ( message ) ;
104
+ }
105
+
106
+ info ( payload : LogPayload ) : void {
107
+ this . log ( "info" , payload ) ;
62
108
}
63
- debug ( id : MongoLogId , context : string , message : string ) : void {
64
- this . log ( "debug" , id , context , message ) ;
109
+
110
+ error ( payload : LogPayload ) : void {
111
+ this . log ( "error" , payload ) ;
112
+ }
113
+ debug ( payload : LogPayload ) : void {
114
+ this . log ( "debug" , payload ) ;
65
115
}
66
116
67
- notice ( id : MongoLogId , context : string , message : string ) : void {
68
- this . log ( "notice" , id , context , message ) ;
117
+ notice ( payload : LogPayload ) : void {
118
+ this . log ( "notice" , payload ) ;
69
119
}
70
120
71
- warning ( id : MongoLogId , context : string , message : string ) : void {
72
- this . log ( "warning" , id , context , message ) ;
121
+ warning ( payload : LogPayload ) : void {
122
+ this . log ( "warning" , payload ) ;
73
123
}
74
124
75
- critical ( id : MongoLogId , context : string , message : string ) : void {
76
- this . log ( "critical" , id , context , message ) ;
125
+ critical ( payload : LogPayload ) : void {
126
+ this . log ( "critical" , payload ) ;
77
127
}
78
128
79
- alert ( id : MongoLogId , context : string , message : string ) : void {
80
- this . log ( "alert" , id , context , message ) ;
129
+ alert ( payload : LogPayload ) : void {
130
+ this . log ( "alert" , payload ) ;
81
131
}
82
132
83
- emergency ( id : MongoLogId , context : string , message : string ) : void {
84
- this . log ( "emergency" , id , context , message ) ;
133
+ emergency ( payload : LogPayload ) : void {
134
+ this . log ( "emergency" , payload ) ;
85
135
}
86
136
}
87
137
88
138
export class ConsoleLogger extends LoggerBase {
89
- log ( level : LogLevel , id : MongoLogId , context : string , message : string ) : void {
90
- message = redact ( message ) ;
139
+ protected type : LoggerType = "console" ;
140
+
141
+ protected logCore ( level : LogLevel , payload : LogPayload ) : void {
142
+ const { id, context, message } = payload ;
91
143
console . error ( `[${ level . toUpperCase ( ) } ] ${ id . __value } - ${ context } : ${ message } (${ process . pid } )` ) ;
92
144
}
93
145
}
@@ -97,6 +149,8 @@ export class DiskLogger extends LoggerBase {
97
149
super ( ) ;
98
150
}
99
151
152
+ protected type : LoggerType = "disk" ;
153
+
100
154
static async fromPath ( logPath : string ) : Promise < DiskLogger > {
101
155
await fs . mkdir ( logPath , { recursive : true } ) ;
102
156
@@ -116,8 +170,8 @@ export class DiskLogger extends LoggerBase {
116
170
return new DiskLogger ( logWriter ) ;
117
171
}
118
172
119
- log ( level : LogLevel , id : MongoLogId , context : string , message : string ) : void {
120
- message = redact ( message ) ;
173
+ protected logCore ( level : LogLevel , payload : LogPayload ) : void {
174
+ const { id , context , message } = payload ;
121
175
const mongoDBLevel = this . mapToMongoDBLogLevel ( level ) ;
122
176
123
177
this . logWriter [ mongoDBLevel ] ( "MONGODB-MCP" , id , context , message ) ;
@@ -149,20 +203,25 @@ export class McpLogger extends LoggerBase {
149
203
super ( ) ;
150
204
}
151
205
152
- log ( level : LogLevel , _ : MongoLogId , context : string , message : string ) : void {
206
+ type : LoggerType = "mcp" ;
207
+
208
+ protected logCore ( level : LogLevel , payload : LogPayload ) : void {
153
209
// Only log if the server is connected
154
210
if ( ! this . server ?. isConnected ( ) ) {
155
211
return ;
156
212
}
157
213
158
214
void this . server . server . sendLoggingMessage ( {
159
215
level,
160
- data : `[${ context } ]: ${ message } ` ,
216
+ data : `[${ payload . context } ]: ${ payload . message } ` ,
161
217
} ) ;
162
218
}
163
219
}
164
220
165
- class CompositeLogger extends LoggerBase {
221
+ export class CompositeLogger extends LoggerBase {
222
+ // This is not a real logger type - it should not be used anyway.
223
+ protected type : LoggerType = "composite" as unknown as LoggerType ;
224
+
166
225
private loggers : LoggerBase [ ] = [ ] ;
167
226
168
227
constructor ( ...loggers : LoggerBase [ ] ) {
@@ -178,11 +237,16 @@ class CompositeLogger extends LoggerBase {
178
237
this . loggers = [ ...loggers ] ;
179
238
}
180
239
181
- log ( level : LogLevel , id : MongoLogId , context : string , message : string ) : void {
240
+ public log ( level : LogLevel , payload : LogPayload ) : void {
241
+ // Override the public method to avoid the base logger redacting the message payload
182
242
for ( const logger of this . loggers ) {
183
- logger . log ( level , id , context , message ) ;
243
+ logger . log ( level , payload ) ;
184
244
}
185
245
}
246
+
247
+ protected logCore ( ) : void {
248
+ throw new Error ( "logCore should never be invoked on CompositeLogger" ) ;
249
+ }
186
250
}
187
251
188
252
const logger = new CompositeLogger ( new ConsoleLogger ( ) ) ;
0 commit comments