start_sim_log_cap
Capture structured logs from a simulator by specifying its UUID and app bundle ID. Optionally include console output by relaunching the app.
Instructions
Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| bundleId | Yes | Bundle identifier of the app to capture logs for. | |
| captureConsole | No | Whether to capture console output (requires app relaunch). | |
| simulatorUuid | Yes | UUID of the simulator to capture logs from (obtained from list_simulators). |
Implementation Reference
- src/tools/log.ts:38-62 (handler)The main handler function implementing the tool logic: validates input, invokes startLogCapture helper, handles errors, and returns formatted response with session ID.async function handler(params: { simulatorUuid: string; bundleId: string; captureConsole?: boolean; }): Promise<ToolResponse> { const validationResult = validateRequiredParam('simulatorUuid', params.simulatorUuid); if (!validationResult.isValid) { return validationResult.errorResponse!; } const { sessionId, error } = await startLogCapture(params); if (error) { return { content: [createTextContent(`Error starting log capture: ${error}`)], isError: true, }; } return { content: [ createTextContent( `Log capture started successfully. Session ID: ${sessionId}.\n\n${params.captureConsole ? 'Note: Your app was relaunched to capture console output.' : 'Note: Only structured logs are being captured.'}\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID '${sessionId}' to stop capture and retrieve logs.`, ), ], }; }
- src/tools/log.ts:26-36 (schema)Zod schema defining the input parameters for the tool.const schema = { simulatorUuid: z .string() .describe('UUID of the simulator to capture logs from (obtained from list_simulators).'), bundleId: z.string().describe('Bundle identifier of the app to capture logs for.'), captureConsole: z .boolean() .optional() .default(false) .describe('Whether to capture console output (requires app relaunch).'), };
- src/tools/log.ts:64-70 (registration)Direct registration of the 'start_sim_log_cap' tool using registerTool, specifying name, description, schema, and handler.registerTool( server, 'start_sim_log_cap', 'Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs.', schema, handler, );
- src/utils/register-tools.ts:401-405 (registration)Top-level conditional registration entry invoking registerStartSimulatorLogCaptureTool as part of the toolRegistrations array.{ register: registerStartSimulatorLogCaptureTool, groups: [ToolGroup.DIAGNOSTICS, ToolGroup.IOS_SIMULATOR_WORKFLOW], envVar: 'XCODEBUILDMCP_TOOL_START_SIMULATOR_LOG_CAPTURE', },
- src/utils/log_capture.ts:29-97 (helper)Core helper function that starts log capture by spawning xcrun simctl processes for os_log stream and optional console output, piping to a temp log file, and managing active sessions via Map.export async function startLogCapture(params: { simulatorUuid: string; bundleId: string; captureConsole?: boolean; }): Promise<{ sessionId: string; logFilePath: string; processes: ChildProcess[]; error?: string }> { // Clean up old logs before starting a new session await cleanOldLogs(); const { simulatorUuid, bundleId, captureConsole = false } = params; const logSessionId = uuidv4(); const logFileName = `${LOG_FILE_PREFIX}${logSessionId}.log`; const logFilePath = path.join(os.tmpdir(), logFileName); try { await fs.promises.mkdir(os.tmpdir(), { recursive: true }); await fs.promises.writeFile(logFilePath, ''); const logStream = fs.createWriteStream(logFilePath, { flags: 'a' }); const processes: ChildProcess[] = []; logStream.write('\n--- Log capture for bundle ID: ' + bundleId + ' ---\n'); if (captureConsole) { const stdoutLogProcess = spawn('xcrun', [ 'simctl', 'launch', '--console-pty', '--terminate-running-process', simulatorUuid, bundleId, ]); stdoutLogProcess.stdout.pipe(logStream); stdoutLogProcess.stderr.pipe(logStream); processes.push(stdoutLogProcess); } const osLogProcess = spawn('xcrun', [ 'simctl', 'spawn', simulatorUuid, 'log', 'stream', '--level=debug', '--predicate', `subsystem == "${bundleId}"`, ]); osLogProcess.stdout.pipe(logStream); osLogProcess.stderr.pipe(logStream); processes.push(osLogProcess); for (const process of processes) { process.on('close', (code) => { log('info', `A log capture process for session ${logSessionId} exited with code ${code}.`); }); } activeLogSessions.set(logSessionId, { processes, logFilePath, simulatorUuid, bundleId, }); log('info', `Log capture started with session ID: ${logSessionId}`); return { sessionId: logSessionId, logFilePath, processes }; } catch (error) { const message = error instanceof Error ? error.message : String(error); log('error', `Failed to start log capture: ${message}`); return { sessionId: '', logFilePath: '', processes: [], error: message }; } }