Skip to main content
Glama

Watchtower DAP Windows Debugging

by rlaksana
logger.ts4.9 kB
import pino from 'pino'; import { LogEntry } from '../schemas/index'; /** * Enhanced Logger with Security Redaction * * Provides structured logging with automatic redaction of sensitive data * and support for file rotation and multiple output formats. */ export class Logger { private logger: pino.Logger; private readonly redactPatterns = [ 'password', 'secret', 'token', 'api_key', 'jwt', 'authorization', 'cookie', 'session', 'private_key', ]; constructor(_name: string) { this.logger = pino({ name: _name, level: process.env['LOG_LEVEL'] || 'info', timestamp: pino.stdTimeFunctions.isoTime, formatters: { level: label => { return { level: label }; }, bindings: bindings => { return { ['pid']: bindings['pid'], ['hostname']: bindings['hostname'] }; }, }, redact: { paths: this.redactPatterns, censor: _value => '[REDACTED]', }, }); } debug(message: string, data?: any): void { if (data) { this.logger.debug(message, this.sanitizeData(data)); } else { this.logger.debug(message); } } info(message: string, data?: any): void { if (data) { this.logger.info(message, this.sanitizeData(data)); } else { this.logger.info(message); } } warn(message: string, data?: any): void { if (data) { this.logger.warn(message, this.sanitizeData(data)); } else { this.logger.warn(message); } } error(message: string, error?: Error | any): void { if (error instanceof Error) { this.logger.error(message, { error: error.message, stack: error.stack, ...this.sanitizeData({ errorName: error.name }), }); } else if (error) { this.logger.error(message, this.sanitizeData(error)); } else { this.logger.error(message); } } fatal(message: string, error?: Error | any): void { if (error instanceof Error) { this.logger.fatal(message, { error: error.message, stack: error.stack, ...this.sanitizeData({ errorName: error.name }), }); } else if (error) { this.logger.fatal(message, this.sanitizeData(error)); } else { this.logger.fatal(message); } } /** * Sanitize data for logging by removing sensitive information */ private sanitizeData(data: any): any { if (typeof data !== 'object' || data === null) { return data; } const sanitized: any = Array.isArray(data) ? [] : {}; for (const [key, value] of Object.entries(data)) { if ( typeof key === 'string' && this.redactPatterns.some(pattern => key.toLowerCase().includes(pattern)) ) { sanitized[key] = '[REDACTED]'; } else if (typeof value === 'object' && value !== null) { sanitized[key] = this.sanitizeData(value); } else { // Check for potential sensitive data in string values if (typeof value === 'string' && this.isPotentialSensitiveData(value)) { sanitized[key] = '[REDACTED]'; } else { sanitized[key] = value; } } } return sanitized; } /** * Detect potential sensitive data in string values */ private isPotentialSensitiveData(value: string): boolean { // Check for API keys (hex strings) if (/^[a-f0-9]{32,}$/i.test(value)) { return true; } // Check for JWT-like patterns if (/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/.test(value)) { return true; } // Check for email addresses (might contain user info) if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) { return true; } // Check for long numeric strings (might be IDs with user info) if (/^\d{10,}$/.test(value)) { return true; } return false; } /** * Create a child logger with additional context */ child(context: Record<string, any>): Logger { const childLogger = new Logger(''); childLogger.logger = this.logger.child(this.sanitizeData(context)); return childLogger; } /** * Log a structured log entry */ logEntry(entry: LogEntry): void { const logData = { ...this.sanitizeData(entry.data), sessionId: entry.sessionId ? this.sanitizeData(entry.sessionId) : undefined, }; switch (entry.level) { case 'debug': this.logger.debug(entry.message, logData); break; case 'info': this.logger.info(entry.message, logData); break; case 'warn': this.logger.warn(entry.message, logData); break; case 'error': this.logger.error(entry.message, logData); break; case 'fatal': this.logger.fatal(entry.message, logData); break; default: this.logger.info(entry.message, logData); } } }

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/rlaksana/mcp-watchtower'

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