(level: string, message: string, context?: LogContext)
| 248 | * @param context Optional context to control Sentry capture and other behavior |
| 249 | */ |
| 250 | export function log(level: string, message: string, context?: LogContext): void { |
| 251 | const timestamp = new Date().toISOString(); |
| 252 | const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`; |
| 253 | |
| 254 | const captureToSentry = isSentryEnabled() && __shouldCaptureToSentryForTests(context); |
| 255 | |
| 256 | if (captureToSentry) { |
| 257 | withSentry((s) => { |
| 258 | const sentryLevel = mapLogLevelToSentry(level); |
| 259 | const loggerMethod = s.logger?.[sentryLevel]; |
| 260 | if (typeof loggerMethod === 'function') { |
| 261 | loggerMethod(message); |
| 262 | return; |
| 263 | } |
| 264 | s.captureMessage(logMessage); |
| 265 | }); |
| 266 | } |
| 267 | |
| 268 | if (logFileStream && clientLogLevel !== 'none') { |
| 269 | try { |
| 270 | logFileStream.write(`${logMessage}\n`); |
| 271 | } catch { |
| 272 | // ignore file logging failures |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | if (!shouldLog(level)) { |
| 277 | return; |
| 278 | } |
| 279 | |
| 280 | // Uses stderr to avoid interfering with MCP protocol on stdout |
| 281 | // https://modelcontextprotocol.io/docs/tools/debugging#server-side-logging |
| 282 | if (areProcessStdioWritesSuppressed()) { |
| 283 | return; |
| 284 | } |
| 285 | |
| 286 | console.error(logMessage); |
| 287 | } |
no test coverage detected