/*
* Copyright (C) 2025 TomTom Navigation B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Logger utility for the MCP server
* Structured logging with Pino - JSON-RPC compatible
* Outputs to stderr for clean separation from stdout
* Supports both simple string messages and structured logging
*/
import pino from "pino";
import type { DestinationStream } from "pino";
export interface Logger {
info: (msgOrData: string | object, msg?: string) => void;
error: (msgOrData: string | object, msg?: string) => void;
warn: (msgOrData: string | object, msg?: string) => void;
debug: (msgOrData: string | object, msg?: string) => void;
}
/**
* Creates a logger instance with the specified destination
* @param destination - Optional destination stream, defaults to stderr
*/
export function makeLogger(destination?: DestinationStream): Logger {
const pinoInstance = pino(
{
level: "debug", // Log all levels
timestamp: pino.stdTimeFunctions.isoTime,
formatters: {
level: (label) => {
return { level: label };
},
},
},
destination ?? pino.destination({ dest: 2, sync: true }) // 2 = stderr
);
return {
info: (msgOrData: string | object, msg?: string): void => {
if (typeof msgOrData === "string") {
pinoInstance.info(msgOrData);
} else {
pinoInstance.info(msgOrData, msg);
}
},
error: (msgOrData: string | object, msg?: string): void => {
if (typeof msgOrData === "string") {
pinoInstance.error(msgOrData);
} else {
pinoInstance.error(msgOrData, msg);
}
},
warn: (msgOrData: string | object, msg?: string): void => {
if (typeof msgOrData === "string") {
pinoInstance.warn(msgOrData);
} else {
pinoInstance.warn(msgOrData, msg);
}
},
debug: (msgOrData: string | object, msg?: string): void => {
if (typeof msgOrData === "string") {
pinoInstance.debug(msgOrData);
} else {
pinoInstance.debug(msgOrData, msg);
}
},
};
}
// Export default logger instance that writes to stderr
export const logger = makeLogger();