Skip to main content
Glama
logger.ts6.99 kB
/** * Professional logging module with correlation ID support for Smartling MCP Server * Based on best practices from @wix/docs-mcp */ import winston from 'winston'; import { v4 as uuidv4 } from 'uuid'; import path from 'path'; import fs from 'fs'; /** * Ensure logs directory exists */ const logsDir = path.resolve(process.cwd(), 'logs'); if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir, { recursive: true }); } /** * Winston logger configuration with structured format */ const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json(), winston.format.metadata({ fillExcept: ['message', 'level', 'timestamp'] }) ), defaultMeta: { service: 'smartling-mcp-server' }, transports: [ // File transports for different log levels new winston.transports.File({ filename: path.join(logsDir, 'error.log'), level: 'error', maxsize: 5242880, // 5MB maxFiles: 5 }), new winston.transports.File({ filename: path.join(logsDir, 'combined.log'), maxsize: 5242880, // 5MB maxFiles: 5 }), // Console transport only for development (disabled in MCP mode) ...(process.env.NODE_ENV === 'development' && process.env.MCP_MODE !== 'true' ? [new winston.transports.Console({ stderrLevels: ['error', 'warn', 'info', 'debug'], format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) })] : [] ) ] }); /** * Log context with correlation ID and operation details */ export interface LogContext { correlationId?: string; operation?: string; toolName?: string; projectId?: string; fileUri?: string; duration?: number; requestId?: string; userId?: string; clientId?: string; error?: string; stack?: string; [key: string]: any; } /** * Enhanced logger with correlation ID support */ export class SmartlingLogger { private correlationId: string; private additionalContext: LogContext; constructor(correlationId?: string, additionalContext?: LogContext) { this.correlationId = correlationId || uuidv4(); this.additionalContext = additionalContext || {}; } private getContext(context?: LogContext): LogContext { const base = this.additionalContext || {}; const provided = context || {}; return { ...base, ...provided, correlationId: this.correlationId }; } info(message: string, context?: LogContext) { logger.info(message, this.getContext(context)); } warn(message: string, context?: LogContext) { logger.warn(message, this.getContext(context)); } error(message: string, error?: Error, context?: LogContext) { const errorContext: LogContext = { ...(context || {}), ...(error?.message && { error: error.message }), ...(error?.stack && { stack: error.stack }) }; logger.error(message, this.getContext(errorContext)); } debug(message: string, context?: LogContext) { logger.debug(message, this.getContext(context)); } /** * Log API operation start */ apiStart(operation: string, context?: LogContext) { this.info(`API operation started: ${operation}`, { ...context, operation, phase: 'start' }); } /** * Log API operation completion */ apiComplete(operation: string, duration: number, context?: LogContext) { this.info(`API operation completed: ${operation}`, { ...context, operation, duration, phase: 'complete' }); } /** * Log API operation error */ apiError(operation: string, error: Error, context?: LogContext) { this.error(`API operation failed: ${operation}`, error, { ...context, operation, phase: 'error' }); } /** * Log tool execution */ toolExecution(toolName: string, args: any, context?: LogContext) { this.info(`Tool executed: ${toolName}`, { ...context, toolName, args: JSON.stringify(args), operation: 'tool_execution' }); } /** * Log streaming event */ streamingEvent(event: string, data?: any, context?: LogContext) { this.debug(`Streaming event: ${event}`, { ...context, event, data: data ? JSON.stringify(data) : undefined, operation: 'streaming' }); } /** * Log OAuth operation */ oauthOperation(operation: string, context?: LogContext) { this.info(`OAuth operation: ${operation}`, { ...context, operation, component: 'oauth' }); } getCorrelationId(): string { return this.correlationId; } /** * Create a child logger with additional context */ child(additionalContext?: LogContext): SmartlingLogger { return new SmartlingLogger(this.correlationId, { ...this.additionalContext, ...additionalContext }); } /** * Create a tool-scoped logger */ forTool(toolName: string): SmartlingLogger { return this.child({ toolName, component: 'tool' }); } /** * Create a request-scoped logger */ forRequest(requestId: string): SmartlingLogger { return this.child({ requestId, component: 'request' }); } } /** * Create a new Smartling logger instance */ export function createLogger(correlationId?: string, context?: LogContext): SmartlingLogger { return new SmartlingLogger(correlationId, context); } /** * Generate a new correlation ID */ export function generateCorrelationId(): string { return uuidv4(); } /** * Generate a request ID */ export function generateRequestId(): string { return `req_${uuidv4().substring(0, 8)}`; } /** * Default logger instance for the server */ export const defaultLogger = new SmartlingLogger(undefined, { component: 'server' }); /** * Logger for startup/shutdown operations */ export const systemLogger = new SmartlingLogger(undefined, { component: 'system' }); /** * Performance timing utility */ export class PerformanceTimer { private startTime: number; private logger: SmartlingLogger; private operation: string; constructor(logger: SmartlingLogger, operation: string) { this.logger = logger; this.operation = operation; this.startTime = Date.now(); this.logger.apiStart(operation); } complete(context?: LogContext) { const duration = Date.now() - this.startTime; this.logger.apiComplete(this.operation, duration, context); return duration; } error(error: Error, context?: LogContext) { const duration = Date.now() - this.startTime; this.logger.apiError(this.operation, error, { ...context, duration }); return duration; } } /** * Create a performance timer for an operation */ export function createTimer(logger: SmartlingLogger, operation: string): PerformanceTimer { return new PerformanceTimer(logger, operation); } export default SmartlingLogger;

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/Jacobolevy/smartling-mcp-server'

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