# karagoz logs

Reads the device log once and prints the newest records as JSON.

- Android emulator Done
- Android device Done
- iOS simulator Done
- iOS device Done

## Usage

```console title="~/app"
karagoz logs [--package <package>] [--since <seconds>] [--lines <n>] [--device <id>]
```

Each record is whole: a stack trace is one record with newlines in its message, not one entry per line.

## Before you start

- On an iOS simulator: Xcode, opened once.
- On an iPhone: Xcode 15 or newer, and the iPhone paired with the Mac and connected by USB cable.

## Options

| Option                | Default                                                                                                                     | Meaning                                                                                                                                                                                                                                                                                                               |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--package <package>` | all records                                                                                                                 | Keep only this app’s records. On Android, those written under its Linux user id (uid) in the main Android user; on a simulator, see [On an iOS simulator](https://karagozmcp.com/docs/commands/logs/#on-an-ios-simulator); on an iPhone, see [On an iPhone](https://karagozmcp.com/docs/commands/logs/#on-an-iphone). |
| `--since <seconds>`   | Android: the whole log. iOS: the last minute; with `--package`, the whole log on a simulator and the last hour on an iPhone | Unix time in seconds on the device clock, up to 9 decimals and at most 4294967295. Only records stamped later are read.                                                                                                                                                                                               |
| `--lines <n>`         | `30`                                                                                                                        | How many of the newest matching records to return, 1 to 999999999.                                                                                                                                                                                                                                                    |
| `--device <id>`       | see [Device selection](https://karagozmcp.com/docs/device-selection/)                                                       |                                                                                                                                                                                                                                                                                                                       |

## Examples

### Read one app’s records

```console title="~/app"
$ karagoz logs --package com.android.shell
{"device":"emulator-5554","package":"com.android.shell","uid":2000,"records":[{"time":1790521762.474251,"pid":24931,"tid":24931,"level":"I","tag":"KaragozManual","message":"hello from karagoz"}],"omitted":0}
```

That record was written with `adb shell log -t KaragozManual "hello from karagoz"`; `adb shell` runs as `com.android.shell`.

## Output

```json
{
  "device": "emulator-5554",
  "package": "com.android.shell",
  "uid": 2000,
  "records": [
    {
      "time": 1790521762.474251,
      "pid": 24931,
      "tid": 24931,
      "level": "I",
      "tag": "KaragozManual",
      "message": "hello from karagoz"
    }
  ],
  "omitted": 0
}
```

An empty `records` with `omitted` `0` means nothing matched. It is not an error.

- `device` string: Serial or UDID of the device read.
- `package` string: Only with `--package`: the package as given.
- `uid` number: Only with `--package`, on Android: the uid the records were matched on. `com.android.settings` has 1000, which it shares with the system server.
- `records` array: The matching records, at most `--lines`, oldest first: on Android in the order the device's log daemon received them, on iOS by time.
- `records[].time` number: When the record was written: seconds since the Unix epoch on the device clock, to the microsecond. Rounded up on Android; on iOS as `log` prints it.
- `records[].pid` number: The process that wrote it.
- `records[].tid` number: The thread that wrote it.
- `records[].level` string: One of `V`, `D`, `I`, `W`, `E`, `F`; `?` for any other priority. On iOS, `D`, `I`, `E`, `F` or `?` (see [On an iOS simulator](https://karagozmcp.com/docs/commands/logs/#on-an-ios-simulator)).
- `records[].tag` string: On Android the log tag; on iOS the name of the process that wrote it.
- `records[].subsystem` string: Only on iOS: the record's `os_log` subsystem, `""` when it has none.
- `records[].category` string: Only on iOS: the record's `os_log` category, `""` when it has none.
- `records[].message` string: The message, newlines kept.
- `omitted` number: How many older matching records `--lines` left out. `0` when `records` holds every match.

## Reading since your last call

Pass the largest `time` of one result as the next `--since` to get only newer records. The rounding up makes this exact: no record comes back in the next call. After the call above:

```console title="~/app"
$ karagoz logs --package com.android.shell --since 1790521762.474251
{"device":"emulator-5554","package":"com.android.shell","uid":2000,"records":[],"omitted":0}
```

Take the largest `time`, not the last record’s: records are in arrival order, and times can step back by a few milliseconds.

## Errors

| Code                                                                      | When                                                                                                                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`INVALID_ARGS`](https://karagozmcp.com/docs/error-codes/#invalid-args)   | `'<value>' is not a package name`, as for `launch`; `--since must be Unix time in seconds (got '<value>')`; `--lines must be a whole number from 1 to 999999999 (got '<value>')`. Checked before any device call.                                                                                                                                                                                               |
| [`APP_NOT_FOUND`](https://karagozmcp.com/docs/error-codes/#app-not-found) | `package 'dev.karagoz.missing' is not installed on emulator-5554`.                                                                                                                                                                                                                                                                                                                                              |
| [`ADB_FAILED`](https://karagozmcp.com/docs/error-codes/#adb-failed)       | logcat printed its own error instead of records, such as `Failed to wait for logd.ready to become true. logd not running?`; the output was not whole log records; or `pm list packages` printed an error while Karagöz looked up the uid. The message is that output, cut at 300 characters. A log larger than the 64 MB Karagöz reads at once is `ADB_FAILED` too, with a message that says to pass `--since`. |
| [`ADB_TIMEOUT`](https://karagozmcp.com/docs/error-codes/#adb-timeout)     | After 10 s.                                                                                                                                                                                                                                                                                                                                                                                                     |

Every call can also get the [device selection errors](https://karagozmcp.com/docs/device-selection/#errors) and the adb errors.

## Notes on Android

- Logs read: main, system and crash, plus kernel from Android 11, logcat’s defaults. The events log is not read.
- The log is never cleared, resized or reconfigured, so other tools and the user keep their history. A call reads what is there and exits; nothing streams. To wait for a line, call again with `--since`.
- On a full log of 26 MB, about 133,000 records, `logs --lines 1` took 0.7 s, 0.8 s with `--package`.
- The default 30 records come to about 7 KB of JSON on average. The largest 30-record result measured was 47 KB.
- `--package` matches the uid, not a process: every process of the app, every restart, and its Java and native crash lines. Lines the system server writes about the app, such as `Start proc` and `ANR in`, have uid 1000 and are not included. A package that shares a system uid, such as Settings, gets the other processes of that uid as well.
- `--package` reads the app in the main Android user. Its copies in Secure Folder, a work profile or a second instance (Dual Messenger, Dual Apps) run under another uid and are not included.
- A record stamped at or before `--since` that the log daemon receives after the previous read is returned by neither call. Records arrived up to 54 ms late in testing.
- `--since` is on the device clock, which can differ from the computer’s, even with automatic time on. Pass a `time` from an earlier result.
- A missing line does not prove the app did not write it: some phones drop lines before they reach the log. A `log.tag` property of `I` (`adb shell getprop log.tag`) drops `D` and `V` lines, Developer options’ “Logger buffer sizes: Off” drops nearly all, and Huawei and Honor phones keep app logs off until “AP Log” is turned on in a hidden menu.
- Android cuts a record’s tag and message at 4068 bytes together when it is written. Bytes that are not valid UTF-8 become U+FFFD.
- From Android 15 the device ends a read after 5 s without data and still exits `0`, so a read cut short looks complete.

## On an iOS simulator

```console title="~/app"
$ karagoz logs --package dev.karagoz.probe --lines 1
{"device":"2EC769C1-2EF2-47EB-A441-E6843968F039","package":"dev.karagoz.probe","records":[{"time":1792054562.937849,"pid":43961,"tid":5236136,"level":"I","tag":"Probe","subsystem":"com.apple.Accessibility","category":"AXAppAccessibility","message":"Presentation controller doesn't modalize: <_UIRootPresentationController: 0x10536f350>"}],"omitted":297}
```

- The window is a time span, since `log show` has no record count. On an idle simulator a minute took under 1 s and an hour 8 s. A read that passes 30 s fails with [`SIMCTL_TIMEOUT`](https://karagozmcp.com/docs/error-codes/#simctl-timeout).

- `--package`:

  - keeps the records of every process that runs the installed build of the app: the app and its extensions, with the lines of the frameworks they load, from this install and from earlier installs of the same build.
  - also keeps the `exited ...` line `launchd_sim` writes when such a process ends, the only trace of a crash by signal.
  - leaves out records of other builds of the app, other processes’ lines about the app, and `print` and stderr output.
  - does not tell apart two apps whose executables are byte for byte the same. Two Flutter apps made from the template with the same plugins are an example.

- `level`: Debug is `D`, Info and Default are `I`, Error is `E`, Fault is `F`, anything else `?`. `Logger.notice` and `NSLog` write Default.

- The simulator keeps Info records in memory only for a few minutes (gone after 213 s in testing) and does not store Debug records by default.

- `time` comes from the computer’s clock. Chained reads, as in [Reading since your last call](https://karagozmcp.com/docs/commands/logs/#reading-since-your-last-call), return nothing twice. Of two records in the same microsecond, the second can be lost.

- The log is never cleared or reconfigured.

- Karagöz runs `simctl spawn <udid> log show --style ndjson --info --debug` once, bounded in time and filtered, and reads its output as it streams.

### Errors on a simulator

| Code                                                                            | When                                                                                                                     |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| [`APP_NOT_FOUND`](https://karagozmcp.com/docs/error-codes/#app-not-found)       | `app 'dev.karagoz.nope' is not installed on <udid>`, before the read.                                                    |
| [`SIMCTL_FAILED`](https://karagozmcp.com/docs/error-codes/#simctl-failed)       | `log`'s own error, such as `log: Bad predicate ...`.                                                                     |
| [`SIMCTL_FAILED`](https://karagozmcp.com/docs/error-codes/#simctl-failed)       | Output that is not `log show`'s records: `unexpected log output: <line>`, `log show ended without its summary line`.     |
| [`SIMCTL_TIMEOUT`](https://karagozmcp.com/docs/error-codes/#simctl-timeout)     | After 30 s: `log show did not finish within 30s. Pass a later --since, or --package, to read less of the simulator log.` |
| [`DEVICE_NOT_READY`](https://karagozmcp.com/docs/error-codes/#device-not-ready) | The simulator is not booted.                                                                                             |

## On an iPhone

Reads a paired iPhone attached by USB cable, with no `sudo` and nothing installed on the Mac or the phone.

- Each read needs about 230 MB of free space in the temporary folder for the length of the call. A short window does not make it smaller: a minute, an hour and a `--package` read each move an archive of about 230 MB from the phone, in 8 to 10 s.

- The whole log moves more: five weeks of records came to 1.1 GB and took 30 s.

- The temporary folder is removed after the read, also when the call is cancelled.

- Records older than the default window are there. The phone stores Info records, unlike a simulator, and some Debug records.

- `--package` keeps the records of the app’s install folders that are proven to be this app: the folder it is installed in now, and any folder one of whose processes launchd started under the app’s id in the archive read.

  - A build changed since the last install always logs under its current folder. A build installed again unchanged logs under the folder of its first install, and is proven once it starts or stops within the read.
  - When nothing is proven, every folder with the app’s folder name is kept. Another app with the same name, such as a second Flutter app’s `Runner.app`, can then mix in. The phone gives no way to read a build’s identity, which a simulator does.
  - Exit lines come from `launchd`.

- `<private>` stands where the app or the system marked a value private: the phone masks it before Karagöz reads it. Karagöz does not change the phone’s log settings.

- `time` is the phone’s clock.

- A locked phone reads the same. Two reads of one phone at once both work; the second took 14 s.

How the iPhone log is read

Karagöz takes this Mac’s pairing record for the phone from usbmuxd; it stays in memory. Karagöz opens a session with the phone over TLS and asks the phone’s log service for an archive of its records from a start time. `tar` unpacks the archive into a temporary folder, and macOS’s own `log show` reads it as on a simulator.

### Errors on an iPhone

| Code                                                                            | When                                                                                                       |
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| [`APP_NOT_FOUND`](https://karagozmcp.com/docs/error-codes/#app-not-found)       | Before the read.                                                                                           |
| [`DEVICE_NOT_READY`](https://karagozmcp.com/docs/error-codes/#device-not-ready) | The phone is not connected by USB cable.                                                                   |
| [`DEVICE_NOT_READY`](https://karagozmcp.com/docs/error-codes/#device-not-ready) | This Mac holds no pairing record for the phone: unplug it, plug it in, tap Trust.                          |
| [`DEVICE_NOT_READY`](https://karagozmcp.com/docs/error-codes/#device-not-ready) | The phone refuses the session.                                                                             |
| [`HELPER_FAILED`](https://karagozmcp.com/docs/error-codes/#helper-failed)       | The phone's log service or the stream fails.                                                               |
| [`HELPER_FAILED`](https://karagozmcp.com/docs/error-codes/#helper-failed)       | Nothing comes for 30 s.                                                                                    |
| [`HELPER_FAILED`](https://karagozmcp.com/docs/error-codes/#helper-failed)       | `tar` or `log` fails, with its own message.                                                                |
| [`HELPER_FAILED`](https://karagozmcp.com/docs/error-codes/#helper-failed)       | `log show did not finish within 30 s. Pass a later --since, or --package, to read less of the iPhone log.` |
| [`WRITE_FAILED`](https://karagozmcp.com/docs/error-codes/#write-failed)         | The temporary folder cannot be created.                                                                    |

## MCP equivalent

MCP equivalent `logs {"package":"com.android.shell"}` [MCP tools](https://karagozmcp.com/docs/mcp/#tools)

Arguments: `package`, `since`, `lines`, `device`. The names are the CLI's option and positional names.

## Related commands

- [karagoz launch](https://karagozmcp.com/docs/commands/launch/): Rejects a malformed package name with the same message.
