discover_logs
Find available log containers across Optimizely DXP environments to identify which logs exist before downloading or analyzing them. Returns container names, log types, and environment availability.
Instructions
š Discover available log containers across all environments. REAL-TIME: <2s. Returns container names, log types available (http, application), and environment availability. Use this before download_logs() or analyze_logs_streaming() to understand what logs exist. Useful for multi-environment projects to find which environments have logging enabled. Optional: project. Returns container inventory.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectName | No | ||
| projectId | No | ||
| apiKey | No | ||
| apiSecret | No |
Implementation Reference
- lib/tools/log-discovery-tools.ts:118-254 (handler)Main handler function for the 'discover_logs' tool. Discovers and classifies log containers across all DXP environments (Production, Preproduction, Integration). Uses StorageTools to list containers, matches against log patterns, generates diagnostic report with recommendations.static async discoverLogContainers(args: DiscoveryArgs): Promise<any> { try { OutputLogger.info('š Discovering log containers across all environments...\n'); // Resolve project configuration const resolved = ProjectTools.resolveCredentials(args); if (!resolved.success || !resolved.credentials) { return ResponseBuilder.invalidParams('Missing required project configuration'); } const projectConfig = resolved.credentials; const projectName = resolved.project ? resolved.project.name : 'Unknown'; OutputLogger.info(`š Project: ${projectName}`); OutputLogger.info(`š Using API key: ${projectConfig.apiKey?.substring(0, 8)}...`); // Test each environment const environments = ['Production', 'Preproduction', 'Integration']; const discoveryResults: DiscoveryResults = {}; for (const env of environments) { OutputLogger.info(`\nš Checking ${env} environment...`); try { const envArgs = { ...projectConfig, environment: env }; if (process.env.DEBUG === 'true') { console.error(`[DISCOVER_LOGS] Calling handleListStorageContainers for ${env}`); console.error(`[DISCOVER_LOGS] Args:`, { hasApiKey: !!envArgs.apiKey, hasApiSecret: !!envArgs.apiSecret, hasProjectId: !!envArgs.projectId, environment: envArgs.environment }); } // Get all containers for this environment const containersResult = await StorageTools.handleListStorageContainers(envArgs as any); if (process.env.DEBUG === 'true') { console.error(`[DISCOVER_LOGS] Result type:`, typeof containersResult); console.error(`[DISCOVER_LOGS] Result structure:`, Object.keys(containersResult || {})); } const containers = this.extractContainerList(containersResult); if (process.env.DEBUG === 'true') { console.error(`[DISCOVER_LOGS] Extracted ${containers.length} containers from result`); } if (!containers || containers.length === 0) { OutputLogger.info(` ā ļø No containers accessible in ${env}`); discoveryResults[env] = { accessible: false, logContainers: [], otherContainers: [] }; continue; } // Identify log containers const logContainers: LogContainerInfo[] = []; const otherContainers: string[] = []; for (const container of containers) { const match = this.identifyLogContainer(container); if (match) { logContainers.push({ name: container, type: match.type, description: match.description }); } else { otherContainers.push(container); } } discoveryResults[env] = { accessible: true, totalContainers: containers.length, logContainers: logContainers, otherContainers: otherContainers }; // Display results for this environment OutputLogger.info(` ā Found ${containers.length} total containers`); if (logContainers.length > 0) { OutputLogger.info(` š Log containers (${logContainers.length}):`); for (const log of logContainers) { OutputLogger.info(` ⢠${log.name} (${log.description})`); } } else { OutputLogger.info(` ā ļø No log containers found!`); } if (otherContainers.length > 0) { OutputLogger.info(` š¦ ALL other containers (${otherContainers.length}):`); for (const container of otherContainers) { OutputLogger.info(` ⢠${container}`); } } } catch (error: any) { if (process.env.DEBUG === 'true') { console.error(`[DISCOVER_LOGS] Full error for ${env}:`, error); console.error(`[DISCOVER_LOGS] Error stack:`, error.stack); } OutputLogger.error(` ā Error accessing ${env}: ${error.message}`); // Provide more context in the error let errorDetail = error.message; if (error.response) { errorDetail += ` (HTTP ${error.response.statusCode || error.response.status})`; } if (error.code) { errorDetail += ` [${error.code}]`; } discoveryResults[env] = { accessible: false, error: errorDetail }; } } // Generate diagnostic report return this.generateDiagnosticReport(discoveryResults, projectName); } catch (error: any) { return ResponseBuilder.error(`Log discovery failed: ${error.message}`); } }
- LOG_CONTAINER_PATTERNS: Comprehensive regex patterns used to identify and classify different types of log containers across DXP environments.static LOG_CONTAINER_PATTERNS: LogContainerPattern[] = [ // Standard DXP containers { pattern: /^appservicelogs$/, type: 'application', description: 'Application logs' }, { pattern: /^webservicelogs$/, type: 'web', description: 'Web server logs' }, { pattern: /^cloudflarelogpush$/, type: 'cloudflare', description: 'Cloudflare logs (beta)' }, // App Service Insights containers { pattern: /^insights-logs-appserviceconsolelogs$/, type: 'application', description: 'App Service console logs' }, { pattern: /^insights-logs-appservicehttplogs$/, type: 'web', description: 'App Service HTTP logs' }, { pattern: /^insights-logs-/, type: 'insights', description: 'Application Insights logs' }, // Alternative Azure containers (commonly found in Production) { pattern: /^azure-application-logs$/, type: 'application', description: 'Azure application logs' }, { pattern: /^azure-web-logs$/, type: 'web', description: 'Azure web server logs' }, { pattern: /^azure-logs-/, type: 'azure', description: 'Azure logs' }, // Application Insights containers { pattern: /applicationinsights/, type: 'appinsights', description: 'Application Insights data' }, { pattern: /appinsights/, type: 'appinsights', description: 'Application Insights data' }, // Enhanced generic log patterns { pattern: /logs?$/, type: 'generic', description: 'Generic log container' }, { pattern: /log-/, type: 'generic', description: 'Generic log container' }, { pattern: /-logs?$/, type: 'generic', description: 'Log container (suffix)' }, { pattern: /diagnostic.*logs?/i, type: 'diagnostic', description: 'Diagnostic logs' }, { pattern: /inte-.*logs?/i, type: 'integration', description: 'Integration environment logs' }, { pattern: /backup.*logs?/i, type: 'backup', description: 'Backup logs' }, { pattern: /^db-backups?-/i, type: 'database', description: 'Database backup container' } ];
- extractContainerList: Parses container lists from various response formats (structured MCP, legacy text, JSON, markdown tables, bullet lists). Handles DXP-86 structured responses and falls back to text parsing.static extractContainerList(response: any): string[] { if (!response) { return []; } // DXP-86: First check for structured data (preferred method) if (typeof response === 'object' && response !== null) { const structuredResponse = response as StructuredResponse; // Check for structuredContent.data.containers (MCP protocol format) if (structuredResponse.structuredContent?.data?.containers) { if (process.env.DEBUG === 'true') { console.error('[DISCOVER_LOGS] Found containers in structuredContent.data.containers'); } return structuredResponse.structuredContent.data.containers; } // Check for direct data.containers format if (structuredResponse.data?.containers) { if (process.env.DEBUG === 'true') { console.error('[DISCOVER_LOGS] Found containers in data.containers'); } return structuredResponse.data.containers; } // Check for error response if (structuredResponse.error) { OutputLogger.info(`Error in container list response: ${structuredResponse.error}`); return []; } } // Fall back to text parsing for legacy formats let text = ''; if (typeof response === 'object' && response !== null) { const structuredResponse = response as StructuredResponse; if (structuredResponse.content && Array.isArray(structuredResponse.content) && structuredResponse.content[0]) { text = structuredResponse.content[0].text || ''; } else if (structuredResponse.result && structuredResponse.result.content && Array.isArray(structuredResponse.result.content)) { const content = structuredResponse.result.content[0]; if (content && content.text) { text = content.text; } } else { text = JSON.stringify(response); } } else if (typeof response === 'string') { text = response; } if (!text) { return []; } if (process.env.DEBUG === 'true') { console.error('[DISCOVER_LOGS] Falling back to text parsing'); } const containers: string[] = []; const lines = text.split('\n'); for (const line of lines) { if (!line.trim()) continue; // Look for numbered emoji format: "1. š¦ container-name" let match = line.match(/^\d+\.\s*š¦\s*(.+)$/); if (match) { containers.push(match[1].trim()); continue; } // Look for simple bullet format: "- container-name" or "⢠container-name" match = line.match(/^[\s\-ā¢]\s*([^\s\-ā¢].+)$/); if (match && !match[1].includes('Storage Container') && !match[1].includes('---')) { const name = match[1].trim(); if (name && !name.includes('|') && !name.includes('Environment')) { containers.push(name); } continue; } // Look for markdown table format: "| container-name |" match = line.match(/\|\s*([^|]+)\s*\|/); if (match && !match[1].includes('Storage Container') && !match[1].includes('---')) { containers.push(match[1].trim()); continue; } // Try JSON parsing if it looks like container data if (line.includes('{') || line.includes('[')) { try { const data = JSON.parse(line); if (Array.isArray(data)) { data.forEach((item: any) => { const name = item.StorageContainer || item.Name || item.ContainerName; if (name) containers.push(name); }); } else if (data.StorageContainer || data.Name || data.ContainerName) { containers.push(data.StorageContainer || data.Name || data.ContainerName); } } catch (e) { // Not JSON, continue } } } return containers.filter(Boolean); }
- lib/utils/tool-availability-matrix.ts:254-258 (registration)TOOL_MATRIX entry for 'discover_logs': Defines availability across hosting types, category, and description. Used for hosting-aware tool filtering and restriction messages.'discover_logs': { hostingTypes: ['dxp-paas', 'dxp-saas', 'self-hosted'], category: 'Storage & Downloads', description: 'Discover available log containers' },