
Another addition to the RPG programming language with IBM i 7.5 and 7.4 TR6 is an operation code that allows me to write to the current job's job log.
I have been using the SQL procedure LPRINTF to write to the job log since 2019. Now I have the ability to do so with native RPG.
The new operation code is called Send Message, SND-MSG, and is accompanied by two new optional Built in Functions, BiF: %MSG and %TARGET.
Just how simple it is to write to job log I will demonstrate below. Let me start with my first example program's source code:
01 **free 02 dcl-s Text varchar(50) inz('Second message') ; 03 snd-msg 'First message' ; 04 snd-msg Text ; 05 snd-msg *INFO 'Third message' ; |
Line 1: If I am not writing my RPG in totally free format then I am making it too complicated.
Line 2: I am defining a variable to contain the text I will be using for a message. Notice that I have define the variable as VARCHAR.
Line 3: This is the simplest form of this operation code. Here I am just writing an informational message to the job log where the message's text is a string.
Line 4: Here I writing another informational message using the text from the variable I defined.
Line 5: I could use the keyword *INFO to denote that this is an informational message. But this is SND-MSG's default message type. I have just put *INFO in upper case here so you would notice it. It can be entered in either case, or even mixed.
I am going to use the SQL JOBLOG_INFO table function to retrieve the messages from the job log. My SQL Select statement would be:
SELECT MESSAGE_TEXT,MESSAGE_TYPE,MESSAGE_ID FROM TABLE(QSYS2.JOBLOG_INFO('310968/SIMON/QPAD143458')) |
I am only interested in three columns from JOBOG_INFO:
- MESSAGE_TEXT: Text of the message
- MESSAGE_TYPE: Message type. INFORMATION, ESCAPE, etc.
- MESSAGE_ID: Message id for the generated message
The results for the above RPG snippet is:
MESSAGE_TEXT MESSAGE_TYPE MESSAGE_ID --------------- ------------- ---------- First message INFORMATIONAL <NULL> Second message INFORMATIONAL <NULL> Third message INFORMATIONAL <NULL> |
None of these messages have a message id as I generated them "on the fly".
I can use the %MSG BiF to send that message to the job log. Before I can show that I am going to need to have a message file, and a message within it I can use. I generated by own using the following CL commands:
01 CRTMSGF MSGF(MYLIB/MYMSGF) 02 ADDMSGD MSGID(MY00001) MSGF(MYMSGF) MSG('This is a message from my message file') SECLVL('This is a message a created for SND-MSG in my message file') |
Line 1: I created a brand-new message file in my library.
Line 2: I added a message to that message file.
I can check my message one of two ways. The first uses the Display Message Description command, DSPMSGD:
DSPMSGD RANGE(MY00001) MSGF(MYMSGF) |
Or I can use the SQL View MESSAGE_FILE_DATA. Here I would use:
SELECT MESSAGE_ID,MESSAGE_TEXT,SEVERITY FROM QSYS2.MESSAGE_FILE_DATA WHERE MESSAGE_FILE_LIBRARY = 'MYLIB' AND MESSAGE_FILE = 'MYMSGF' AND MESSAGE_ID = 'MY00001' ; |
Which returns to me:
MESSAGE_ID MESSAGE_TEXT SEVERITY ---------- -------------------------------------- -------- MY00001 This is a message from my message file 0 |
Back to my RPG source code, and the next three lines are:
06 snd-msg %MSG('MY00001' : 'MYMSGF') ; 07 snd-msg *INFO %MSG('MY00001' : 'MYMSGF') ; 08 snd-msg %MSG('CPF9898' : 'QCPFMSG' : 'Replacement text') ; |
Line 6: Writes the message I created in my message file to the job log as an informational message. Here I am only using the first two parameters of the BiF:
- Message id
- Message file name
Line 7: I could have inserted the *INFO to do the same, to be honest what is the point as this is the default?
Line 8: The message CPF9898 allows the use replacement text. The replacement text I want to write to the job log is the third parameter of the %MSG BiF.
In these three source lines I have used %MSG in upper case, I did so to allow draw it to your attention. I can be entered in lower or mixed case too.
When I use the SQL Select statement with the JOBLOG_INFO table function I receive the following results:
MESSAGE_TEXT MESSAGE_TYPE MESSAGE_ID -------------------------------------- ------------- ---------- This is a message from my message file INFORMATIONAL MY00001 This is a message from my message file INFORMATIONAL MY00001 Replacement text. INFORMATIONAL CPF9898 |
The other BiF I can use with SND-MSG is %TARGET. It must always be the last BiF used, following %MSG or the message text.
In its simplest form I can use %TARGET thus:
09 snd-msg 'Fourth message' %TARGET(*SELF) ; |
*SELF within the Target BIF sends the message to the procedure this is included within. %TARGET can contain other values when used to generate an Escape message. Here is my second example program's source code:
01 **free 02 ctl-opt dftactgrp(*no) ; 03 SubProc1() ; 04 *inlr = *on ; //---------------------- 05 dcl-proc SubProc1 ; 06 SubProc2() ; 07 end-proc ; //---------------------- 08 dcl-proc SubProc2 ; 09 SubProc3() ; 10 end-proc ; //---------------------- 11 dcl-proc SubProc3 ; 12 snd-msg *ESCAPE 'Escape message' %TARGET(*CALLER : 0) ; 13 end-proc ; |
The program contains three subprocedures, SubProc1, SubProc2, and SubProc3.
Lines 1 – 4: The main line of this RPG program just calls the first subprocedure.
Lines 5 – 7: The first subprocedure just calls the second procedure.
Lines 8 – 10: In turn the second subprocedure calls the third.
Lines 11 – 13: In the third subprocedure I send an Escape type message. Notice in the %TARGET I have *CALLER followed by zero. This means that the message is returned the previous step in the call stack, in this case the second procedure.
When I call this program, and using the JOBLOG_INFO table function, my results are:
MESSAGE_TEXT ----------------------------------------------------------------------------- Escape message. The call to SUBPROC1 ended in error (C G D F). C The call to SUBPROC1 ended in error (C G D F). C Application error. CPF9898 unmonitored by TESTPGM at statement 0000000900... Application error. CPF9898 unmonitored by TESTPGM at statement 0000000900... MESSAGE_TYPE MESSAGE_ID ------------- ---------- ESCAPE CPF9898 SENDER RNQ0202 REPLY <NULL> INQUIRY RNQ0202 REPLY <NULL> ESCAPE CEE9901 INFORMATIONAL CEE9901 |
It is important to notice the line number shown in the last two messages. This is the line number of where the third procedure was called, line 9.
I change the second parameter in the target to "1".
12 snd-msg *ESCAPE 'Escape message' %TARGET(*CALLER : 1) ; |
When I call the program again the last two message lines are:
MESSAGE_TEXT ----------------------------------------------------------------------------- Application error. CPF9898 unmonitored by TESTPGM at statement 0000000600... Application error. CPF9898 unmonitored by TESTPGM at statement 0000000600... |
Notice that the statement number is now 6. By incrementing the second parameter I have gone up one more position in the call stack to where the second procedure is called.
If I increase the second parameter again:
12 snd-msg *ESCAPE 'Escape message' %TARGET(*CALLER : 2) ; |
And I run the program again I receiving the following last two message lines:
MESSAGE_TEXT ----------------------------------------------------------------------------- Application error. CPF9898 unmonitored by TESTPGM at statement 0000000300... Application error. CPF9898 unmonitored by TESTPGM at statement 0000000300... |
I have "climbed up" one more step in the call stack and this is now referencing the call to the first subprocedure in the main line part of the program.
If I wanted to keep the message with the subprocedure it occurred within I would use the target with *SELF:
12 snd-msg *ESCAPE 'Escape message' %TARGET(*SELF) ; |
My results show the line number the message was issued by SND-MSG.
MESSAGE_TEXT ----------------------------------------------------------------------------- Application error. CPF9898 unmonitored by TESTPGM at statement 0000001200... Application error. CPF9898 unmonitored by TESTPGM at statement 0000001200... |
IMHO this is a useful addition to the RPG programming language. I can certainly see myself using SND-MSG to write informational messages to the job log, rather than using LPRINTF.
You can learn more about this from the IBM website:
This article was written for IBM i 7.5 and 7.4 TR6.
No comments:
Post a Comment
To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.
Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.