I know I have written a lot recently about SQL, but IBM has added a lot of great views that allow you to access information easily. A good example is today's post: how to get information from a job log using SQL. Prior to this if you need to get information from the job log you had to use the RCVMSG command or the "List Job Log Messages" API, QMHLJOBL. Now I can get the information I want just using a SQL Select statement.
The JOBLOG_INFO table function was added to IBM i 7.2 and 7.1 TR9. It returns one row for each job log message, and what I really like about it is I can choose what information to return by using a WHERE clause in the Select statement.
I can simply go into the Interactive SQL screen, STRSQL, and type to list the job log for my current job, and press Enter:
select * from table(qsys2.joblog_info('*')) a |
As I want the information for my current job I can use the asterisk (*) as I would do using the "Display Job" command, DSPJOB. I also have to include the "A" following the table part of the statement. If I omit it the statement will not execute.
The output looks like:
ORDIN00001 MESSAGE_ID MESSA00001 MESSA00002 SEVERITY 1 CPF1124 INFORMATIONAL 0 2 - REQUEST 0 3 - REQUEST 0 4 CPD0030 DIAGNOSTIC 30 5 CPF0001 DIAGNOSTIC 30 |
There are a lot more other columns to the left of these. I am not going to list them all as they can be found in IBM's documentation that I will give a link to at the bottom of this post.
Below are what I consider the most useful columns to select:
What is the kind of information I would want to know about a view?
Column | Description |
ORDINAL_POSITION | Unique number assigned to each row (job log entry). |
MESSAGE_ID | Id of the message received. |
MESSAGE_TYPE | Type of message. I will give more details in a separate table below. |
MESSAGE_SUBTYPE | If message type is NOTIFY or ESCAPE:
|
SEVERITY | Severity of the message. |
MESSAGE_TIMESTAMP | Timestamp when the message occurred. |
TO_LIBRARY | Library containing the program the received the message. |
TO_PROGRAM | Program or service program that received the message. |
TO_MODULE | Module that received the message. |
TO_PROCEDURE | Procedure that received the message. |
TO_INSTRUCTION | Instruction that received the message. |
MESSAGE_TEXT | First level message text. |
Some of the above fields are large VARCHAR fields that I use the CAST to make them smaller to fit on a display.
I know many people are unsure what the various message types are, so I will list them below with an explanation taken from IBM's documentation:
Message type | Description |
Completion (*COMP) | Reports the successful completion of a task. |
Diagnostic (*DIAG) | Describes errors in processes or input data. When an error occurs, a program usually sends an escape message, which causes the task to end abnormally. One or more diagnostic messages can be sent before the escape message to describe the error. |
Escape (*ESCAPE) | Indicates a condition causing a program to end abnormally, without completing its work. |
Exception (*EXCP) | Indicates a condition causing a program to end abnormally, without completing its work. An exception message can be either an escape or a notify message. The exception message type and the special value *EXCP are used only with the Receive Program Message (QMHRCVPM) API. |
Informational (*INFO) | Conveys information without asking for a reply. |
Inquiry (*INQ) | Conveys information and asks for a reply. |
Notify (*NOTIFY) | Describes a condition in the sending program requiring corrective action or a reply. |
Reply (*RPY) | Responds to an inquiry or notify message. |
Request (*RQS) | Requests a function from the receiving program. |
Sender's copy (*COPY) | Is a copy of an inquiry or notify message. This copy is kept by the sender of the inquiry or notify message. |
Scope (*SCOPE) | Specifies a program to run when the program this message is sent to completes. If the message is sent to *EXT the program is to run when the job completes. |
Status (*STATUS) | Describes the status of work being done by a program. |
Let me give a very simple example of how this can be used. Here is a CL program that I am going to run. The file DUMMY does not exist in QTEMP. The first attempt to delete it, line 2, will error and the "Monitor Message" command, MONMSG on line 3, will prevent the program from erroring. The second delete attempt, line 4, is not monitor and the program will error .
01 PGM 02 DLTF FILE(QTEMP/DUMMY) 03 MONMSG MSGID(CPF0000) 04 DLTF FILE(QTEMP/DUMMY) 05 ENDPGM |
I run the program, and answer the CPF2105 message with a "C".
Now I build a SQL Select statement only for the fields I want to display and where the severity of the error is greater or equal to 20. I have used the AS to give each column heading a meaningful name. I have also used the CAST to convert Program name, Procedure, and Message text from VARCHAR to fixed length character fields, and converted the CCSID for the Message text too.
select ordinal_position as seq, message_id as msg_id, message_type as msg_type, message_subtype as msg_subtype, severity, message_timestamp as timestamp, to_library as library, cast(to_program as char(10)) as program, to_module as moldule, cast(to_procedure as char(20)) as procedure to_instruction as instruction, cast(message_text as char(200) ccsid 37) as message_text from table(qsys2.joblog_info('*')) a where severity >= 20 |
When I execute the select this is what is displayed (I have removed all other log rows apart from those to do with this program for clarity). The first part shows the Ordinal position, Message id, Message type, Message subtype and the Timestamp:
SEQ MSG_ID MSG_TYPE MSG_SUBTYPE SEVERITY 21 CPF2105 ESCAPE EXCEPTION HANDLED 40 23 CPF2105 ESCAPE EXCEPTION HANDLED 40 24 CPA0702 INQUIRY 99 25 - REPLY VALIDITY CHECKED 99 26 CEE9901 ESCAPE EXCEPTION HANDLED 30 TIMESTAMP 2015-04-05-20.13.19.994456 2015-04-05-20.13.19.994569 2015-04-05-20.13.19.994816 2015-04-05-20.13.22.508821 2015-04-05-20.13.22.509147 |
The second part contains the columns to do with the program that caused the error with the Program's library, Program, Module, Procedure, and Instruction number.
LIBRARY PROGRAM MOLDULE PROCEDURE INSTRUCTION RPGPGM1 TESTCLLE TESTCLLE TESTCLLE 300 RPGPGM1 TESTCLLE TESTCLLE TESTCLLE 600 - *EXT - *N - *EXT - *N QSYS QUOCMD - 03B3 |
The last part displays the Message text for the messages:
MESSAGE_TEXT Object DUMMY in QTEMP type *FILE not found. Object DUMMY in QTEMP type *FILE not found. CPF2105 received by procedure TESTCLLE. (C D I R) C Application error. CPF2105 unmonitored by TESTCLLE at statement 0000000600, instruction X'0000'. |
My example has just been for my current job. If I wanted to use the log of another job I would simply use the following:
select * from table(qsys2.joblog_info('149289/jsmith/qpadev0099')) a |
The next time someone tells me that John Smith had an error an hour ago and answered it without telling anyone, I can use this approach to easily and quickly find what it was and see if it was something I need to worry about.
You can learn more about this on the IBM website:
This article was written for IBM i 7.2
Lots and lots of possibilities here. Like!!!
ReplyDeleteYou can always "print" the joblog into an *OUTFILE and than you can use SQL to access it.
ReplyDeleteYou could do. But why do those extra steps when you can use the method I describe above the get the data in one step.
DeleteHi Simon: We are still on 7.1. Is there any way that I can get the History Log without printing and searching. Something like your joblog_info. Thanks. Carlos carlosir@bellsouth.net
DeleteWhile running the sql "select * from table(qsys2/joblog_info('*')) a " I am getting error "JOBLOG_INFO in QSYS2 type *N not found". I am in V6R1M000. please advise.
ReplyDeleteAs I mentioned at the top of this post this table was added as part of IBM i 7.1 TR9 and 7.2
DeleteAs you are at 6.1 the table, as you have found, is not present on your server.
Is there a process we can do to get similar results if we are running on 6.1?
DeleteIf your IBM i is only at 6.1 you will have to use the DSPJOBLOG command.
DeleteThis looks useful, but what if I wanted to use it in a program or command to show a list of possible errors. We would need to be able to change the select statement on the fly, is this possible?
ReplyDeleteI think that you can do what you want if you create your SQL statement in a variable, and then use the EXECUTE IMMEDIATE to execute the statement.
DeleteFor example:
String = 'select * from table(qsys2.joblog_info(''*'')) a' ;
exec sql EXECUTE IMMEDIATE :String ;
Hi,
ReplyDeleteOn your second example, why can't I find the jobs triggered by other users?
The only job logs I can see when I use the "*" parameter are joblogs under my user id.
Am I missing anything?
This is probably due to the settings of your user profile.
DeleteDo you have *JOBCTL authority?
Missing ending coma:
ReplyDeletecast(to_procedure as char(20)) as procedure
Hello Simon,
ReplyDeleteThis way we can get to know the message/job log details of a perticular job... thats seriously a great one to know....
is there a way we can be able to reply that ( I mean other than conventional way )
Thanks,
Sandeep Gubba
I tried for a long time to try and work out how to do that. I even posted the question on IBM's forums. Alas, I did not receive a reply or find a way to do it.
DeleteThanks, that is useful info!! BTW, you are missing a combo in your view after the 'cast(to_procedure as char(20)) as procedure' statement.
ReplyDeleteIs there a way to get detailed RPG job log when the job is run in batch and completed?
ReplyDeleteI am not sure what you mean by "RPG job log", as a job log is just a job log, independent of any language.
DeleteOnce a job is completed, without a job log, the only way I know to get information about it is to go to the history.
Searching the history log using SQL
Hi Simon
ReplyDeleteIt's strange that this SQL table function is not applicable to batch job in *OUTQ status. I got the QRY2293 Query cannot be run. See lower level messages
My assumption would be that the job was not set to generate a job log, and as the job has finished there is no job log.
DeleteHow can you put this information in an array for further notifications or logging?
ReplyDeleteThis only work for active jobs, is there any way for job which completed.?
ReplyDeleteLook in the History log using HISTORY_LOG_INFO.
DeleteHow can I use this SQL with a variable for job info instead of '*'
ReplyDeleteBuild the statement in a variable. If you are using a Fetch you can just do what is described here.
DeleteI am running the sql statement inside in a submitted sqlrpgle program and it is not returning any data.
ReplyDeleteAny clue?
There could be many, many reasons.
ReplyDeleteCheck SQLCOD and SQLCA to get a clue as to what is going on.
If you used GET DIAGNOSTICS you would have everything you need at your finger tips.