Power users, watch out!
Brief: Job Accounting is a built-in OS/400 function that, for whatever reasons, is surrounded by mystery. This article removes the mystique by explaining why you'd want to use Job Accounting, how to set it up and how to use the data it produces.
It is safe to assume that many Data Processing managers have wondered, at some point in their career, who's using the computer the most. If you could only know which department has cornered the market on computer paper, for example, you could forecast its usage and place your next purchase order before paper runs out. Or, knowing that a particular user shows 500 percent more system use than peer users might reveal computer abuse or a security breach-especially if several people sign on with the same profile name.
OS/400 provides the tool you need to determine all this and much more. The tool is known as job accounting and is actually simple and straightforward, requiring no programming changes in your applications. It uses journals and journal receivers, which may explain why most DP managers shy away from its implementation. However, as Craig Pelkie demonstrated in his two-part article series, "Introduction to Journaling" (MC, January and February 1992), there's really nothing terribly complicated about journaling-it's just a new concept to which you need to become accustomed.
Why Bother?
Job accounting lends itself to many uses. Anytime you need to know how much your system and any of its devices is being used and by whom, job accounting can help you.
For example, your corporation may insist that each department be charged, for the use of the computer, a dollar amount which is in direct proportion to the time they spend using it. Or you may want to start charging departments for the papers and ribbons they use while printing their favorite reports. You could also charge departments for using communication lines, especially if they connect with systems across the nation or overseas.
Why charge them? Data Process-ing is usually perceived as a service department-a constant source of expenses without tangible revenues. There's nothing you can do to correct this impression unless you produce evidence that the cost of operating your department should be attributed (in great part) to other departments. Job accounting provides that evidence in the form of cold data residing in journal receivers. Since journal receiver entries cannot be faked, altered or deleted (even by QSECOFR!), there's no doubt that journal receivers tell the truth, the whole truth and nothing but the truth.
If you're thinking that the Send Journal Entry (SNDJRNE) command can fake a journal receiver entry, think again. SNDJRNE-produced entries always have a code of U, while entries produced by job accounting have a code of A.
Accounting Codes
Before we delve into the details of setting up job accounting, you need to get acquainted with accounting codes. Before you start yelling, "Oh, no! Here we go with more codes I have to memorize," I'll hasten to say that these codes are anything you want them to be. And they aren't short, cryptic codes either; you can use codes of up to 15 characters in length.
You don't have to maintain a file or table of accounting codes in order to use them. You simply make them up and use them immediately. They provide a way to group system activities. You can use the codes to summarize activity or print reports broken down by accounting codes.
For example, you could use a different accounting code for each department in your company, and call them ACCOUNTING, PERSONNEL, SALES, INVENTORY, SHIPPING and so on.
Jobs may or may not have accounting codes assigned. Perhaps you don't care to group your system's jobs in any fashion, being content to have the job accounting data broken down by user profile. But what would happen if your company hires a data entry operator who works half of each day in Purchasing and the other half in Finance?
Assigning Accounting Codes
You can assign accounting codes at three different levels:
In the job description. The job description contains an ACGCDE parameter (in either the Create Job Description [CRTJOBD] or the Change Job Description [CHG-JOBD] command), which normally defaults to *USRPRF. If you use *USRPRF, the system looks up the accounting code found in the user profile.
In the user profile. The user profile also has an ACGCDE parameter (which you can set with either the Create User Profile [CRTUSRPRF] or the Change User Profile [CHGUSRPRF] command).
In the job. After the job runs, a CL program can run the Change Accounting Code (CHGACGCDE) command to use an accounting code different from the one the job used when it started.
You could begin by assigning accounting codes to your user profiles (or job descriptions), grouping your users in a sensible manner-perhaps by department or by the project on which they are working. For users who work in different departments at different times, you can embed CHGACGCDE commands in your CL programs.
Batch and System Jobs
Batch jobs always use the accounting code that was active at the time the job was submitted to batch (with SBMJOB). SBMJOB does not provide a way to override the accounting code (not even with a job description in the JOBD parameter), so you have to make sure that batch jobs are submitted while using the correct accounting code. If in doubt, use the CHGACGCDE command immediately before SBMJOB and immediately after (to restore your accounting code).
Some system jobs have an accounting code of *SYS, and others have none. You can change the accounting code assigned to RJE, printer and other readers and writers by changing their job description and starting the jobs again. All other system jobs have fixed accounting codes that cannot be changed by any means.
The CHGACGCDE Command
CHGACGCDE has a public authority of *USE. This can create a way to cheat the job accounting system, since a user with enough authority can execute CHGACGCDE from the command line and avoid being charged for activity he performed.
Although all uses of the CHG-ACGCDE command are duly recorded in the journal receiver with JACCDE=99 (making such occurrences easy to spot), you probably want to keep your job accounting data as accurate as possible. Therefore, you would want to prevent users from executing CHGACGCDE as a way to cheat your journaling system.
The trouble is that any user may need access to CHGACGCDE if you include it in a CL program. The solution is simple:
1. Create your own version of CHGACGCDE (call it USEACGCDE) and put it in a library that's higher than QSYS in the system portion of the library list, which we'll name ALTQSYS for illustration. To create this command, enter the source code listed in Figures 1a and 1b and compile as indicated at the bottom of each figure.
2. Revoke public authority to the original command in QSYS:
GRTOBJAUT OBJ(QSYS/CHGACGCDE) + OBJTYPE(*CMD) + AUT(*EXCLUDE) + USER(*PUBLIC)
3. Now include USEACGCDE in your CL programs instead of CHG-ACGCDE.
USEACGCDE can only be run from a CL program or a REXX procedure-never from the keyboard. Its processing program runs the original CHGACGCDE command while adopting the authority of QSECOFR. Notice too that USEACGCDE doesn't let you change another job's accounting code-only your own, as an added security feature.
How to Set Up Job Accounting
Once you have assigned accounting codes to your users (either through their user profiles or through job descriptions), you're ready to start using job accounting. These are the steps you need to follow:
1. Create a journal receiver in one of your production libraries, preferably in one you use for objects used by the entire company. It's not a good idea to place it in QSYS, however; you should leave QSYS pretty much alone. What you name the journal receiver doesn't matter. In the following example, I'll call it JOBACG0001:
CRTJRNRCV JRNRCV(xxx/JOBACG0001) + TEXT('Job Accounting + journal receiver')
2. Create the QACGJRN journal in QSYS. You don't have a choice here. You must use the name QACGJRN and you must place it in QSYS:
CRTJRN JRN(QSYS/QACGJRN) + JRNRCV(xxx/JOBACG0001) + TEXT('Job Accounting + journal')
3. Change the system value QACGLVL to activate Job Accounting. If you want to keep track of CPU usage, communication line usage and so forth, use the value *JOB. If you want to keep track of printer usage, use *PRINT. If you want both, enter both values:
CHGSYSVAL SYSVAL(QACGLVL) + VALUE('*JOB') or VALUE('*JOB *PRINT') or VALUE('*PRINT')
You may prefer using the Work System Values (WRKSYSVAL) command, since it presents a handy data entry screen which is easier to work with than CHGSYSVAL. In either case, job accounting is now active. Let it run for a while.
Although nothing stops you from logging other activities in the QACGJRN journal, you should reserve it for job accounting. This eliminates confusion and keeps your journal receivers as free from "contamination" as possible.
Preparing Reports
Job accounting would be useless if you couldn't print reports based on the data gathered in the journal receiver. The trouble is that HLL programs like RPG/400 cannot read the information in a journal receiver (they're not files, after all). Therefore, you need to download the journal receiver into a database file.
Your first step is to create database files capable of holding that information. Fortunately, QSYS has two model files you can use: one for *JOB data and the other for *PRINT data. You can create your own database files, based on the two model files, in any library you choose-for example, the same library in which you placed the journal receiver.
Figures 2a and 2b show you the DDS for files JOBACGJB (for *JOB data) and JOBACGPR (for *PRINT data). The DDS is only one line long, using the FORMAT keyword to copy the record format from the model file as is (all fields included). The resulting record layouts are shown in Figures 3a and 3b. As you can see, there's information aplenty.
You can now create logical files based on JOBACGJB and JOBACGPR, using JACDE (accounting code) as the primary key field; this would let you read the information sequentially by accounting code and thus obtain subtotals at L1 time. You can create other logical files to suit your own needs. Analyze the information contained in the database files and you're sure to come up with many useful ideas.
After you have created the physical files (and the optional logical files as you see fit), you can create HLL programs that will process the files to create printed reports. And these reports are the evidence you need to convince Finance that the AS/400 is not just an expensive toy you bought for your own enjoyment.
Downloading the Journal Receiver
To download the information from the journal receiver into the physical files, run the following commands. The first one downloads to JOBACGJB; the second to JOBACGPR:
DSPJRN JRN(QSYS/QACGJRN) + JRNCDE(A) + ENTTYP(JB) + OUTPUT(*OUTFILE) + OUTFILE(xxx/JOBACGJB) DSPJRN JRN(QSYS/QACGJRN) + JRNCDE(A) + ENTTYP(SP DP) + OUTPUT(*OUTFILE) + OUTFILE(xxx/JOBACGPR)
The JRNCDE(A) parameter is very important. As mentioned earlier, anyone can run the SNDJRNE command to write a journal entry into the journal receiver. However, these entries have a code of U instead of A. By specifying JRNCDE(A), you filter out everything but the job accounting-related entries.
ENTTYP specifies the type of entry. JB is the proper type for *JOB entries. SP and DP are for *PRINT entries; the difference is that SP is used for spooled printouts while DP is used for direct (non-spooled) printouts. If you always spool every printout and you're sure everyone else will always spool every printout, you don't have to include DP in ENTTYP.
After you run these commands, files JOBACGJB and JOBACGPR are loaded with all the entries contained in the journal receivers (which still contain all their data-they haven't been emptied or anything). You can now run your HLL programs to print whatever reports you may have designed.
The sidebar "The RPTJOBACG Command" (page 28) illustrates a simple job accounting summary report, which gives totals for each user, within every accounting code.
A Few Observations
Before finishing up, make sure you understand the following points:
Job accounting only provides raw data, such as how many milliseconds of CPU time have been used, or how many pages of printer paper. It's up to you to use this information in a meaningful manner.
Job accounting uses journals and journal receivers. This means that you have to maintain the journal periodically, changing the journal receiver. For example, you can keep the journal receiver for a fixed period of time, such as a week or a month.
At the end of each job accounting period, you should run the Change Journal (CHGJRN) command to detach the current journal receiver and attach a new, empty one:
CHGJRN JRN(QSYS/QACGJRN) + JRNRCV(*GEN)
Job accounting is, for many AS/400 administrators, a brand-new concept. I hope this article has dispelled some of the fears, concerns or misconceptions that surround it, so you can take full advantage of this incredibly useful, free feature. It's one of the best bargains in OS/400!
The RPTJOBACG Command
The article has explained how to get Job Accounting up and running, and has even provided some pointers as to what to do with the raw data contained in the journal receiver. Now you can build upon that foundation and create a Job Accounting Summary Report, which will list total usage information for each user and summarize by accounting code. 4 shows the finished report.
The article has explained how to get Job Accounting up and running, and has even provided some pointers as to what to do with the raw data contained in the journal receiver. Now you can build upon that foundation and create a Job Accounting Summary Report, which will list total usage information for each user and summarize by accounting code. Figure 4 shows the finished report.
The command we've used to start the reporting activity is called Report Job Accounting (RPTJOBACG), with the word "report" used as a verb. The command (which has no parameters) is listed in 5a.
The command we've used to start the reporting activity is called Report Job Accounting (RPTJOBACG), with the word "report" used as a verb. The command (which has no parameters) is listed in Figure 5a.
When you run RPTJOBACG, program JOB005CL (5b) kicks in, submitting itself to batch. Once in batch, it runs the DSPJRN command twice: once to download the *JOB information into file JOBACGJB, then to download *PRINT information into file JOBACGPR. Then it calls RPG program JOB005RG, which actually prints the report.
When you run RPTJOBACG, program JOB005CL (Figure 5b) kicks in, submitting itself to batch. Once in batch, it runs the DSPJRN command twice: once to download the *JOB information into file JOBACGJB, then to download *PRINT information into file JOBACGPR. Then it calls RPG program JOB005RG, which actually prints the report.
As 5c illustrates, we need to create a logical file over both JOBACGJB and JOBACGPR. This logical file (JOB005LF) will have two very different record formats, and is keyed by accounting code and user profile name. RPG program JOB005RG uses this logical file as its primary input file, reading the records sequentially by key; this means that all records for an accounting code/user profile combo will be processed together, no matter what physical file they come from.
As Figure 5c illustrates, we need to create a logical file over both JOBACGJB and JOBACGPR. This logical file (JOB005LF) will have two very different record formats, and is keyed by accounting code and user profile name. RPG program JOB005RG uses this logical file as its primary input file, reading the records sequentially by key; this means that all records for an accounting code/user profile combo will be processed together, no matter what physical file they come from.
Next, 5d shows our externally described printer file, JOB005P1. There's nothing terribly complicated about it; it defines fields by referencing either QAJBACG or QAPTACG, the two original model files in QSYS.
Next, Figure 5d shows our externally described printer file, JOB005P1. There's nothing terribly complicated about it; it defines fields by referencing either QAJBACG or QAPTACG, the two original model files in QSYS.
Finally, 5e contains RPG program JOB005RG. JOB005RG reads the logical file one record at a time. Since this logical file contains two widely different record formats, JOB005RG uses subfield *RECORD from the INFDS to find out what record format has been read (and therefore, whether the data just read contains *JOB or *PRINT information). Consequently, JOB005RG accumulates one set of L1xxx variables or another. The rest of the program is pretty straightforward.
Finally, Figure 5e contains RPG program JOB005RG. JOB005RG reads the logical file one record at a time. Since this logical file contains two widely different record formats, JOB005RG uses subfield *RECORD from the INFDS to find out what record format has been read (and therefore, whether the data just read contains *JOB or *PRINT information). Consequently, JOB005RG accumulates one set of L1xxx variables or another. The rest of the program is pretty straightforward.
Job Accounting-OS/400's Built-in Watchdog
Figure 1A Command USEACGCDE
USEACGCDE: CMD PROMPT('Use Accounting Code') PARM KWD(ACGCDE) TYPE(*CHAR) LEN(15) DFT(*SAME) + SPCVAL((*SAME) (*BLANK)) EXPR(*YES) + PROMPT('Accounting code')
Job Accounting-OS/400's Built-in Watchdog
Figure 1B CL program ACG001CL
ACG001CL: PGM PARM(&ACGCDE) DCL VAR(&ACGCDE) TYPE(*CHAR) LEN(15) CHGACGCDE JOB(*) ACGCDE(&ACGCDE) ENDPGM
Job Accounting-OS/400's Built-in Watchdog
Figure 2A Physical file JOBACGJB
A R QWTJAJBE FORMAT(QSYS/QAJBACG)
Job Accounting-OS/400's Built-in Watchdog
Figure 2B Physical file JOBACGPR
A R QSPJAPTE FORMAT(QSYS/QAPTACG)
Job Accounting-OS/400's Built-in Watchdog
Figure 3A Record layout for JOBACGPR
Figure 3a: Record Layout for JOBACGPR Field Size Description JAENTL 5,0 Length of entry JASEQN 10,0 Sequence number JACODE 1 Journal code (always 'A' for Job Accounting) JAENTT 2 Entry type (always 'JB' for *JOB data) JADATE 6 Date of entry JATIME 6 Time of entry JARES 95 (Reserved) JAJOB 10 Job name JAUSER 10 User profile name JANBR 6,0 Job number JACDE 15 Accounting code JADFN 10 Printer device file name JADFNL 10 Printer device file library name JADEVN 10 Printer device name JADEVT 4 Printer device type (such as 4245) JADEVM 4 Printer device model (such as T20) JATPAG 11,0 Total number of pages JATLIN 11,0 Total number of lines JASPFN 10 Spool file name JASPNB 4 Spool file number JAOPTY 1 Output priority JAFMTP 10 Forms type JABYTE 15,0 Total number of bytes JAUSRD 10 User data
Job Accounting-OS/400's Built-in Watchdog
Figure 3B Record layout for JOBACGJB
Figure 3b: Record Layout for JOBACGJB Field Size Description JAENTL 5,0 Length of entry JASEQN 10,0 Sequence number JACODE 1 Journal code (always 'A' for Job Accounting) JAENTT 2 Entry type (always 'JB' for *JOB data) JADATE 6 Date of entry JATIME 6 Time of entry JARES 95 (Reserved) JAJOB 10 Job name JAUSER 10 User profile name JANBR 6,0 Job number JACDE 15 Accounting code JACPU 11,0 CPU time used (in milliseconds). It doesn't include time used to produce job log. JARTGS 5,0 Number of routing steps JAEDTE 6 Date job entered the system (MMDDYY) JAETME 6 Time job entered the system (HHMMSS) JASDTE 6 Date job started (MMDDYY) JASTME 6 Time job started (HHMMSS) JATRNT 11,0 Total transaction time (milliseconds) JATRNS 11,0 Number of transactions JAAUX 11,0 Auxiliary I/O operations JATYPE 1 Job type: A=Autostart B=Batch/Communications/MRT I=Interactive M=subsystem Monitor R=spool Reader W=spool Writer JACCDE 3,0 Job completion code: 000=Normal 010=Normal during ENDJOB/ENDSBS OPTION(*CNTRLD) 020=Exceeded end severity 030=Abnormal 040=Ended before becoming active 050=Ended while active 060=Subsystem ended abnormally while job active 070=System ended abnormally while job active 080=Completed in time limit 090=Forced to complete after time limit ended 099=Entry caused by CHGACGCDE command JALINE 11,0 Number of lines printed, as written by program. It doesn't include job log. JAPAGE 11,0 Number of pages printed JAPRTF 11,0 Number of print files produced JADBPT 11,0 Number of database write operations JADBGT 11,0 Number of database read operations JADBUP 11,0 Number of database update, delete, force end-of-file, release, commit and rollback operations JACMPT 11,0 Number of communications write operations JACMGT 11,0 Number of communcations read operations JAACT 11,0 Total time job was active (in milliseconds) JASPN 11,0 Total time job was suspended (in milliseconds) Note: JADBPT, JADBGT and JADBUP don't include I/O operations to writers and readers, I/O operations performed by CPYSPLF, DSPSPLF or WRKSPLF. Further, if SEQONLY(*YES) is specified, the number recorded is the number of blocks-not records. Note: JACMPT and JACMGT don't reflect remote workstation activities. If the I/O is for a communications device, the number recorded only reflects activity related to ICF files.
Job Accounting-OS/400's Built-in Watchdog
Figure 4 Sample report, unable to display graphic
Job Accounting-OS/400's Built-in Watchdog
Figure 5A Command RPTJOBACG
RPTJOBACG: CMD PROMPT('Report Job Accounting')
Job Accounting-OS/400's Built-in Watchdog
Figure 5B CL program JOB005CL
JOB005CL: + PGM DCL VAR(&JOBTYPE) TYPE(*CHAR) LEN(1) DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(132) DCL VAR(&MSGF) TYPE(*CHAR) LEN(10) DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10) DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR)) RTVJOBA TYPE(&JOBTYPE) IF COND(&JOBTYPE *EQ '0') THEN(GOTO CMDLBL(EXEC)) ELSE CMD(DO) SBMJOB CMD(RPTJOBACG) JOB(RPTJOBACG) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Command + RPTJOBACG submitted to batch') MSGTYPE(*COMP) RETURN ENDDO EXEC: + DSPJRN JRN(QSYS/QACGJRN) JRNCDE(A) ENTTYP(JB) OUTPUT(*OUTFILE) + OUTFILE(JOBACGJB) OUTMBR(*FIRST *REPLACE) DSPJRN JRN(QSYS/QACGJRN) JRNCDE(A) ENTTYP(DP SP) + OUTPUT(*OUTFILE) OUTFILE(JOBACGPR) OUTMBR(*FIRST *REPLACE) CALL PGM(JOB005RG) RETURN ERROR: + RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID) MSGF(&MSGF) + MSGFLIB(&MSGFLIB) SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) MSGDTA(&MSGDTA) + MSGTYPE(*ESCAPE) ENDPGM
Job Accounting-OS/400's Built-in Watchdog
Figure 5C Logical file JOB005LF
A R QWTJAJBE PFILE(JOBACGJB) A K JACDE A K JAUSER * A R QSPJAPTE PFILE(JOBACGPR) A K JACDE A K JAUSER
Job Accounting-OS/400's Built-in Watchdog
Figure 5D Printer file JOB005P1
A R HEADER SKIPB(2) A 1DATE EDTCDE(Y) A 11TIME A 52'Job Accounting Summary Report' A 112'RPTJOBACG - Page' A 129PAGNBR EDTCDE(3) A SPACEA(2) * A R NEWCDE SPACEB(1) A 1'----------------------------------- A ------------------------------------ A ------------------------------------ A ----------------------------' A SPACEA(1) A 1'Accounting Code:' A JACDE R O 19REFFLD(JACDE QAJBACG) A SPACEA(2) A 20'*------------------------ CPU Usag- A e --------------------------*' A 88'*----------- Printer Usage -------- A ------*' A SPACEA(1) A 4'User Profile' A 21'Usage (ms)' A 35'Nbr Trans' A 47'Trans Time' A 62'DASD I/O' A 75'Comm I/O' A 94'Pages' A 107'Lines' A 124'Bytes' A SPACEA(1) * A R DETAIL SPACEA(1) A JAUSER R O 4REFFLD(JAUSER QAJBACG) A L1CPU R O 20REFFLD(JACPU QAJBACG) A EDTCDE(3) A L1TRNS R O 33REFFLD(JATRNS QAJBACG) A EDTCDE(3) A L1TRNT R O 46REFFLD(JATRNT QAJBACG) A EDTCDE(3) A L1AUX R O 59REFFLD(JAAUX QAJBACG) A EDTCDE(3) A L1COMM R O 72REFFLD(JACMPT QAJBACG) A EDTCDE(3) A L1TPAG R O 88REFFLD(JATPAG QAPTACG) A EDTCDE(3) A L1TLIN R O 101REFFLD(JATLIN QAPTACG) A EDTCDE(3) A L1BYTE R O 114REFFLD(JABYTE QAPTACG) A EDTCDE(3)
Job Accounting-OS/400's Built-in Watchdog
Figure 5E RPG program JOB005RG
FJOB005LFIP E K DISK F KINFDS INFDS FJOB005P1O E 99 PRINTER * IQWTJAJBE I JACDE L2 I JAUSERL1 IQSPJAPTE I JACDE L2 I JAUSERL1 * IINFDS DS I *RECORD RCDNAM * C *IN99 IFEQ *ON C WRITEHEADER C MOVE *OFF *IN99 C ENDIF * C *INL2 IFEQ *ON C WRITENEWCDE C ENDIF * C RCDNAM IFEQ 'QWTJAJBE' *JOB DATA C ADD JACPU L1CPU C ADD JATRNT L1TRNT C ADD JATRNS L1TRNS C ADD JAAUX L1AUX C ADD JACMPT L1COMM C ADD JACMGT L1COMM C ELSE *PRINT DATA C ADD JATPAG L1TPAG C ADD JATLIN L1TLIN C ADD JABYTE L1BYTE C ENDIF * CL1 EXSR EXECL1 * C *INZSR BEGSR C WRITEHEADER C ENDSR * C EXECL1 BEGSR C WRITEDETAIL C CLEARDETAIL C ENDSR
LATEST COMMENTS
MC Press Online