search_logs
Search and filter SolarWinds Observability logs by time, group, entity, or keywords to troubleshoot issues and analyze system events.
Instructions
Search SolarWinds Observability logs with optional filtering
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filter | No | A search query string. Use AND/OR operators to combine terms (e.g., "error AND timeout"). The search is performed across all fields including message, hostname, program, and nested JSON fields like Context.CorrelationId. Field-specific queries like "field:value" are not supported. | |
| group | No | Filter logs by a specific group name | |
| entityId | No | Filter logs by a specific entity ID | |
| startTime | No | UTC start time (ISO 8601 format) | |
| endTime | No | UTC end time (ISO 8601 format) | |
| direction | No | Sort order: backward (oldest to newest), forward (newest to oldest), or tail (oldest to newest) | backward |
| pageSize | No | Maximum messages to return per page | |
| skipToken | No | Token to skip to the next page of results |
Implementation Reference
- src/tools/index.ts:14-42 (registration)Registers the 'search_logs' MCP tool with description, input schema, and error-handling wrapper that delegates to searchLogs function.
server.tool( 'search_logs', 'Search SolarWinds Observability logs with optional filtering', searchParamsSchema, async (args) => { try { const result = await searchLogs(apiClient, args); return { content: [ { type: 'text', text: result, }, ], }; } catch (error) { const message = error instanceof Error ? error.message : String(error); return { content: [ { type: 'text', text: `Error searching logs: ${message}`, }, ], isError: true, }; } } ); - src/tools/search-logs.ts:10-89 (handler)Core implementation of the search_logs tool: processes arguments, calls API, formats output with parameters, tips, metadata, and log entries.
export async function searchLogs( apiClient: SolarWindsApiClient, args: Record<string, any> ): Promise<string> { try { // Get current date/time const now = new Date(); const oneDayAgo = new Date(now); oneDayAgo.setDate(oneDayAgo.getDate() - 1); // Validate and convert arguments to search parameters const params: SolarWindsSearchParams = { // Default limit to 50 results pageSize: args.pageSize !== undefined ? args.pageSize : 50, // Default direction to backward (oldest to newest) direction: args.direction || 'backward' }; // Only add time parameters if explicitly provided // This allows API calls to work without time constraints if (args.startTime !== undefined) { params.startTime = args.startTime; } if (args.endTime !== undefined) { params.endTime = args.endTime; } // Add optional parameters if provided if (args.filter !== undefined) params.filter = args.filter; if (args.group !== undefined) params.group = args.group; if (args.entityId !== undefined) params.entityId = args.entityId; if (args.skipToken !== undefined) params.skipToken = args.skipToken; // Perform the search const response = await apiClient.searchEvents(params); // Format the response let result = ''; // Add search parameters result += 'Search Parameters:\n'; if (params.filter) result += `Query: ${params.filter}\n`; if (params.entityId) result += `Entity ID: ${params.entityId}\n`; if (params.group) result += `Group: ${params.group}\n`; if (params.startTime) result += `Start Time: ${params.startTime}\n`; if (params.endTime) result += `End Time: ${params.endTime}\n`; if (params.direction) result += `Direction: ${params.direction}\n`; if (params.pageSize) result += `Page Size: ${params.pageSize}\n`; result += '\n'; // Add search tips result += 'Search Tips:\n'; result += '- The search is performed across all fields, including nested JSON fields in the message.\n'; result += '- Use AND/OR operators to combine terms (e.g., "error AND timeout").\n'; result += '- The search does not support field-specific queries like "field:value".\n'; result += '- To search for logs with a specific ID in a nested field (like Context.CorrelationId),\n'; result += ' simply include the ID in your search query.\n'; result += '\n'; // Add search metadata result += `Found ${response.logs.length} logs\n`; if (response.pageInfo.nextPage) result += 'More logs available. Use skipToken to get the next page.\n'; result += '\n'; // Add events if (response.logs.length === 0) { result += 'No logs found matching the search criteria.\n'; } else { result += 'Logs:\n'; for (const log of response.logs) { result += `[${log.time}] ${log.hostname} ${log.program || ''}: ${log.message}\n`; } } return result; } catch (error) { throw error; } } - src/utils/types.ts:18-27 (schema)Zod schema defining input parameters for the search_logs tool, including descriptions for MCP tool interface.
export const searchParamsSchema = { filter: z.string().optional().describe('A search query string. Use AND/OR operators to combine terms (e.g., "error AND timeout"). The search is performed across all fields including message, hostname, program, and nested JSON fields like Context.CorrelationId. Field-specific queries like "field:value" are not supported.'), group: z.string().optional().describe('Filter logs by a specific group name'), entityId: z.string().optional().describe('Filter logs by a specific entity ID'), startTime: z.string().optional().describe('UTC start time (ISO 8601 format)'), endTime: z.string().optional().describe('UTC end time (ISO 8601 format)'), direction: z.enum(['backward', 'forward', 'tail']).default('backward').describe('Sort order: backward (oldest to newest), forward (newest to oldest), or tail (oldest to newest)'), pageSize: z.number().optional().default(50).describe('Maximum messages to return per page'), skipToken: z.string().optional().describe('Token to skip to the next page of results') } as const;