gcp-logging-search-comprehensive
Search across all Google Cloud Platform log fields including payloads, labels, and metadata to quickly find relevant log entries and troubleshoot issues.
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 |
|---|---|---|---|
| limit | No | Maximum number of log entries to return | |
| resource | No | Resource type to filter by (e.g., "cloud_function", "gke_container") | |
| searchTerm | Yes | Term to search for across all payload types and fields | |
| severity | No | Minimum severity level to filter by | |
| timeRange | No | Time range to search (e.g., "1h", "24h", "7d") | 1h |
Implementation Reference
- src/services/logging/tools.ts:243-379 (handler)The main execution handler for the 'gcp-logging-search-comprehensive' tool. Constructs an advanced filter searching the searchTerm across dozens of log fields (textPayload, jsonPayload.*, protoPayload.*, labels.*, httpRequest.*, etc.), applies time/severity/resource filters, queries GCP Logging API, formats entries, and returns detailed markdown 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, }; } },
- Input schema using Zod for the tool parameters: searchTerm (required), timeRange (default 1h), optional severity enum, resource string, limit (1-500, default 50).{ 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)Registration of the 'gcp-logging-search-comprehensive' tool via server.registerTool within registerLoggingTools function.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)Key helper function used by the handler to format each log entry into detailed markdown covering all metadata, payloads, and structured fields.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:9-54 (schema)TypeScript interface defining the structure of GCP LogEntry used in formatting and type safety.export interface LogEntry { timestamp: string; severity: string; resource: { type: string; labels: Record<string, string>; }; logName: string; textPayload?: string; jsonPayload?: Record<string, unknown>; protoPayload?: Record<string, unknown>; labels?: Record<string, string>; insertId?: string; trace?: string; spanId?: string; traceSampled?: boolean; sourceLocation?: { file?: string; line?: number; function?: string; }; httpRequest?: { requestMethod?: string; requestUrl?: string; requestSize?: string; status?: number; responseSize?: string; userAgent?: string; remoteIp?: string; referer?: string; latency?: string; cacheLookup?: boolean; cacheHit?: boolean; cacheValidatedWithOriginServer?: boolean; cacheFillBytes?: string; protocol?: string; }; operation?: { id?: string; producer?: string; first?: boolean; last?: boolean; }; receiveTimestamp?: string; [key: string]: unknown; }