Exporting Device Logs
SDK Practice
Overview
The Wuji Hand 2 keeps a log ring buffer in flash. export_flash_logs() pulls the whole ring, decodes it frame by frame, and writes a JSONL file with one log entry per line.
When something goes wrong on the device, this is the primary evidence. It records events from the firmware’s point of view, which sits closer to the fault than host-side SDK logs.
Matching examples: examples/python/wuji_hand_2/5.export_flash_logs.py and examples/c/wuji_hand_2/5_export_flash_logs.c.
For API and field definitions, see Diagnostics and Logs.
This example only reads. It doesn’t enable the motors, and the fingers don’t move.
Prerequisites
Hardware
| Item | Requirement |
|---|---|
| Wuji Hand 2 | 1 |
| Connection | Ethernet |
Software
| Item | Requirement |
|---|---|
| Operating system | Linux or a Mac with Apple silicon, meeting the system requirements |
| Wuji SDK | v2026.8.31 or later |
| Python | 3.10 or later |
| C toolchain | CMake 3.16 or later, plus the extracted SDK package |
Data Flow
Every call exports everything and writes a new file. There’s no API to ask how many entries exist first, and no incremental export.
Where Output Goes
out_dir defaults to None, writing to the SDK’s default directory ~/.wuji/logs alongside the SDK’s own logs, with a flash_ prefix on the filename:
~/.wuji/logs/flash_<SN>_<date>_<time>.jsonl Pass out_dir to write elsewhere.
Fields per Line
| Field | Meaning |
|---|---|
timestamp_ms | Device timestamp in milliseconds |
level | Log level, such as Info or Warn |
target | The firmware module that produced the entry |
message | The log text |
Integration Steps
1. Discover and Connect to the Target Device
In Python, filter scan() results for DeviceType.WujiHand2, confirm that a match exists, then call connect(sn=...). In C, initialize the SDK, filter wuji_scan() results for WUJI_DEVICE_TYPE_WUJI_HAND_2, then connect by serial number with wuji_connect(). Export doesn’t require enabling the motors.
manager = SdkManager.instance()
hands = [d for d in manager.scan() if d.device_type == DeviceType.WujiHand2]
if not hands:
raise RuntimeError("No Wuji Hand 2 found")
hand = manager.connect(sn=hands[0].sn, device_name="wuji_hand_2") 2. Export Logs and Get the Result
One call exports all logs and returns the file path and frame count. Pass None in Python or NULL in C to use the SDK’s default directory.
Python
C
result = hand.export_flash_logs(out_dir=None)
print(result["path"], result["frames"])WujiFlashLogExport result;
WujiStatus st = wuji_hand_2_export_flash_logs(dev, NULL, &result);
if (st == WUJI_STATUS_OK) {
printf("%s: %u frames\n", result.path, result.frame_count);
wuji_hand_2_flash_log_export_free(&result);
} In C, read and free result only after a successful export. On failure, the output isn’t populated. Read the error and enter device cleanup.
3. Check the File and Disconnect
When the frame count is nonzero, the Python example reads and parses the first JSONL line. The C example prints the path and frame count. Use that path to read the file in the application.
if result["frames"]:
with open(result["path"], encoding="utf-8") as stream:
first = json.loads(stream.readline())
print(first["level"], first["target"], first["message"]) Python calls manager.disconnect_all() in finally. C calls wuji_dev_disconnect(dev), wuji_dev_release(dev), and wuji_shutdown() in order.
Running It
Python
C
python examples/python/wuji_hand_2/5.export_flash_logs.py
python examples/python/wuji_hand_2/5.export_flash_logs.py --out /tmp/hand2-logs./build/hand2/5_export_flash_logs Measurements
On a right-hand Wuji Hand 2:
Connected: WH2KA00XXXXXXXXX
Decoded 1180 frames into ~/.wuji/logs/flash_WH2KA00XXXXXXXXX_<date>_<time>.jsonl
First entry: [Info] sboard_app: [B2] T:25 R:20 g:0 bc:0/0 rel:0/0 mgmt:25/20 to:5 crc:0% oe:0 ne:0 fe:5 dma:0/0 | Metric | Measured |
|---|---|
| Entries exported | 1180 |
| Duration | 2.89 seconds |
| Lines in the file | 1180, matching frames |
| Returned fields | path, frames |
What the export contained:
| Level | Count |
|---|---|
Info | 1169 |
Warn | 11 |
target | Count |
|---|---|
sboard_app | 1117 |
sboard_app::bus_manager | 40 |
sboard_app::wuji_server | 12 |
sboard_app::zenoh_server | 6 |
sboard_app::wall_clock_persist | 2 |
Most of it is periodic status lines from sboard_app shaped like [B2] T:25 R:20 g:0 bc:0/0 ..., plus node heartbeats such as nodes 4/5: [1:hb=0 2:hb=0 ...]. When investigating, filter by level for Warn and above first, then narrow by target to a module.
Notes
| Branch point | Behavior | What to do |
|---|---|---|
| Empty ring buffer | frames is 0 and a file is still written | The example uses that to skip reading the first line |
| Target directory doesn’t exist | Handled on the SDK side | Passing an existing directory is safer |
| Several devices | The example uses the first it finds | On a shared network, name a serial number |
| Export duration | Scales with entry count, measured at 2.9 seconds for 1180 entries | Don’t call it inside a real-time loop |
Adapting It
Filter for What You Care About
Each JSONL line stands alone, so jq filters directly:
jq -c 'select(.level != "Info")' flash_*.jsonl
jq -c 'select(.target | startswith("sboard_app::bus_manager"))' flash_*.jsonl Export to a Specific Directory
python examples/python/wuji_hand_2/5.export_flash_logs.py --out /tmp/hand2-logs Pass the out_dir argument. The default lands in ~/.wuji/logs mixed in with the SDK’s own logs, distinguished by the flash_ prefix.
How This Relates to Host-Side SDK Logs
The two have different vantage points. Flash logs are the firmware’s view, recording the bus, node heartbeats, and server state. SDK logs sit in the same directory and record host-side connect, subscribe, and publish activity. For connection problems, compare timestamps across both.
Don’t Call It in a Real-Time Loop
A full export pulls the entire ring buffer, measured at about 3 seconds for a thousand-odd entries. This is an investigation tool, not a monitoring mechanism. For continuous monitoring, subscribe to joint_diagnostics.
Related
- Subscribing With Callbacks: how to get live diagnostic data
- Troubleshooting: common problems and how to locate them in logs
Documentation