Skip to main content
Glama
logger.ts7.46 kB
/** * @fileoverview Simple logging service for the application. * This provides a centralized place for all logging functionality. * * Configuration: * - LOG_LEVEL: Sets the minimum log level (DEBUG, INFO, WARN, ERROR). Defaults to DEBUG. * - LOG_FILE: Path to the log file. If not set, no logs will be written. * * Note: Since MCP servers use stdout for protocol communication, logs are written * to a file instead of stdout/stderr to avoid interference. */ import { writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs'; import { dirname } from 'path'; /** * Log levels for the application * @enum {string} */ // This enum is part of the public API and is used by consumers, even if not all values are used in this file export enum LogLevel { DEBUG = 'DEBUG', INFO = 'INFO', WARN = 'WARN', ERROR = 'ERROR', } /** * Environment-aware logging configuration */ const LOG_LEVELS_PRIORITY: Record<LogLevel, number> = { [LogLevel.DEBUG]: 0, [LogLevel.INFO]: 1, [LogLevel.WARN]: 2, [LogLevel.ERROR]: 3, }; /** * Get the log file path from environment * @returns {string | null} The log file path or null if not configured * @private */ function getLogFilePath(): string | null { return process.env.LOG_FILE || null; } let logFileInitialized = false; /** * Initialize the log file if needed by creating the directory and file * Only initializes once per process to avoid redundant file operations * @private * @returns {void} */ function initializeLogFile(): void { const logFile = getLogFilePath(); if (logFile && !logFileInitialized) { try { // Create directory if it doesn't exist const dir = dirname(logFile); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } // Create or truncate the log file writeFileSync(logFile, ''); logFileInitialized = true; } catch { // Fail silently if we can't create the log file logFileInitialized = true; // Don't retry } } } /** * Write a log message to file * @param message The formatted log message to write * @private */ function writeToLogFile(message: string): void { const logFile = getLogFilePath(); if (logFile) { try { if (!logFileInitialized) { initializeLogFile(); } appendFileSync(logFile, `${message}\n`); } catch { // Fail silently if we can't write to the log file } } } /** * Check if a log level should be displayed based on the environment configuration * @param level The log level to check * @returns {boolean} True if the log level should be displayed * @private */ function shouldLog(level: LogLevel): boolean { const configuredLevel = (process.env.LOG_LEVEL || 'DEBUG') as LogLevel; return LOG_LEVELS_PRIORITY[level] >= LOG_LEVELS_PRIORITY[configuredLevel]; } /** * Format a log message with timestamp, level, and context information * @param level The log level of the message * @param message The log message content * @param context Optional context identifier * @returns {string} Formatted log message * @private */ function formatLogMessage(level: LogLevel, message: string, context?: string): string { const timestamp = new Date().toISOString(); const contextStr = context ? `[${context}] ` : ''; return `${timestamp} ${level} ${contextStr}${message}`; } /** * Logger service for consistent logging throughout the application */ export class Logger { private context?: string; /** * Create a new logger instance, optionally with a context * @param context Optional context name to identify the log source */ constructor(context?: string) { if (context !== undefined) { this.context = context; } } /** * Log a debug message * @param message The message to log * @param data Optional data to include in the log */ debug(message: string, data?: unknown): void { if (shouldLog(LogLevel.DEBUG) && getLogFilePath()) { const formattedMessage = formatLogMessage(LogLevel.DEBUG, message, this.context); const fullMessage = data !== undefined ? `${formattedMessage} ${JSON.stringify(data, null, 2)}` : formattedMessage; writeToLogFile(fullMessage); } } /** * Log an info message * @param message The message to log * @param data Optional data to include in the log */ info(message: string, data?: unknown): void { if (shouldLog(LogLevel.INFO) && getLogFilePath()) { const formattedMessage = formatLogMessage(LogLevel.INFO, message, this.context); const fullMessage = data !== undefined ? `${formattedMessage} ${JSON.stringify(data, null, 2)}` : formattedMessage; writeToLogFile(fullMessage); } } /** * Log a warning message * @param message The message to log * @param data Optional data to include in the log */ warn(message: string, data?: unknown): void { if (shouldLog(LogLevel.WARN) && getLogFilePath()) { const formattedMessage = formatLogMessage(LogLevel.WARN, message, this.context); const fullMessage = data !== undefined ? `${formattedMessage} ${JSON.stringify(data, null, 2)}` : formattedMessage; writeToLogFile(fullMessage); } } /** * Log an error message with improved error formatting * @param message The message to log * @param error Optional error to include in the log. The error will be formatted for better readability: * - Error objects will include name, message and stack trace * - Objects will be stringified with proper indentation * - Other values will be converted to strings */ error(message: string, error?: unknown): void { if (shouldLog(LogLevel.ERROR) && getLogFilePath()) { const formattedMessage = formatLogMessage(LogLevel.ERROR, message, this.context); // Format the error for better debugging let errorOutput = ''; if (error !== undefined) { if (error instanceof Error) { errorOutput = `${error.name}: ${error.message}${error.stack ? `\n${error.stack}` : ''}`; } else { try { errorOutput = JSON.stringify(error, null, 2); } catch { // Fallback to string representation if JSON.stringify fails errorOutput = String(error); } } } const fullMessage = errorOutput ? `${formattedMessage} ${errorOutput}` : formattedMessage; writeToLogFile(fullMessage); } } } /** * Default logger instance for the application * Pre-configured with the 'DeepSourceMCP' context for quick imports * @const {Logger} */ export const defaultLogger = new Logger('DeepSourceMCP'); /** * Helper function to create a logger with a specific context * @param context The context to use for the logger * @returns A new logger instance with the specified context */ export function createLogger(context: string): Logger { return new Logger(context); } /** * Default export for simpler imports */ export default defaultLogger; // These log levels define the hierarchy for logging operations: // - DEBUG: Detailed information for debugging // - INFO: General information about application operation // - WARN: Warning events that might lead to issues // - ERROR: Error events that might still allow the application to continue // This helps maintain consistent logging patterns throughout the application

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sapientpants/deepsource-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server