Oninit® Logwalker — Output Schema
Logwalker emits text. Every line is either a header line for one log record or an indented continuation line carrying that record's decoded body (or a hex dump of bytes the per-type decoder didn't handle). The Output Examples page links to verbatim sample output for each mode; this page documents the field shapes you'll see there.
HUMAN-mode header line
LSN <uniqid>:0x<low> xid=<N> <plain-English description>
| Field | Meaning |
|---|---|
| LSN <uniqid>:0x<low> | Canonical Informix LSN. The low half is the slotted-page encoding (page_idx « 12) | offset_in_page — not the raw chunk byte offset. The same value the loghdr's link field carries on the next record in the chain. |
| xid=<N> | Transaction id from the loghdr. Not unique across a log — the engine reuses xid values after a transaction completes. Pair with the BEGIN's LSN if you need a unique identifier; or use --group-by-xid which handles reuse explicitly. |
| plain-English description | Per-type sentence: INSERT into <db>.<table> (rowid 0x…, N bytes), BEGIN transaction (pid=N, started YYYY-MM-DD HH:MM:SS), CHALLOC — allocated N page(s) at chunk M page P, and so on. Partnums render as their catalog <db>.<table> names when the phase-1 catalog resolves them, else fall back to partition 0xN. |
Per-record-type body decoders
| Type code | Name | Decoded body fields |
|---|---|---|
| 0x0001 | BEGIN | begin_lsn_low + timestamp + uid + pid |
| 0x0002 | COMMIT | commit timestamp |
| 0x0004 | BEGCOM | commit timestamp (single-record begin+commit) |
| 0x0009 | CKPOINT | header only |
| 0x000d | ERASE | partnum |
| 0x0011 | UNIQID | partnum + next_serial |
| 0x001c | ADDITEM | parent_part + child_part + rowid + slot_len + 2 trailing int2 columns |
| 0x0020 | BLDCL | partnum + four numeric columns + table name (catalog-aware) |
| 0x0028 | HINSERT | partnum + rowid + rowlen (rowlen is an int2; the high bytes belong to a different field) |
| 0x0029 | HDELETE | partnum + rowid + rowlen |
| 0x002a | HUPBEF | partnum + rowid + rowlen |
| 0x002b | HUPAFT | partnum + rowid + rowlen |
| 0x0032 | PTEXTEND | partnum + n_pages + chunk:page |
| 0x0033 | CHALLOC | chunk + first_page + n_pages |
| 0x0034 | CHFREE | chunk + first_page + n_pages |
| 0x0076 | PTRUNCATE | partnum |
| 0x0077 | TRUNCATE | partnum |
Unknown record types render as 0x<HHHH> in the description slot and the full body hex-dumps to stdout, 16 bytes per line, indented eight spaces so the dump lines up under the header. The walker never stops on an unknown type — it always advances by the header's len field to the next record.
Schema-aware row decoding
With --decode-rows, HINSERT / HUPAFT / HUPBEF / HDELETE bodies decode as (col1=val1, col2=val2, …) against the phase-2 column map. Covered column types: SMALLINT, INTEGER, SERIAL, BIGINT, BIGSERIAL, INT8, SERIAL8, DATE (DATE 'YYYY-MM-DD'), CHAR / NCHAR (blank-trimmed, single-quote-escaped), VARCHAR / NVARCHAR, LVARCHAR (coltype 40 and 43 both), BOOLEAN. FLOAT / SMALLFLOAT have a known on-disk width and render as a <float:N bytes> placeholder; decoding continues past them.
DECIMAL, DATETIME, INTERVAL, MONEY, BYTE, TEXT, and BLOB have no fixed on-disk width that this file can determine safely — sizing them needs Informix's own rtypsize() API, which the decoder deliberately doesn't link (so it stays testable without a live engine). Rather than guess a size and silently misread every later column from a corrupted offset, hitting one of these types stops the row: (col1=val1, …other columns NOT LOGGED) — nothing after it decodes, even if the engine did log it. This is a refusal, not a claim that the engine didn't log the data; see the caveat below for the case where it genuinely didn't.
Partial-row caveat: the same trailing …other columns NOT LOGGED marker also appears for a different, genuine reason — a HUPBEF / HUPAFT under Informix's standard (non-full-row) logging only records the columns that changed, not the whole row, so (col1=val1, …, col4=val4, …other columns NOT LOGGED) can mean the engine truly never wrote the rest to the log. The output doesn't distinguish the two cases today: an unsized type earlier in the row and a column the application simply didn't touch both end the row the same visible way. If it matters which one you're looking at, check the column's position and type against the table's schema — an unsized-type stop is fixed by column type and always the same for that table, a partial-row gap depends on which columns that specific UPDATE happened to touch.
--compat onlog form
With --compat onlog, logwalker emits the legacy onlog -l shape: a one-line banner addr len type xid id link followed by one line per record in the same column order. Tail extras after the basic six columns match onlog -n output:
- BEGIN — MM/DD/YYYY HH:MM:SS <pid> <username> (username resolved from the body's uid via getpwuid())
- COMMIT / BEGCOM — MM/DD/YYYY HH:MM:SS
- HINSERT / HDELETE / HUPBEF / HUPAFT — <partnum_hex> <rowid_hex> <rowlen>
- UNIQID — <partnum_hex> <next_serial>
- ADDITEM — <parent> <child> <rowid> <slot_len> <extra1> <extra2>
- BLDCL — <partnum> <n1> <n2> <n3> <n4> <table_name>
- CHALLOC / CHFREE — <chunk:page> <n_pages>
- PTEXTEND — <partnum> <n_pages> <chunk:page>
- ERASE — <partnum>
- PTRUNCATE / TRUNCATE — <partnum> 0
Address style in --compat onlog is bare hex (no 0x prefix) to match the legacy column-1 display. Addresses are inflated by one pagesize per crossed chunk-page boundary so they agree with onlog's page_idx × 2×pagesize + offset_in_page scheme — HUMAN mode uses the same transform under the LSN low formatting, just rendered as 0x….
--group-by-xid framing
With --group-by-xid, every BEGIN gets a leading frame line:
--- TX <xid> BEGIN @ LSN <uniqid>:<low> ---
…every COMMIT / BEGCOM gets a trailing frame line:
--- TX <xid> COMMITTED @ LSN <uniqid>:<low> ---
…and when an xid value recurs (the engine has recycled it after a prior transaction completed), the new BEGIN line is tagged (xid REUSED):
--- TX 26 BEGIN @ LSN 302:0x278 (xid REUSED) ---
ROLLBACK frames close with --- TX <xid> ROLLED BACK @ LSN … --- rather than COMMITTED.
Residual-record caveat
Logwalker walks raw chunk bytes sequentially. When the engine recycles a log file, the new uniqid’s records overwrite the chunk pages but the engine doesn’t scrub the bytes — pages that the new uniqid hasn’t filled yet still carry bytes from the prior generation. Logwalker has no signal to skip those residual bytes, so the record count it surfaces for a partially-filled uniqid will diverge from onlog -n's view of the same uniqid (which filters by the per-page logserial stamp). Within any single transaction’s BEGIN → COMMIT chain the two tools agree record-for-record; the divergence is at the inter-transaction layer.