UNPKG

aem-dev-mcp-server

Version:

AEM MCP server providing connectivity tools for Adobe Experience Manager® instances

220 lines • 11 kB
import { BaseOSGiService } from '../utils/base-osgi-service.js'; import { createAemLogsSuccessResult, createAemLogsFailureResult } from '../utils/aem-logs-result.js'; import { TIMEOUTS } from '../constants/timeouts.js'; import { AEM_LOGS_ERROR_CODES, LOG_TYPE_PATHS } from '../types/aem-logs.types.js'; import { paginateLogLines, TokenCountError, estimateTokensForLines } from '../utils/pagination.utils.js'; const DEFAULT_CONFIG = { timeout: TIMEOUTS.DEFAULT, logTimeout: (TIMEOUTS.DEFAULT * 2), actionDelayMs: 500, maxLogLines: 50000 }; export class AemLogsService extends BaseOSGiService { #config; constructor(httpClient, config = {}) { const fullConfig = { ...DEFAULT_CONFIG, ...config }; super(httpClient, fullConfig); this.#config = fullConfig; } async searchLogs(instance, regex, logType, page) { const startTime = Date.now(); try { const regexValidation = this.#validateRegexPattern(regex); if (!regexValidation.valid) { return createAemLogsFailureResult(this.#createAemLogsError(AEM_LOGS_ERROR_CODES.INVALID_REGEX_PATTERN, regexValidation.error || 'Invalid regex pattern'), Date.now() - startTime); } const logPath = LOG_TYPE_PATHS[logType]; if (!logPath) { return createAemLogsFailureResult(this.#createAemLogsError(AEM_LOGS_ERROR_CODES.UNSUPPORTED_LOG_TYPE, `Unsupported log type: ${logType}`), Date.now() - startTime); } const logsResult = await this.#fetchLogsFromAem(instance, logPath, regex); if (!logsResult.success) { return createAemLogsFailureResult(logsResult.error, Date.now() - startTime); } const rawLogs = logsResult.data || ''; this.logger.debug('Raw logs received', { instance: instance.url, logType, rawLogsLength: rawLogs.length, firstChars: rawLogs.substring(0, 100) }); let allLines = rawLogs .split('\n') .map(line => line.trim()) .filter(line => line.length > 0); try { const regexPattern = new RegExp(regex, 'i'); const filteredLines = allLines.filter(line => regexPattern.test(line)); this.logger.debug('Client-side regex filtering', { instance: instance.url, originalLines: allLines.length, filteredLines: filteredLines.length, regex }); allLines = filteredLines; } catch (error) { this.logger.warn('Invalid regex pattern, using all lines', { instance: instance.url, regex, error: error instanceof Error ? error.message : 'Unknown error' }); } this.logger.debug('Processed log lines', { instance: instance.url, logType, totalLines: allLines.length, firstLines: allLines.slice(0, 3) }); if (allLines.length > this.#config.maxLogLines) { this.logger.warn(`Processing large log file with ${allLines.length} lines (max recommended: ${this.#config.maxLogLines}). This may impact performance.`, { instance: instance.url, logType, totalLines: allLines.length }); } const paginationResult = this.#paginateLogs(allLines, page); if (!paginationResult.success) { return createAemLogsFailureResult(paginationResult.error, Date.now() - startTime); } this.logger.debug('Pagination applied', { instance: instance.url, totalLines: allLines.length, pageLines: paginationResult.data.entriesOnPage, currentPage: paginationResult.data.currentPage, totalPages: paginationResult.data.totalPages, estimatedTokensForPage: estimateTokensForLines(paginationResult.data.paginatedLines) }); this.logger.debug('Pagination result', { instance: instance.url, logType, pagination: { currentPage: paginationResult.data.currentPage, totalPages: paginationResult.data.totalPages, totalEntries: paginationResult.data.totalEntries, entriesOnPage: paginationResult.data.entriesOnPage, entriesPreview: paginationResult.data.paginatedLines.slice(0, 2) } }); const logSearchResult = { instance: instance.url, log_type: logType, entries: paginationResult.data.paginatedLines, pagination: { current_page: paginationResult.data.currentPage, total_pages: paginationResult.data.totalPages, total_entries: paginationResult.data.totalEntries, entries_on_page: paginationResult.data.entriesOnPage } }; const result = { success: true, result: logSearchResult, message: `Found ${paginationResult.data.totalEntries} log entries matching pattern` }; this.logger.debug('Created LogOperationResult', { instance: instance.url, success: result.success, message: result.message, hasResult: !!result.result, resultInstance: result.result?.instance }); return createAemLogsSuccessResult(result, Date.now() - startTime); } catch (error) { return createAemLogsFailureResult(this.#createAemLogsError(AEM_LOGS_ERROR_CODES.LOG_PARSING_ERROR, `Failed to search logs: ${error instanceof Error ? error.message : 'Unknown error'}`), Date.now() - startTime); } } async #fetchLogsFromAem(instance, logPath, regex) { const startTime = Date.now(); try { const fullUrl = `/system/console/slinglog/tailer.txt?name=${encodeURIComponent(logPath)}&grep=${encodeURIComponent(regex)}&tail=-1`; this.logger.debug('Making request to AEM log endpoint', { instance: instance.url, logPath, regex, fullUrl }); const response = await this.makeAuthenticatedRequest(instance, fullUrl, 'GET', undefined, this.#config.logTimeout, 'Log tailer service unavailable', 'Authentication required for log access'); this.logger.debug('Response from AEM log endpoint', { instance: instance.url, success: response.success, dataType: response.success ? typeof response.data : 'no data', dataLength: response.success ? (response.data?.length || 0) : 0, dataPreview: response.success ? response.data?.substring(0, 200) : 'no preview', errorCode: !response.success ? response.error?.code : undefined, errorMessage: !response.success ? response.error?.message : undefined, duration: response.duration }); if (!response.success) { let aemLogsError; if (response.error.message.includes('not found') || response.error.message.includes('404')) { aemLogsError = this.#createAemLogsError(AEM_LOGS_ERROR_CODES.LOG_FILE_NOT_FOUND, `Log file not found: ${logPath}`); } else if (response.error.message.includes('Authentication') || response.error.message.includes('403')) { aemLogsError = this.#createAemLogsError(AEM_LOGS_ERROR_CODES.LOG_ACCESS_DENIED, 'Access denied to log file'); } else { aemLogsError = this.#createAemLogsError(AEM_LOGS_ERROR_CODES.LOG_PARSING_ERROR, response.error.message); } return createAemLogsFailureResult(aemLogsError, Date.now() - startTime); } return createAemLogsSuccessResult(response.data || '', Date.now() - startTime); } catch (error) { return createAemLogsFailureResult(this.#createAemLogsError(AEM_LOGS_ERROR_CODES.LOG_PARSING_ERROR, `Failed to fetch logs: ${error instanceof Error ? error.message : 'Unknown error'}`), Date.now() - startTime); } } #paginateLogs(lines, page) { try { if (page < 1) { return createAemLogsFailureResult(this.#createAemLogsError(AEM_LOGS_ERROR_CODES.INVALID_PAGE_NUMBER, 'Page number must be at least 1'), 0); } const paginationResult = paginateLogLines(lines, page); if (page > paginationResult.totalPages && paginationResult.totalPages > 0) { return createAemLogsFailureResult(this.#createAemLogsError(AEM_LOGS_ERROR_CODES.INVALID_PAGE_NUMBER, `Requested page ${page} exceeds total pages ${paginationResult.totalPages}`), 0); } return createAemLogsSuccessResult(paginationResult, 0); } catch (error) { let errorCode = AEM_LOGS_ERROR_CODES.PAGINATION_ERROR; let message = 'Failed to paginate logs'; if (error instanceof TokenCountError) { errorCode = AEM_LOGS_ERROR_CODES.TOKEN_COUNT_ERROR; message = `Token counting failed: ${error.message}`; } else if (error instanceof Error) { message = `Pagination failed: ${error.message}`; } return createAemLogsFailureResult(this.#createAemLogsError(errorCode, message, error), 0); } } #validateRegexPattern(pattern) { try { new RegExp(pattern); if (pattern.length === 0) { return { valid: false, error: 'Regex pattern cannot be empty' }; } if (pattern.length > 1000) { return { valid: false, error: 'Regex pattern too long (max 1000 characters)' }; } return { valid: true }; } catch (error) { const errorMsg = error instanceof Error ? error.message : 'Unknown error'; return { valid: false, error: `Invalid regex pattern '${pattern}': ${errorMsg}. Examples of valid patterns: 'ERROR.*', '\\d{4}-\\d{2}-\\d{2}', 'Exception|Error'` }; } } #createAemLogsError(code, message, details, retry) { return { code, message, details, retry: retry ?? false }; } } //# sourceMappingURL=aem-logs.service.js.map