Skip to main content
Glama
logger.ts4.29 kB
/** * Logging utility for the Jira MCP server. * Provides structured logging with configurable log levels. * @module utils/logger */ import type { LogLevel } from '../types/index.js'; /** * Log level priority mapping. */ const LOG_LEVELS: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3, }; /** * Sensitive keys that should be redacted from logs. */ const SENSITIVE_KEYS = [ 'apiToken', 'api_token', 'password', 'secret', 'authorization', 'token', 'key', 'credential', ]; /** * Logger class for structured logging. */ export class Logger { private readonly name: string; private level: LogLevel; constructor(name: string, level: LogLevel = 'info') { this.name = name; this.level = level; } /** * Sets the log level. */ setLevel(level: LogLevel): void { this.level = level; } /** * Checks if a log level should be output. */ private shouldLog(level: LogLevel): boolean { return LOG_LEVELS[level] >= LOG_LEVELS[this.level]; } /** * Formats a log message with timestamp and context. */ private format( level: LogLevel, message: string, context?: Record<string, unknown> ): string { const timestamp = new Date().toISOString(); const sanitizedContext = context ? this.sanitize(context) : undefined; const contextStr = sanitizedContext ? ` ${JSON.stringify(sanitizedContext)}` : ''; return `[${timestamp}] [${level.toUpperCase()}] [${this.name}] ${message}${contextStr}`; } /** * Sanitizes sensitive data from log context. */ private sanitize(obj: Record<string, unknown>): Record<string, unknown> { const result: Record<string, unknown> = {}; for (const [key, value] of Object.entries(obj)) { if (SENSITIVE_KEYS.some((k) => key.toLowerCase().includes(k))) { result[key] = '[REDACTED]'; } else if (typeof value === 'object' && value !== null) { result[key] = this.sanitize(value as Record<string, unknown>); } else { result[key] = value; } } return result; } /** * Logs a debug message. * Uses stderr to avoid interfering with MCP stdio protocol on stdout. */ debug(message: string, context?: Record<string, unknown>): void { if (this.shouldLog('debug')) { // Use stderr for all logging to avoid interfering with MCP JSON-RPC on stdout process.stderr.write(this.format('debug', message, context) + '\n'); } } /** * Logs an info message. * Uses stderr to avoid interfering with MCP stdio protocol on stdout. */ info(message: string, context?: Record<string, unknown>): void { if (this.shouldLog('info')) { // Use stderr for all logging to avoid interfering with MCP JSON-RPC on stdout process.stderr.write(this.format('info', message, context) + '\n'); } } /** * Logs a warning message. * Uses stderr to avoid interfering with MCP stdio protocol on stdout. */ warn(message: string, context?: Record<string, unknown>): void { if (this.shouldLog('warn')) { // Use stderr for all logging to avoid interfering with MCP JSON-RPC on stdout process.stderr.write(this.format('warn', message, context) + '\n'); } } /** * Logs an error message. * Uses stderr to avoid interfering with MCP stdio protocol on stdout. */ error( message: string, error?: Error, context?: Record<string, unknown> ): void { if (this.shouldLog('error')) { const errorContext = error ? { ...context, errorName: error.name, errorMessage: error.message, stack: error.stack?.split('\n').slice(0, 5).join('\n'), } : context; // Use stderr for all logging to avoid interfering with MCP JSON-RPC on stdout process.stderr.write(this.format('error', message, errorContext) + '\n'); } } /** * Creates a child logger with a prefixed name. */ child(name: string): Logger { return new Logger(`${this.name}:${name}`, this.level); } } /** * Default logger instance. */ export const logger = new Logger('jira-mcp'); /** * Creates a logger for a specific module. */ export function createLogger(name: string): Logger { return logger.child(name); }

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/icy-r/jira-mcp'

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