Skip to main content
Glama

DevFlow MCP

by Takin-Profit
logger.ts6.14 kB
/** * Application Logging * - logger: Winston-based file logging for the MCP server (avoids stdio interference) * - cliLogger: Consola-based CLI output for user-facing tools (with colors/emojis) * * This module provides concrete implementations of the Logger interface. * Business logic should depend on the Logger type, not these implementations directly. */ /** biome-ignore-all lint/suspicious/noEmptyBlockStatements: noOpLogger */ import path from "node:path" import { consola } from "consola" import winston from "winston" import DailyRotateFile from "winston-daily-rotate-file" import { getLogDir } from "#config" import type { Logger } from "#types" // Get log directory from config (uses XDG paths) const LOG_DIR = getLogDir() // Log levels - can be overridden via DFM_LOG_LEVEL environment variable const LOG_LEVEL = process.env.DFM_LOG_LEVEL ?? "info" // Format for file logs (JSON for easy parsing) const fileFormat = winston.format.combine( winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston.format.errors({ stack: true }), winston.format.json() ) // Format for console logs (human-readable, only used in development/debugging) const consoleFormat = winston.format.combine( winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston.format.colorize(), winston.format.errors({ stack: true }), winston.format.printf(({ timestamp, level, message, ...meta }) => { const metaStr = Object.keys(meta).length > 0 ? `\n${JSON.stringify(meta, null, 2)}` : "" return `${timestamp} [${level}]: ${message}${metaStr}` }) ) // Daily rotate file transport for error logs const errorFileTransport = new DailyRotateFile({ filename: path.join(LOG_DIR, "error-%DATE%.log"), datePattern: "YYYY-MM-DD", level: "error", maxFiles: "30d", // Keep error logs for 30 days maxSize: "20m", format: fileFormat, handleExceptions: true, handleRejections: true, }) // Daily rotate file transport for combined logs const combinedFileTransport = new DailyRotateFile({ filename: path.join(LOG_DIR, "combined-%DATE%.log"), datePattern: "YYYY-MM-DD", maxFiles: "14d", // Keep combined logs for 14 days maxSize: "20m", format: fileFormat, }) // Create Winston logger instance const winstonLogger = winston.createLogger({ level: LOG_LEVEL, transports: [errorFileTransport, combinedFileTransport], exitOnError: false, }) // Add console transport only in development or when explicitly enabled via DFM_ENABLE_CONSOLE_LOGS // IMPORTANT: This should NOT be used in production MCP server context // as it will interfere with stdio communication const ENABLE_CONSOLE_LOGS = process.env.DFM_ENABLE_CONSOLE_LOGS === "true" if (ENABLE_CONSOLE_LOGS) { winstonLogger.add( new winston.transports.Console({ format: consoleFormat, stderrLevels: ["error", "warn", "info", "debug"], }) ) } /** * Winston-based logger implementation * This provides structured logging with file rotation for the MCP server * * Implements the Logger interface for dependency injection */ export const logger: Logger = { /** * Log an error message */ error: ( message: string, error?: Error | unknown, meta?: Record<string, unknown> ) => { if (error instanceof Error) { winstonLogger.error(message, { error: error.message, stack: error.stack, ...meta, }) } else if (error) { winstonLogger.error(message, { error, ...meta }) } else { winstonLogger.error(message, meta) } }, /** * Log a warning message */ warn: (message: string, meta?: Record<string, unknown>) => { winstonLogger.warn(message, meta) }, /** * Log an info message */ info: (message: string, meta?: Record<string, unknown>) => { winstonLogger.info(message, meta) }, /** * Log a debug message */ debug: (message: string, meta?: Record<string, unknown>) => { winstonLogger.debug(message, meta) }, /** * Log a trace message (lowest level, most detailed) */ trace: (message: string, meta?: Record<string, unknown>) => { // Winston doesn't have a trace level by default, map it to debug winstonLogger.debug(message, { level: "trace", ...meta }) }, } /** * Get the underlying Winston logger instance * Use this if you need access to advanced Winston features * (not part of the Logger interface, use sparingly) */ export const getWinstonInstance = (): winston.Logger => winstonLogger // ============================================================================ // CLI Logger (Consola) // ============================================================================ /** * CLI logger for user-facing output * Provides formatted output with emojis and colors for better UX * Use this for CLI tools, NOT for the MCP server */ export const cliLogger = { success: (message: string, ...args: unknown[]) => consola.success(message, ...args), error: (message: string, error?: Error | unknown) => { if (error instanceof Error) { consola.error(message, error) } else if (error) { consola.error(message, error) } else { consola.error(message) } }, warn: (message: string, ...args: unknown[]) => consola.warn(message, ...args), info: (message: string, ...args: unknown[]) => consola.info(message, ...args), debug: (message: string, ...args: unknown[]) => consola.debug(message, ...args), start: (message: string) => consola.start(message), box: (message: string) => consola.box(message), getInstance: () => consola, } export type CliLogger = typeof cliLogger /** * No-op logger for testing or when logging is disabled */ export const createNoOpLogger = (): Logger => ({ info: (_message: string, _meta?: Record<string, unknown>): void => {}, error: ( _message: string, _error?: Error | unknown, _meta?: Record<string, unknown> ): void => {}, warn: (_message: string, _meta?: Record<string, unknown>): void => {}, debug: (_message: string, _meta?: Record<string, unknown>): void => {}, trace: (_message: string, _meta?: Record<string, unknown>): void => {}, })

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/Takin-Profit/devflow-mcp'

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