Skip to main content
Glama

MCP Image Generator

by shinpr
logger.ts•6.86 kB
/** * Logger utility for structured logging with sensitive data filtering * Provides consistent logging format across the application */ import * as crypto from 'node:crypto' /** * Log entry structure for consistent formatting */ interface StructuredLogEntry { timestamp: string level: 'debug' | 'info' | 'warn' | 'error' context: string message: string metadata?: Record<string, unknown> traceId?: string sessionId?: string } /** * Logger class for structured logging with sensitive data protection */ export class Logger { private readonly sensitivePatterns = [ /GEMINI_API_KEY=([^\s]+)/gi, /api[_-]?key[^\s]*[:=]\s*([^\s]+)/gi, /password[^\s]*[:=]\s*([^\s]+)/gi, /bearer\s+([a-zA-Z0-9\-._~+/]+=*)/gi, /secret[^\s]*[:=]\s*([^\s]+)/gi, /token[^\s]*[:=]\s*([^\s]+)/gi, ] private readonly urlPatterns = [ /(https?:\/\/[^\s]+)/gi, // URLs - separate to handle differently ] private readonly filterPatterns = [ /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g, // Credit card numbers /\b\d{3}-\d{2}-\d{4}\b/g, // SSN /\b(?:\+?1[-.]?)?\(?\d{3}\)?[-.]?\d{3}[-.]?\d{4}\b/g, // Phone numbers /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/gi, // Emails ] private readonly keyBasedSensitivePatterns = [ /api[_-]?key/i, /gemini[_-]?api[_-]?key/i, /secret/i, /password/i, /token/i, /credential/i, /bearer/i, ] private currentTraceId?: string private currentSessionId?: string constructor() { // Initialize session ID once per logger instance this.currentSessionId = this.generateId() } /** * Log a debug message (only in development mode) * @param context Context or module where the log originates * @param message Log message * @param metadata Optional metadata object */ debug(context: string, message: string, metadata?: Record<string, unknown>): void { if (process.env['NODE_ENV'] === 'production') return this.writeLog('debug', context, message, metadata) } /** * Log an info message * @param context Context or module where the log originates * @param message Log message * @param metadata Optional metadata object */ info(context: string, message: string, metadata?: Record<string, unknown>): void { this.writeLog('info', context, message, metadata) } /** * Log a warning message * @param context Context or module where the log originates * @param message Log message * @param metadata Optional metadata object */ warn(context: string, message: string, metadata?: Record<string, unknown>): void { this.writeLog('warn', context, message, metadata) } /** * Log an error message * @param context Context or module where the log originates * @param message Log message * @param error Optional error object * @param metadata Optional metadata object */ error(context: string, message: string, error?: Error, metadata?: Record<string, unknown>): void { const enhancedMetadata = { ...metadata, ...(error && { errorName: error.name, errorMessage: this.sanitizeString(error.message), errorStack: process.env['NODE_ENV'] !== 'production' ? error.stack : undefined, }), } this.writeLog('error', context, message, enhancedMetadata) } /** * Core log writing method with structured format */ private writeLog( level: StructuredLogEntry['level'], context: string, message: string, metadata?: Record<string, unknown> ): void { const logEntry: StructuredLogEntry = { timestamp: new Date().toISOString(), level, context, message: this.sanitizeString(message), ...(metadata && { metadata: this.sanitizeMetadata(metadata) }), traceId: this.getCurrentTraceId(), sessionId: this.getCurrentSessionId(), } // JSON format structured log output const logOutput = JSON.stringify(logEntry) // For MCP servers, ALL logs must go to stderr // stdout is reserved for JSON-RPC messages only console.error(logOutput) } /** * Sanitize string content by redacting sensitive information * @param input String to sanitize * @returns Sanitized string */ private sanitizeString(input: string): string { let sanitized = input // Redact sensitive data patterns (API keys, passwords, etc.) for (const pattern of this.sensitivePatterns) { sanitized = sanitized.replace(pattern, (match, group1) => match.replace(group1, '[REDACTED]')) } // Additional broad filter for API key-like strings in text // Remove any reference to API key terms even in plain text sanitized = sanitized.replace(/\bapi[_-]?key\b/gi, '[REDACTED]') sanitized = sanitized.replace(/\bgemini[_-]?api[_-]?key\b/gi, '[REDACTED]') // Remove long alphanumeric strings that might be API keys or secrets sanitized = sanitized.replace(/\b[A-Za-z0-9]{20,}\b/g, '[REDACTED]') // Redact URLs with specific label for (const pattern of this.urlPatterns) { sanitized = sanitized.replace(pattern, '[URL_REDACTED]') } // Filter personal information patterns for (const pattern of this.filterPatterns) { sanitized = sanitized.replace(pattern, '[FILTERED]') } return sanitized } /** * Sanitize metadata by redacting sensitive information * @param metadata Metadata object to sanitize * @returns Sanitized metadata object */ private sanitizeMetadata(metadata: Record<string, unknown>): Record<string, unknown> { const sanitized: Record<string, unknown> = {} for (const [key, value] of Object.entries(metadata)) { if (this.isSensitiveKey(key)) { sanitized[key] = '[REDACTED]' } else if (typeof value === 'string') { sanitized[key] = this.sanitizeString(value) } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) { sanitized[key] = this.sanitizeMetadata(value as Record<string, unknown>) } else { sanitized[key] = value } } return sanitized } /** * Check if a key contains sensitive information * @param key Object key to check * @returns True if the key contains sensitive information */ private isSensitiveKey(key: string): boolean { return this.keyBasedSensitivePatterns.some((pattern) => pattern.test(key)) } /** * Generate unique ID for trace/session tracking */ private generateId(): string { return crypto.randomUUID().substring(0, 8) } /** * Get or generate current trace ID */ private getCurrentTraceId(): string { if (!this.currentTraceId) { this.currentTraceId = this.generateId() } return this.currentTraceId } /** * Get current session ID */ private getCurrentSessionId(): string { return this.currentSessionId! } }

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/shinpr/mcp-image'

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