# journalctl JSON exports

URL: /collect/formats/journald

Export the companion computer's systemd journal with journalctl -o json for the window or boot of a run. Foxborne reads the realtime and monotonic timestamps, the boot ID and the fields that say who logged each line.



The companion computer's systemd journal records what the autonomy stack, systemd and the kernel did during a run. Export it with `journalctl -o json`, which writes one JSON object per line, and import the file with the flight log. Foxborne reads it with parser `journal 0.6.0`.

If the companion collector is installed, it delivers the allowlisted journal units on its own. An export still matters for vehicles without a collector, and for lines from units outside the collector's allowlist.

## Export the journal [#export-the-journal]

Run the export on the companion computer as root, or as a member of the `systemd-journal` group, so it includes the system and kernel entries. Name the file for the host, as in `uas04-orin-journal.json`.

To export the window of a run, give its start and end in UTC:

```bash title="On the companion computer" caption="Example values: the window around run R-0931"
sudo journalctl -o json --since "2026-09-24 14:10:00 UTC" --until "2026-09-24 14:40:00 UTC" > uas04-orin-journal.json
```

To export a whole boot, find its boot ID, then pass it to `-b`:

```bash title="On the companion computer" caption="Example values: the boot that covers run R-0931"
sudo journalctl --list-boots
sudo journalctl -o json -b 9b2e4c1d0f8a4e57b3c2a1d9e8f7c6b5 > uas04-orin-journal.json
```

Export the whole journal for the window or the boot, not single units. The rules read the kernel's lines and systemd's lines about each unit, and a `-u` filter leaves the kernel's lines out.

<Callout type="warning" title="A time window needs a correct clock">
  `--since` and `--until` select entries by the companion's wall clock. If chrony never synchronized, that clock can be hours off.

  In INC-0139 the companion's journal stamped a crash at 03:14:07, during a flight that the flight log places after 14:05 UTC. Export by boot ID when you are not sure of the clock.
</Callout>

## Fields Foxborne uses [#fields-foxborne-uses]

| Field                   | What it holds                                                                                          | In the INC-0142 kill                                                  |
| ----------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| `__REALTIME_TIMESTAMP`  | Wall-clock time when journald received the entry, in microseconds since the epoch, as a decimal string | `1790260324118000`, which is 14:32:04.118 UTC                         |
| `__MONOTONIC_TIMESTAMP` | Monotonic time when journald received the entry, in microseconds, meaningful only with the boot ID     | `1152904000`, 1,152.904 s after boot                                  |
| `_BOOT_ID`              | The kernel's ID for the boot, 128 bits in hex                                                          | `9b2e4c1d0f8a4e57b3c2a1d9e8f7c6b5`                                    |
| `_HOSTNAME`             | The host that logged the entry                                                                         | `uas04-orin`                                                          |
| `PRIORITY`              | The syslog level, from 0 (emerg) to 7 (debug), as a decimal string                                     | `3`                                                                   |
| `SYSLOG_IDENTIFIER`     | The syslog tag of the program that logged it                                                           | `kernel`                                                              |
| `_PID`                  | The ID of the process that logged it                                                                   | Absent on kernel lines. systemd's own lines carry `1`.                |
| `_TRANSPORT`            | How journald received the entry: `audit`, `driver`, `syslog`, `journal`, `stdout` or `kernel`          | `kernel`                                                              |
| `UNIT`                  | The unit a message from systemd is about                                                               | Absent on the kill. systemd's next line carries `perception.service`. |
| `MESSAGE`               | The text of the entry                                                                                  | The kernel's kill line, below                                         |

Each field has a job in the investigation:

* **Time.** `__REALTIME_TIMESTAMP` places each entry on the companion's wall clock. `__MONOTONIC_TIMESTAMP` and `_BOOT_ID` place it on the monotonic clock of its boot, the clock that collector samples share.
* **Level.** `PRIORITY` sets how an entry shows: the timeline marks priority 3 or lower red and 4 amber. The event page labels 2 or lower **Critical**, 3 **Error** and 4 **Warning**.
* **Source.** `_HOSTNAME`, `SYSLOG_IDENTIFIER`, `_PID`, `_TRANSPORT` and `UNIT` say who logged the entry and what it is about. The event page's tags carry them as host, identifier, pid, transport and unit.
* **Text.** `MESSAGE` is quoted exactly wherever the entry appears as evidence, with the line it came from.

| `PRIORITY` | Level   |
| ---------- | ------- |
| 0          | emerg   |
| 1          | alert   |
| 2          | crit    |
| 3          | err     |
| 4          | warning |
| 5          | notice  |
| 6          | info    |
| 7          | debug   |

## How Foxborne recognizes the export [#how-foxborne-recognizes-the-export]

Foxborne looks for `__REALTIME_TIMESTAMP` in the file's first 4,096 bytes. The file's row then reads **journalctl JSON export** with **Journal fields found**. journalctl's default `short` output carries no field names, so Foxborne cannot recognize it.

## Clocks and chrony [#clocks-and-chrony]

Both timestamps mark when journald received the entry, not when the program wrote it. `__REALTIME_TIMESTAMP` is usually a little later than `_SOURCE_REALTIME_TIMESTAMP`, the earliest trusted time of the message.

Wall time is only as good as the clock under it, and chrony disciplines that clock. Foxborne anchors the journal to UTC while chrony reports a synchronized source. For run R-0931 the run page reads **chrony, synchronised, offset −1.8 ms**, and the example dataset carries ±6 ms on the journal.

When chrony has no source, `chronyc tracking` shows `Leap status : Not synchronised`. In INC-0139, chronyd logged **No suitable source for synchronisation** at boot, and the run page reads **None: chrony never synchronised**. The journal then stays on its own clock, and an engineer can [align it by hand](/investigate/align-a-clock).

Record `chronyc tracking` before and after each run, and keep the output with the journal.

## Example: the kill in INC-0142 [#example-the-kill-in-inc-0142]

The kernel killed `perception_node` for running out of memory at 14:32:04.118 UTC. This is the entry in the example dataset's export, on several lines to read:

```json title="uas04-orin-journal.json" caption="Example dataset, INC-0142, line 18,314"
{
  "__REALTIME_TIMESTAMP": "1790260324118000",
  "__MONOTONIC_TIMESTAMP": "1152904000",
  "_BOOT_ID": "9b2e4c1d0f8a4e57b3c2a1d9e8f7c6b5",
  "_HOSTNAME": "uas04-orin",
  "PRIORITY": "3",
  "SYSLOG_IDENTIFIER": "kernel",
  "_TRANSPORT": "kernel",
  "MESSAGE": "Out of memory: Killed process 2213 (perception_node) total-vm:11873248kB, anon-rss:6823516kB, file-rss:10240kB, shmem-rss:0kB, UID:1001 pgtables:14720kB oom_score_adj:0"
}
```

systemd's lines about the units follow. Each carries `SYSLOG_IDENTIFIER` `systemd`, `_PID` `1` and the unit in `UNIT`.

| Time, UTC    | `PRIORITY` | `UNIT`                   | `MESSAGE`                                                                       |
| ------------ | ---------- | ------------------------ | ------------------------------------------------------------------------------- |
| 14:32:04.121 | 5          | `perception.service`     | `perception.service: A process of this unit has been killed by the OOM killer.` |
| 14:32:04.133 | 5          | `perception.service`     | `perception.service: Main process exited, code=killed, status=9/KILL`           |
| 14:32:04.134 | 4          | `perception.service`     | `perception.service: Failed with result 'oom-kill'.`                            |
| 14:32:04.139 | 6          | `mavlink-router.service` | `Stopping MAVLink router...`                                                    |
| 14:32:04.152 | 6          | `mavlink-router.service` | `Stopped MAVLink router.`                                                       |
| 14:32:09.141 | 6          | `perception.service`     | `perception.service: Scheduled restart job, restart counter is at 1.`           |
| 14:32:09.203 | 6          | `mavlink-router.service` | `Started MAVLink router.`                                                       |
| 14:32:09.214 | 6          | `perception.service`     | `Started Perception service.`                                                   |

The event page's **Unit history** lays out the same lines. The `process_exit` rule fired on the exit of `perception.service` and on the stop of `mavlink-router.service`, and `service_restart` fired on the restart.

## Related [#related]

* [Clocks, anchors and error bounds](/concepts/clocks) explains the wall clock, the monotonic clock and chrony's bound.
* [The companion collector](/collect/collector) delivers allowlisted journal units without an export.
* [process\_exit](/rules/process-exit) and [service\_restart](/rules/service-restart) describe the rules that read these lines.
* [Import files in the console](/collect/import-files) covers the upload itself.
