Understanding journals and how to use them

Log in to save

Journalling is one of those functions people switch on because "it is needed for commitment control" and then leave sitting there unused. That is a shame, because the journal answers a question businesses ask every week: who changed this record, when, and what did it say before?

How it is put together

You need two objects:

  • The journal receiver (*JRNRCV), the physical file where entries are written.
  • The journal (*JRN), which points at the active receiver and which the files you want watched are attached to.
CRTJRNRCV JRNRCV(MYLIB/JRNRCV0001)
          TEXT('Application data receiver')

CRTJRN JRN(MYLIB/JRNDATA)
       JRNRCV(MYLIB/JRNRCV0001)
       MNGRCV(*SYSTEM)
       DLTRCV(*NO)

MNGRCV(*SYSTEM) matters: it leaves the system to detach the full receiver and create a new one. Without it, the receiver grows until it fills the disk, and that is one of the most classic incidents.

DLTRCV(*NO) says not to delete old receivers automatically. It is worth starting this way — with automatic deletion you risk losing entries before you have saved them — and moving to *YES only once the receiver save procedure is in place and verified.

Turning journalling on for a file

STRJRNPF FILE(MYLIB/CUSTOMERS)
         JRN(MYLIB/JRNDATA)
         IMAGES(*BOTH)
         OMTJRNE(*OPNCLO)

Two options count for a lot:

IMAGES(*BOTH) records both the record before the change and the one after. With *AFTER you only see the result: you know what it says now, not what it said before. It takes more space, but it is what makes the journal useful for understanding what happened.

OMTJRNE(*OPNCLO) leaves out file open and close entries. They are noise that inflates the receiver without adding information.

The everyday question

A customer insists their credit limit was different. The journal can answer:

DSPJRN JRN(MYLIB/JRNDATA)
       FILE(MYLIB/CUSTOMERS)
       FROMTIME('27/07/2026' '00:00:00')
       ENTTYP(*RCD)
       OUTPUT(*OUTFILE)
       OUTFILE(QTEMP/ENTRIES)

The entry types that matter for data work are three: PT (record inserted), UP (record updated) and DL (record deleted). With IMAGES(*BOTH) an update produces two entries: the before image and the after image.

By writing the result to a file (OUTPUT(*OUTFILE)), you can query it in SQL like any other table: filter by user, by time range, by record content. That is far more practical than paging through the screen, especially when there are thousands of entries.

Every entry carries the time, the job, the program and the user profile that made the change. That is the complete answer to the original question, and it requires no tracking to have been built into the application.

The role it was born for: recovering after a failure

The journal bridges the gap between the last save and the moment of failure. You restore the previous evening's backup, then reapply every subsequent change from the journal with APYJRNCHG. If the save was at 22:00 and the failure comes at 17:00, you recover the whole day instead of losing it.

There is also the opposite operation, RMVJRNCHG, which removes changes from a given point onwards — useful when a faulty batch has updated thousands of records and you need to go back.

Both commands should be rehearsed first, in a test library. They are operations that rewrite data: it is not the moment to meet them for the first time during an emergency.

Space, which is the practical problem

Receivers grow, and on a heavily updated file they grow fast. The countermeasures:

  • MNGRCV(*SYSTEM), so receiver changes are automatic.
  • Save the receivers along with the backup, and only then delete them.
  • Journal the right files. Not every file deserves a journal: work tables and rebuildable ones can be left out with nothing lost.
  • OMTJRNE(*OPNCLO), which removes a surprising number of useless entries.

A periodic check of the space receivers occupy avoids the surprise:

DSPOBJD OBJ(MYLIB/JRNRCV*) OBJTYPE(*JRNRCV) DETAIL(*FULL)

The audit journal, which is a different thing

Alongside application journals there is QAUDJRN, the security journal: it records sign-ons, changes to profiles, denied authority attempts. It is not about data but about who does what on the system, and it is switched on through the QAUDCTL and QAUDLVL system values.

It is worth having on even if nobody reads it daily: the moment you need it is always a moment when you can no longer switch it on retrospectively.


Which versions this works on

CRTJRNRCV, CRTJRN, STRJRNPF, DSPJRN, APYJRNCHG, RMVJRNCHG — long-standing commands, present on every release in use and with the same behaviour. Journalling is one of the most stable parts of the system.

MNGRCV(*SYSTEM) for automatic receiver management has been available for many releases and is the value to prefer. The manual method — detaching the receiver with CHGJRN JRNRCV(*GEN) from a scheduled program — is what came before and is still found on older installations: it works, but it depends on somebody remembering.

The QAUDJRN audit journal, with the QAUDCTL and QAUDLVL system values, has been around a long time. The individual event types that can be recorded have been extended across releases: the valid list is the one in the documentation for your own version.

Reading journal entries in SQL — more recent releases offer QSYS2.DISPLAY_JOURNAL, which avoids the detour through OUTFILE. It is an IBM i Service, so availability depends on release and PTF level. The method shown in the article (DSPJRN with OUTPUT(*OUTFILE) and then a query on the file) works on any version, which is why it is the one used here.

Commitment control rests on the journal and is likewise present across many releases.

Sources

← Back to blog

Comments

No comments yet. Be the first to comment!

You need an account to comment. Log in · Sign up