Oninit® Logwalker — Operations
Logwalker is a single command-line binary. There is no daemon, no configuration file, no state directory; every invocation is one walk of one log uniqid with the flags you pass on the command line. This page documents the flags and the runtime contract; the Output Schema page documents what each line of the resulting text stream means.
Synopsis
logwalker -n <uniqid> [options] # walk one log
logwalker -t <db:table|partnum> --from-time <dt> [options] # track one object over a date window
logwalker -t <db:table|partnum> --from-log <id> [options] # track one object over a log range
logwalker -n <uniqid> --chunk <file> --start <b> --length <b> \
--truncate-at <dt> --truncate-out <file> [--keep-tail] # cut an offline log at a point in time
Command-line flags
| Flag | Meaning |
|---|---|
| -n <uniqid> | Required for a single-log walk (omitted in -t track mode, which derives its log set from the range). The logical-log uniqid to walk. Must be an integer the engine still has on disk — archived logs that have been recycled past the LOGFILES ring are not walkable. Query syslogs or onstat -l to see what's currently available. |
| -m <count> | Cap the number of records scanned, not printed (0 = unlimited) — counts every record examined, including ones -f/-t suppress from the output. With -t set, -m can scan past without printing anything at all; raise it or drop it if a filtered walk looks truncated. Useful for sanity-checking a big log without waiting for the full walk. Default 0. |
| -f, --filter T,U,… | Comma-separated list of record types to print. Type names (BEGIN, HINSERT, COMMIT, …) or numeric hex codes (0x0028,0x0002) accepted. Records of other types are silently skipped. |
| -t, --table <tgt> | Object-track mode: print every record whose primary partition matches <tgt> across a range of logs, rather than walking one log in full. <tgt> is either <database>:<table> (resolved to a data partnum through the phase-1 catalog) or a partnum given in hex (0x…) or decimal. Requires a start of range (--from-log or --from-time); pairs with an end (--to-log / --to-time). Composes with -f, --decode-rows and every output mode. Under --chunk (engine offline) the catalog is unreachable, so <tgt> must be a numeric partnum — a db:table name is refused. Replaces -n: a track walk derives its own log set from the range, so you don't pass a single uniqid. In HUMAN and --group-by-xid output it also reports each matched transaction's boundary, so you can tell whether a change actually persisted — see Transaction boundaries below. See the object-track example. |
| --from-log <id> | Start uniqid for a -t range walk. Must still be on disk — if it is older than MIN(uniqid) in syslogs the walk is refused with the oldest walkable uniqid named (overwritten logs can't be walked). |
| --to-log <id> | End uniqid for the range (inclusive). Optional — defaults to the last log on disk (MAX(uniqid)). |
| --from-time <dt> | Start the range at the log covering a datetime instead of a uniqid — the date-scoped form of --from-log. <dt> is 'YYYY-MM-DD HH:MM:SS' or 'YYYY-MM-DD' (time assumed 00:00:00). logwalker probes each log's first BEGIN/COMMIT timestamp and binary-searches for the log that contained <dt>. Mutually exclusive with --from-log. |
| --to-time <dt> | End the range at the log covering a datetime (same format as --from-time). Mutually exclusive with --to-log. |
| --decrypt | Decrypt encrypted-dbspace (EAR) logical logs before walking. A plain walk of an encrypted log only yields ciphertext, so logwalker refuses it with a pointer to this flag. Requires --keystore; --dbsnum is auto-detected. The crypto reader is statically linked in — no extra package. Needs gsk8capicmd_64 (GSKit) on PATH. |
| --keystore <ks> | Keystore base for --decrypt. A bare name (no /) resolves under $INFORMIXDIR/etc/ — the Informix default keystore location — so you can pass the keystore name straight from onconfig DISK_ENCRYPTION. A value containing / is used as a full path verbatim. The raw DISK_ENCRYPTION value is accepted too: a leading keystore= and a trailing ,cipher=… are stripped. Do not add the .p12/.sth suffix — the library appends them. |
| --dbsnum <N> | The DBSPACE number — NOT the chunk number. Used for --decrypt key derivation. Best practice: omit it — logwalker auto-detects the dbspace from the log's chunk via the resolve. Pass it only to override. The common mistake is giving the chunk number (from onstat -l) instead of the dbspace number (from onstat -d / sysdbspaces): a wrong value silently derives the wrong key and the output stays ciphertext (logwalker flags this with a [CRITICAL] "no valid records" notice). See Troubleshooting. |
| --report-skips | Report every page-filler heuristic skip to stderr — offset, reclen/rectype/xid, and why — plus a total at end-of-walk. The walker jumps past a chunk page's zero-padded tail with no evidence by default; that's routine on nearly every page, so it's silent unless you ask. A real record whose xid happens to match the filler shape would otherwise vanish from a forensic walk without a trace — use this flag to audit exactly where the heuristic fired. |
| -r, --decode-rows | Schema-aware row decoding. On startup, after the cheap systabnames phase, logwalker walks every distinct accessible database and pulls syscolumns into a per-table column list. HINSERT / HUPAFT / HUPBEF / HDELETE bodies then render as (col1=val1, col2=val2, …) instead of a hex dump. Adds startup latency proportional to the number of databases on the instance. |
| --compat <fmt> | Alternate output format. The only supported value is onlog, which emits the legacy onlog -l 6-column shape (addr len type xid id link) plus per-type tail extras for downstream tooling that already parses that form. See the --compat onlog example. |
| --group-by-xid | Wrap each transaction's records with --- TX <xid> BEGIN @ LSN U:<low> --- / --- TX <xid> COMMITTED @ LSN U:<low> --- framing markers. Because xid is reused after a transaction completes, later BEGINs with the same numeric xid as a prior frame get an (xid REUSED) tag so the framing stays unambiguous. |
| -u <user>, -p <pass> | Source-DB user and password for the sysmaster connect. Optional — if the running OS user already has CONNECT privilege on sysmaster (e.g. informix) the catalog load works without explicit credentials. Prefer -p - over -p <pass>: a literal password on the command line is visible to every local user via /proc/<pid>/cmdline for the life of the run, a real concern on a tool designed to run on a shared database server. -p - prompts instead (echo off, reads the terminal directly) and never touches argv. |
| -s <server> | Override INFORMIXSERVER for the sysmaster connection. Defaults to the environment variable. |
| --truncate-at <dt> | Point-in-time cut of one offline log file (needs --chunk, --length, -n and --truncate-out). <dt> is 'YYYY-MM-DD HH:MM:SS' in the local time zone of the machine running logwalker. Keeps everything through the last COMMIT at or before <dt>, zeroes the rest of that page and drops the later pages, so the log reads like a salvaged current log; transactions still open at the cut are rolled back by logical restore. It scans in log order and stops at the first later commit (a clock stepped backwards can leave timestamps out of order, so it never binary-searches). See Point-in-time cut of an ontape log backup for a complete worked example. |
| --truncate-out <file> | Where --truncate-at writes the shortened log. The file is created new — an existing file is never overwritten, and the input is only ever read. |
| --keep-tail | With --truncate-at: copy the bytes that follow the walked extent (--start + --length) after the cut. For an ontape log backup file, walk just the log pages with --start/--length and add this, so the backup's trailer and end-of-tape header stay in place and the file is still a well-formed backup. |
| --restore-filter <cmd> | The --chunk file is an ontape backup written with an onconfig BACKUP_FILTER (for example gzip). logwalker unframes it, decodes it with <cmd> — the matching RESTORE_FILTER (for example gunzip) — and walks or cuts the decoded log. --start and --length become optional (the log pages run from the data marker to the trailer marker). With --truncate-at it also needs --backup-filter. See Backups written with a BACKUP_FILTER. |
| --backup-filter <cmd> | Re-encodes a cut log: your BACKUP_FILTER (for example gzip). The output is a normal filtered backup file — the original tape header and end block with the data re-blocked — that the engine's own restore filter reads. |
| --tape-block <KB> | Block size of the filtered file. By default logwalker reads LTAPEBLK from the engine's onconfig — $ONCONFIG, looked up under $INFORMIXDIR/etc unless it is an absolute path — so on the database server you normally give nothing. If the onconfig can't be read it warns and assumes 32. Pass this flag to override. Only used with --restore-filter. |
| --pit-filter | Run as the engine's RESTORE_FILTER (stdin → stdout) so a restore stops at a point in time without touching the backup files. The point in time comes from the LW_PIT environment variable (or --truncate-at); the inner decoder from --restore-filter. Archives pass through untouched; the log that contains the PIT is cut; logs wholly before it pass through; logs after it are emitted with no log pages. See Stopping a restore at a point in time with a restore filter. |
| -V | Print version and exit. |
| -h, --help | Print usage and exit. |
Transaction boundaries in track mode
A track walk filters to the records that carry the tracked partition, so on its own it shows that a row changed — an HINSERT, an HDELETE — but not whether the change persisted. An INSERT whose transaction never reached a COMMIT (or that ended in a ROLLBACK) was undone; the bare record can't tell you which. Because BEGIN / COMMIT / ROLLBACK records carry no partition number, the plain filter would drop exactly the context you need.
So in HUMAN and --group-by-xid output, track mode passes through the boundary of every transaction that touches the object. The first time one of a transaction's records matches, logwalker prints an opening marker carrying the BEGIN's pid, uid and start time; when that transaction commits or rolls back, it prints a verdict:
=== TX 4812 opened on coris.sysprocedures (pid=73628 uid=1000 began 06/16/2026 14:02:11) === LSN 480661:0x00000080 xid=4812 INSERT into coris.sysprocedures (rowid 0x00000101, 64 bytes) === TX 4812 COMMITTED @ 06/16/2026 14:02:11 — 1 tracked change(s) PERSISTED === === TX 4815 opened on coris.sysprocedures (pid=73630 uid=1000 began 06/16/2026 14:02:14) === LSN 480661:0x000001dc xid=4815 DELETE from coris.sysprocedures (rowid 0x00000090) === TX 4815 ROLLED BACK — 1 tracked change(s) DISCARDED (did NOT persist) ===
At the end of the walk a one-line summary tallies the transactions seen (committed, rolled back, open). Any transaction still open — one that touched the tracked object but whose COMMIT or ROLLBACK lies beyond the walked range — is flagged explicitly, because its persistence is genuinely unknown until you walk far enough to see its fate:
# ---------------------------------------------------------- # transaction summary: 1 committed, 1 rolled back, 1 open # WARNING: 1 tracked transaction(s) had NO COMMIT/ROLLBACK in # the walked range — persistence UNKNOWN. Extend --to-log to # see their fate: # TX 4820 2 change(s) (pid=73641 began 06/16/2026 14:02:20)
The fix for an open transaction is simply to widen the range (raise --to-log, or drop it so the walk runs to the last log on disk). If the BEGIN itself fell before the start of the range, the opening marker says (BEGIN before walked range) rather than inventing a timestamp. The --compat onlog format is left untouched — it stays a pure six-column stream for scripts that parse it.
Point-in-time recovery
Point-in-time recovery — stopping an ontape restore at a chosen commit time — has its own page with a worked example, the layout of filtered backups, and the sidenav for the whole topic: Point-in-time recovery. The flags it uses are listed in the table above.
- Cut a log backup (--truncate-at, --truncate-out, --keep-tail)
- Backups written with a BACKUP_FILTER (--restore-filter, --backup-filter, --tape-block)
- Stopping a restore with a restore filter (--pit-filter)
Pre-flight gates
Before the walk begins, logwalker enforces three gates and refuses to start with a [CRITICAL] diagnostic if any fail:
- sysmaster connect — if the ESQL/C CONNECT to sysmaster fails, the deployment-constraint reminder is printed and the binary exits non-zero. This catches the common "wrong INFORMIXSERVER" / "wrong INFORMIXSQLHOSTS" mistake.
- uniqid resolve — the requested uniqid must exist in the current syslogs view. Archived-and-recycled uniqids and never-existed uniqids both fail here.
- chunk R_OK — every chunk path in the resolved extent map for the uniqid must be readable by the OS user running logwalker. If any one chunk fails access(R_OK), the binary refuses with the deployment-constraint reminder. No partial walks; either the operator can read every chunk in the extent map or the walk doesn't start.
Catalog phases
Two catalog-load phases run at startup, sequenced to amortize their cost differently:
- Phase 1 (names only) — walks sysmaster.systabnames in one read. Builds a partnum → (dbname, owner, tabname) lookup covering every named partition on the instance (tables and indexes both; systabnames is the unified pseudo-view). Single connection, ~2–3 seconds on a 2K-partition instance. Auto-loaded for HUMAN and GROUPED modes so partnums render as their <db>.<table> names; skipped for --compat onlog (whose output has no name slot).
- Phase 2 (column maps) — gated on --decode-rows. For each unique dbname collected in phase 1, opens a separate ESQL connection and pulls syscolumns ORDER BY colno into the table's column list. Cost is proportional to the number of databases, not the number of tables; closes each connection before the walker starts emitting records. Databases the running user can't CONNECT to are logged as warnings (SQLCODE -387 / -329 / -23197) and their tables' rows render as hex rather than decoded values.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Walk completed; records emitted to stdout. |
| 1 | Pre-flight gate failed (sysmaster connect, uniqid resolve, or chunk R_OK), or unrecoverable I/O error mid-walk. |
| 2 | Usage error (missing -n, bad flag combination, unknown --compat value). In --truncate-at mode the codes differ: 1 is any error (including bad options) and 2 means the point in time is not inside this file — see Point-in-time cut. |