gcp-logging-search-comprehensive
Search Google Cloud logs across all payload types, fields, and metadata to find specific entries by term, time range, severity, or resource.
Instructions
Search across all log fields including textPayload, jsonPayload, protoPayload, labels, HTTP requests, and metadata. Provides maximum context.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| searchTerm | Yes | Term to search for across all payload types and fields | |
| timeRange | No | Time range to search (e.g., "1h", "24h", "7d") | 1h |
| severity | No | Minimum severity level to filter by | |
| resource | No | Resource type to filter by (e.g., "cloud_function", "gke_container") | |
| limit | No | Maximum number of log entries to return |
Implementation Reference
- src/services/logging/tools.ts:243-379 (handler)The main execution handler for 'gcp-logging-search-comprehensive'. Builds a sophisticated filter searching across textPayload, numerous jsonPayload fields (message, error, stack, HTTP details, etc.), protoPayload, labels, httpRequest fields, sourceLocation, and operation fields. Queries logs within time range, optional severity/resource filters, sorts by timestamp desc, and formats results.async ({ searchTerm, timeRange, severity, resource, limit }) => { try { const projectId = await getProjectId(); const logging = getLoggingClient(); const endTime = new Date(); const startTime = parseRelativeTime(timeRange); // Build comprehensive search filter that searches across all payload types const filterParts = [ `timestamp >= "${startTime.toISOString()}"`, `timestamp <= "${endTime.toISOString()}"`, ]; // Add search term across multiple payload types and fields const searchParts = [ `textPayload:("${searchTerm}")`, `jsonPayload.message:("${searchTerm}")`, `jsonPayload.msg:("${searchTerm}")`, `jsonPayload.error:("${searchTerm}")`, `jsonPayload.exception:("${searchTerm}")`, `jsonPayload.stack:("${searchTerm}")`, `jsonPayload.stackTrace:("${searchTerm}")`, `jsonPayload.description:("${searchTerm}")`, `jsonPayload.details:("${searchTerm}")`, `jsonPayload.reason:("${searchTerm}")`, `jsonPayload.code:("${searchTerm}")`, `jsonPayload.status:("${searchTerm}")`, `jsonPayload.method:("${searchTerm}")`, `jsonPayload.url:("${searchTerm}")`, `jsonPayload.path:("${searchTerm}")`, `jsonPayload.endpoint:("${searchTerm}")`, `jsonPayload.service:("${searchTerm}")`, `jsonPayload.operation:("${searchTerm}")`, `jsonPayload.function:("${searchTerm}")`, `jsonPayload.name:("${searchTerm}")`, `jsonPayload.type:("${searchTerm}")`, `jsonPayload.level:("${searchTerm}")`, `jsonPayload.category:("${searchTerm}")`, `jsonPayload.component:("${searchTerm}")`, `jsonPayload.module:("${searchTerm}")`, `jsonPayload.class:("${searchTerm}")`, `jsonPayload.thread:("${searchTerm}")`, `jsonPayload.user:("${searchTerm}")`, `jsonPayload.userId:("${searchTerm}")`, `jsonPayload.sessionId:("${searchTerm}")`, `jsonPayload.requestId:("${searchTerm}")`, `jsonPayload.traceId:("${searchTerm}")`, `jsonPayload.spanId:("${searchTerm}")`, `jsonPayload.host:("${searchTerm}")`, `jsonPayload.hostname:("${searchTerm}")`, `jsonPayload.ip:("${searchTerm}")`, `jsonPayload.port:("${searchTerm}")`, `protoPayload.methodName:("${searchTerm}")`, `protoPayload.serviceName:("${searchTerm}")`, `protoPayload.resourceName:("${searchTerm}")`, `labels.service:("${searchTerm}")`, `labels.version:("${searchTerm}")`, `labels.environment:("${searchTerm}")`, `labels.region:("${searchTerm}")`, `labels.zone:("${searchTerm}")`, `httpRequest.requestUrl:("${searchTerm}")`, `httpRequest.userAgent:("${searchTerm}")`, `httpRequest.remoteIp:("${searchTerm}")`, `httpRequest.referer:("${searchTerm}")`, `sourceLocation.file:("${searchTerm}")`, `sourceLocation.function:("${searchTerm}")`, `operation.id:("${searchTerm}")`, `operation.producer:("${searchTerm}")`, ]; filterParts.push(`(${searchParts.join(" OR ")})`); // Add severity filter if specified if (severity) { filterParts.push(`severity >= ${severity}`); } // Add resource filter if specified if (resource) { filterParts.push(`resource.type = "${resource}"`); } const filter = filterParts.join(" AND "); const [entries] = await logging.getEntries({ pageSize: limit, filter, orderBy: "timestamp desc", }); if (!entries || entries.length === 0) { return { content: [ { type: "text", text: `# Comprehensive Log Search Results\n\nProject: ${projectId}\nSearch Term: "${searchTerm}"\nTime Range: ${startTime.toISOString()} to ${endTime.toISOString()}\nSeverity: ${severity || "All levels"}\nResource: ${resource || "All resources"}\n\n**No matching log entries found.**\n\nThe search looked across:\n- Text payloads\n- JSON payload fields (message, error, exception, etc.)\n- Proto payload fields\n- Labels\n- HTTP request details\n- Source location\n- Operation details`, }, ], }; } const formattedLogs = entries .map((entry) => { try { return formatLogEntry(entry as unknown as LogEntry); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : "Unknown error"; return `## Error Formatting Log Entry\n\nAn error occurred while formatting a log entry: ${errorMessage}`; } }) .join("\n\n---\n\n"); return { content: [ { type: "text", text: `# Comprehensive Log Search Results\n\nProject: ${projectId}\nSearch Term: "${searchTerm}"\nTime Range: ${startTime.toISOString()} to ${endTime.toISOString()}\nSeverity: ${severity || "All levels"}\nResource: ${resource || "All resources"}\nEntries Found: ${entries.length}\n\n**Search Coverage:**\nThis search looked across all payload types and fields including:\n- Text payloads\n- JSON payload fields (message, error, exception, stack traces, HTTP details, etc.)\n- Proto payload fields\n- Labels and metadata\n- HTTP request details\n- Source location information\n- Operation details\n\n---\n\n${formattedLogs}`, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; return { content: [ { type: "text", text: `# Error in Comprehensive Log Search\n\nAn error occurred while searching logs: ${errorMessage}\n\nPlease check your search parameters and try again.`, }, ], isError: true, }; } },
- The tool schema definition including title, description, and inputSchema with Zod validation for searchTerm, timeRange, severity enum, resource, and limit.{ title: "Comprehensive Log Search", description: "Search across all log fields including textPayload, jsonPayload, protoPayload, labels, HTTP requests, and metadata. Provides maximum context.", inputSchema: { searchTerm: z .string() .describe("Term to search for across all payload types and fields"), timeRange: z .string() .default("1h") .describe('Time range to search (e.g., "1h", "24h", "7d")'), severity: z .enum([ "DEFAULT", "DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY", ]) .optional() .describe("Minimum severity level to filter by"), resource: z .string() .optional() .describe( 'Resource type to filter by (e.g., "cloud_function", "gke_container")', ), limit: z .number() .min(1) .max(500) .default(50) .describe("Maximum number of log entries to return"), }, },
- src/services/logging/tools.ts:201-380 (registration)The server.registerTool call that registers the 'gcp-logging-search-comprehensive' tool with the MCP server.server.registerTool( "gcp-logging-search-comprehensive", { title: "Comprehensive Log Search", description: "Search across all log fields including textPayload, jsonPayload, protoPayload, labels, HTTP requests, and metadata. Provides maximum context.", inputSchema: { searchTerm: z .string() .describe("Term to search for across all payload types and fields"), timeRange: z .string() .default("1h") .describe('Time range to search (e.g., "1h", "24h", "7d")'), severity: z .enum([ "DEFAULT", "DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY", ]) .optional() .describe("Minimum severity level to filter by"), resource: z .string() .optional() .describe( 'Resource type to filter by (e.g., "cloud_function", "gke_container")', ), limit: z .number() .min(1) .max(500) .default(50) .describe("Maximum number of log entries to return"), }, }, async ({ searchTerm, timeRange, severity, resource, limit }) => { try { const projectId = await getProjectId(); const logging = getLoggingClient(); const endTime = new Date(); const startTime = parseRelativeTime(timeRange); // Build comprehensive search filter that searches across all payload types const filterParts = [ `timestamp >= "${startTime.toISOString()}"`, `timestamp <= "${endTime.toISOString()}"`, ]; // Add search term across multiple payload types and fields const searchParts = [ `textPayload:("${searchTerm}")`, `jsonPayload.message:("${searchTerm}")`, `jsonPayload.msg:("${searchTerm}")`, `jsonPayload.error:("${searchTerm}")`, `jsonPayload.exception:("${searchTerm}")`, `jsonPayload.stack:("${searchTerm}")`, `jsonPayload.stackTrace:("${searchTerm}")`, `jsonPayload.description:("${searchTerm}")`, `jsonPayload.details:("${searchTerm}")`, `jsonPayload.reason:("${searchTerm}")`, `jsonPayload.code:("${searchTerm}")`, `jsonPayload.status:("${searchTerm}")`, `jsonPayload.method:("${searchTerm}")`, `jsonPayload.url:("${searchTerm}")`, `jsonPayload.path:("${searchTerm}")`, `jsonPayload.endpoint:("${searchTerm}")`, `jsonPayload.service:("${searchTerm}")`, `jsonPayload.operation:("${searchTerm}")`, `jsonPayload.function:("${searchTerm}")`, `jsonPayload.name:("${searchTerm}")`, `jsonPayload.type:("${searchTerm}")`, `jsonPayload.level:("${searchTerm}")`, `jsonPayload.category:("${searchTerm}")`, `jsonPayload.component:("${searchTerm}")`, `jsonPayload.module:("${searchTerm}")`, `jsonPayload.class:("${searchTerm}")`, `jsonPayload.thread:("${searchTerm}")`, `jsonPayload.user:("${searchTerm}")`, `jsonPayload.userId:("${searchTerm}")`, `jsonPayload.sessionId:("${searchTerm}")`, `jsonPayload.requestId:("${searchTerm}")`, `jsonPayload.traceId:("${searchTerm}")`, `jsonPayload.spanId:("${searchTerm}")`, `jsonPayload.host:("${searchTerm}")`, `jsonPayload.hostname:("${searchTerm}")`, `jsonPayload.ip:("${searchTerm}")`, `jsonPayload.port:("${searchTerm}")`, `protoPayload.methodName:("${searchTerm}")`, `protoPayload.serviceName:("${searchTerm}")`, `protoPayload.resourceName:("${searchTerm}")`, `labels.service:("${searchTerm}")`, `labels.version:("${searchTerm}")`, `labels.environment:("${searchTerm}")`, `labels.region:("${searchTerm}")`, `labels.zone:("${searchTerm}")`, `httpRequest.requestUrl:("${searchTerm}")`, `httpRequest.userAgent:("${searchTerm}")`, `httpRequest.remoteIp:("${searchTerm}")`, `httpRequest.referer:("${searchTerm}")`, `sourceLocation.file:("${searchTerm}")`, `sourceLocation.function:("${searchTerm}")`, `operation.id:("${searchTerm}")`, `operation.producer:("${searchTerm}")`, ]; filterParts.push(`(${searchParts.join(" OR ")})`); // Add severity filter if specified if (severity) { filterParts.push(`severity >= ${severity}`); } // Add resource filter if specified if (resource) { filterParts.push(`resource.type = "${resource}"`); } const filter = filterParts.join(" AND "); const [entries] = await logging.getEntries({ pageSize: limit, filter, orderBy: "timestamp desc", }); if (!entries || entries.length === 0) { return { content: [ { type: "text", text: `# Comprehensive Log Search Results\n\nProject: ${projectId}\nSearch Term: "${searchTerm}"\nTime Range: ${startTime.toISOString()} to ${endTime.toISOString()}\nSeverity: ${severity || "All levels"}\nResource: ${resource || "All resources"}\n\n**No matching log entries found.**\n\nThe search looked across:\n- Text payloads\n- JSON payload fields (message, error, exception, etc.)\n- Proto payload fields\n- Labels\n- HTTP request details\n- Source location\n- Operation details`, }, ], }; } const formattedLogs = entries .map((entry) => { try { return formatLogEntry(entry as unknown as LogEntry); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : "Unknown error"; return `## Error Formatting Log Entry\n\nAn error occurred while formatting a log entry: ${errorMessage}`; } }) .join("\n\n---\n\n"); return { content: [ { type: "text", text: `# Comprehensive Log Search Results\n\nProject: ${projectId}\nSearch Term: "${searchTerm}"\nTime Range: ${startTime.toISOString()} to ${endTime.toISOString()}\nSeverity: ${severity || "All levels"}\nResource: ${resource || "All resources"}\nEntries Found: ${entries.length}\n\n**Search Coverage:**\nThis search looked across all payload types and fields including:\n- Text payloads\n- JSON payload fields (message, error, exception, stack traces, HTTP details, etc.)\n- Proto payload fields\n- Labels and metadata\n- HTTP request details\n- Source location information\n- Operation details\n\n---\n\n${formattedLogs}`, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; return { content: [ { type: "text", text: `# Error in Comprehensive Log Search\n\nAn error occurred while searching logs: ${errorMessage}\n\nPlease check your search parameters and try again.`, }, ], isError: true, }; } }, );
- src/services/logging/types.ts:73-260 (helper)formatLogEntry helper function used to format each log entry into a comprehensive markdown display including timestamp, severity, resource, metadata, trace info, HTTP details, labels, and payloads.export function formatLogEntry(entry: LogEntry): string { // Safely format the timestamp let timestamp: string; try { if (!entry.timestamp) { timestamp = "No timestamp"; } else { const date = new Date(entry.timestamp); timestamp = !isNaN(date.getTime()) ? date.toISOString() : String(entry.timestamp); } } catch { timestamp = String(entry.timestamp || "Invalid timestamp"); } const severity = entry.severity || "DEFAULT"; const resourceType = entry.resource?.type || "unknown"; const resourceLabels = entry.resource?.labels ? Object.entries(entry.resource.labels) .map(([k, v]) => `${k}=${v}`) .join(", ") : ""; const resource = resourceLabels ? `${resourceType}(${resourceLabels})` : resourceType; // Start building the comprehensive log entry display let result = `## ${timestamp} | ${severity} | ${resource}\n\n`; // Basic metadata if (entry.logName) result += `**Log Name:** ${entry.logName}\n`; if (entry.insertId) result += `**Insert ID:** ${entry.insertId}\n`; if (entry.receiveTimestamp) { try { const receiveTime = new Date(entry.receiveTimestamp).toISOString(); result += `**Receive Time:** ${receiveTime}\n`; } catch { result += `**Receive Time:** ${entry.receiveTimestamp}\n`; } } // Trace context information if (entry.trace) result += `**Trace:** ${entry.trace}\n`; if (entry.spanId) result += `**Span ID:** ${entry.spanId}\n`; if (entry.traceSampled !== undefined) result += `**Trace Sampled:** ${entry.traceSampled}\n`; // Source location if available if (entry.sourceLocation) { result += `**Source Location:**\n`; if (entry.sourceLocation.file) result += ` - File: ${entry.sourceLocation.file}\n`; if (entry.sourceLocation.line) result += ` - Line: ${entry.sourceLocation.line}\n`; if (entry.sourceLocation.function) result += ` - Function: ${entry.sourceLocation.function}\n`; } // HTTP request details if available if (entry.httpRequest) { result += `**HTTP Request:**\n`; if (entry.httpRequest.requestMethod) result += ` - Method: ${entry.httpRequest.requestMethod}\n`; if (entry.httpRequest.requestUrl) result += ` - URL: ${entry.httpRequest.requestUrl}\n`; if (entry.httpRequest.status) result += ` - Status: ${entry.httpRequest.status}\n`; if (entry.httpRequest.userAgent) result += ` - User Agent: ${entry.httpRequest.userAgent}\n`; if (entry.httpRequest.remoteIp) result += ` - Remote IP: ${entry.httpRequest.remoteIp}\n`; if (entry.httpRequest.latency) result += ` - Latency: ${entry.httpRequest.latency}\n`; if (entry.httpRequest.requestSize) result += ` - Request Size: ${entry.httpRequest.requestSize}\n`; if (entry.httpRequest.responseSize) result += ` - Response Size: ${entry.httpRequest.responseSize}\n`; if (entry.httpRequest.referer) result += ` - Referer: ${entry.httpRequest.referer}\n`; if (entry.httpRequest.protocol) result += ` - Protocol: ${entry.httpRequest.protocol}\n`; if (entry.httpRequest.cacheHit !== undefined) result += ` - Cache Hit: ${entry.httpRequest.cacheHit}\n`; if (entry.httpRequest.cacheLookup !== undefined) result += ` - Cache Lookup: ${entry.httpRequest.cacheLookup}\n`; if (entry.httpRequest.cacheValidatedWithOriginServer !== undefined) { result += ` - Cache Validated: ${entry.httpRequest.cacheValidatedWithOriginServer}\n`; } if (entry.httpRequest.cacheFillBytes) result += ` - Cache Fill Bytes: ${entry.httpRequest.cacheFillBytes}\n`; } // Operation details if available if (entry.operation) { result += `**Operation:**\n`; if (entry.operation.id) result += ` - ID: ${entry.operation.id}\n`; if (entry.operation.producer) result += ` - Producer: ${entry.operation.producer}\n`; if (entry.operation.first !== undefined) result += ` - First: ${entry.operation.first}\n`; if (entry.operation.last !== undefined) result += ` - Last: ${entry.operation.last}\n`; } // Labels if they exist if (entry.labels && Object.keys(entry.labels).length > 0) { try { result += `**Labels:**\n`; Object.entries(entry.labels).forEach(([key, value]) => { result += ` - ${key}: ${value}\n`; }); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; result += `**Labels:** [Error formatting labels: ${errorMessage}]\n`; } } // Add any additional fields that might be present const knownFields = new Set([ "timestamp", "severity", "resource", "logName", "textPayload", "jsonPayload", "protoPayload", "labels", "insertId", "trace", "spanId", "traceSampled", "sourceLocation", "httpRequest", "operation", "receiveTimestamp", ]); const additionalFields: Record<string, unknown> = {}; Object.entries(entry).forEach(([key, value]) => { if (!knownFields.has(key) && value !== undefined && value !== null) { additionalFields[key] = value; } }); if (Object.keys(additionalFields).length > 0) { result += `**Additional Fields:**\n`; try { result += `\`\`\`json\n${JSON.stringify(additionalFields, null, 2)}\n\`\`\`\n`; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; result += `[Error formatting additional fields: ${errorMessage}]\n`; } } // Format the main payload result += `\n**Payload:**\n`; try { if (entry.textPayload !== undefined && entry.textPayload !== null) { result += `\`\`\`\n${String(entry.textPayload)}\n\`\`\``; } else if (entry.jsonPayload) { result += `\`\`\`json\n${JSON.stringify(entry.jsonPayload, null, 2)}\n\`\`\``; } else if (entry.protoPayload) { result += `\`\`\`json\n${JSON.stringify(entry.protoPayload, null, 2)}\n\`\`\``; } else { // Check for any other payload-like fields const data = entry.data || entry.message || entry.msg; if (data) { if (typeof data === "string") { result += `\`\`\`\n${data}\n\`\`\``; } else { result += `\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\``; } } else { result += `\`\`\`\n[No payload available]\n\`\`\``; } } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; result += `\`\`\`\n[Error formatting payload: ${errorMessage}]\n\`\`\``; } return result; }
- src/services/logging/types.ts:61-65 (helper)getLoggingClient helper that initializes and returns the Google Cloud Logging client instance.export function getLoggingClient(): Logging { return new Logging({ projectId: process.env.GOOGLE_CLOUD_PROJECT, }); }