Skip to main content
Glama

Optimizely DXP MCP Server

by JaxonDigital
log-discovery-tools.js16.7 kB
/** * Log Discovery Tools * Enhanced tools for discovering and accessing log containers across all environments * Especially important for Production where container names may vary */ const PowerShellHelper = require('../powershell-helper'); const PowerShellCommandBuilder = require('../powershell-command-builder'); const ResponseBuilder = require('../response-builder'); const OutputLogger = require('../output-logger'); const ProjectTools = require('./project-tools'); const StorageTools = require('./storage-tools'); class LogDiscoveryTools { // Known log container patterns static LOG_CONTAINER_PATTERNS = [ // 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' } ]; /** * Discover all log containers across environments */ static async discoverLogContainers(args) { 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 = {}; for (const env of environments) { OutputLogger.info(`\n🌍 Checking ${env} environment...`); try { const envArgs = { ...projectConfig, environment: env }; // Get all containers for this environment const containersResult = await StorageTools.handleListStorageContainers(envArgs); const containers = this.extractContainerList(containersResult); if (!containers || containers.length === 0) { OutputLogger.info(` ⚠️ No containers accessible in ${env}`); discoveryResults[env] = { accessible: false, containers: [], logContainers: [] }; continue; } // Identify log containers const logContainers = []; const otherContainers = []; 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) { OutputLogger.error(` ❌ Error accessing ${env}: ${error.message}`); discoveryResults[env] = { accessible: false, error: error.message }; } } // Generate diagnostic report return this.generateDiagnosticReport(discoveryResults, projectName); } catch (error) { return ResponseBuilder.error(`Log discovery failed: ${error.message}`); } } /** * Identify if a container is a log container */ static identifyLogContainer(containerName) { for (const pattern of this.LOG_CONTAINER_PATTERNS) { if (pattern.pattern.test(containerName)) { return pattern; } } return null; } /** * Extract container list from response */ static extractContainerList(response) { if (!response) { return []; } let text = ''; // Handle ResponseBuilder format if (typeof response === 'object' && response !== null) { if (response.content && Array.isArray(response.content) && response.content[0]) { text = response.content[0].text || ''; } else if (response.result && response.result.content && Array.isArray(response.result.content)) { const content = response.result.content[0]; if (content && content.text) { text = content.text; } } else if (response.error) { OutputLogger.info('Error in container list response:', response.error); return []; } else { text = JSON.stringify(response); } } else if (typeof response === 'string') { text = response; } if (!text) { return []; } const containers = []; 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 => { 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); } /** * Generate diagnostic report */ static generateDiagnosticReport(results, projectName) { let message = `# 🔍 Log Container Discovery Report\n\n`; message += `**Project**: ${projectName}\n`; message += `**Timestamp**: ${new Date().toISOString()}\n\n`; // Check for critical issues const prodResult = results['Production']; const hasProductionIssue = !prodResult?.accessible || prodResult?.logContainers?.length === 0; if (hasProductionIssue) { message += `## 🚨 CRITICAL ISSUE DETECTED\n\n`; if (!prodResult?.accessible) { message += `❌ **Cannot access Production containers**\n`; message += ` Error: ${prodResult?.error || 'Access denied'}\n\n`; message += `### Recommended Actions:\n`; message += `1. Verify API key has Production access\n`; message += `2. Contact Optimizely Support to enable Production logging\n`; message += `3. Check if Production environment exists for this project\n\n`; } else if (prodResult?.logContainers?.length === 0) { message += `⚠️ **No log containers found in Production**\n`; message += ` Found ${prodResult.totalContainers} containers, but none are log containers\n\n`; message += `### Possible Causes:\n`; message += `1. **Logging not enabled**: Contact Optimizely Support to enable Production logging\n`; message += `2. **Non-standard names**: Containers may have custom names\n`; message += `3. **Permission issue**: API key may lack log container access\n\n`; if (prodResult.otherContainers?.length > 0) { message += `### ALL containers found in Production (${prodResult.otherContainers.length} total):\n`; for (const container of prodResult.otherContainers) { message += ` • \`${container}\`\n`; } message += `\n`; message += `💡 **Tip**: Any of these might contain logs with non-standard names.\n`; message += `Try: \`download_logs environment: "Production" containerName: "[container-name]"\`\n\n`; } } } // Environment summary message += `## 📊 Environment Summary\n\n`; for (const [env, result] of Object.entries(results)) { const icon = result.accessible ? '✅' : '❌'; const logCount = result.logContainers?.length || 0; message += `### ${icon} ${env}\n`; if (!result.accessible) { message += ` Status: **Not accessible**\n`; if (result.error) { message += ` Error: ${result.error}\n`; } } else { message += ` Status: **Accessible**\n`; message += ` Total containers: ${result.totalContainers}\n`; message += ` Log containers: ${logCount}\n`; if (logCount > 0) { message += ` Available logs:\n`; for (const log of result.logContainers) { message += ` • \`${log.name}\` - ${log.description}\n`; } } } message += `\n`; } // Recommendations message += `## 💡 Recommendations\n\n`; if (hasProductionIssue) { message += `### For Production Log Access:\n`; message += `1. **Contact Optimizely Support** with this information:\n`; message += ` - Request to enable Production logging\n`; message += ` - Project ID: Include your project ID\n`; message += ` - Mention you need access to Application Insights logs\n`; message += ` - Request both console and HTTP logs\n\n`; message += `2. **Alternative Methods** while waiting:\n`; message += ` - Check DXP Management Portal for downloadable logs\n`; message += ` - Use Application Insights in Azure Portal (if accessible)\n`; message += ` - Request Kudu access from Optimizely Support\n\n`; } // Working examples from other environments const workingEnvs = Object.entries(results) .filter(([env, r]) => r.accessible && r.logContainers?.length > 0) .map(([env, r]) => ({ env, containers: r.logContainers })); if (workingEnvs.length > 0) { message += `### ✅ Working Log Access:\n`; for (const { env, containers } of workingEnvs) { message += `\n**${env}** - Use these commands:\n`; for (const container of containers) { message += `\`\`\`\n`; message += `download_logs environment: "${env}" containerName: "${container.name}"\n`; message += `\`\`\`\n`; } } } message += `\n## 📞 Support Contact\n`; message += `If Production logs remain inaccessible:\n`; message += `• **Optimizely Support**: support@optimizely.com\n`; message += `• **Reference**: "Production Application Insights log access"\n`; message += `• **Include**: This diagnostic report\n`; return ResponseBuilder.success(message); } } module.exports = LogDiscoveryTools;

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/JaxonDigital/optimizely-dxp-mcp'

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