ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Generic error handling

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Generic error handling

    So I am trying to create a very generic error handling procedure so that at the very least every program in our shop has it. Right now it is just wrapping a main procedure in a monitor and if it errors pass the psds to a service program. The service program logs the error so that we can set up tickets to get them fixed. Simple enough except that with the sub procedure the line number in the psds reflects the procedure and not actually where the error was, is there a way to change that or a better way?

    Code:
    // header specifications
           /Include misjd/qcpysrc,copyhspec
    
           // include procedure definitions
           /Include misjd/qcpysrc,copyallprc
           /Include misjd/qcpysrc,copypsds
    
           Monitor;
             Main();
           On-Error;
             MiscP_GeneralError(pgmsds);
           EndMon;
    
           *Inlr = *On;
           Return;
    
           Dcl-Proc Main;
    
           Dcl-S result packed(1);
           dcl-s one packed(1) inz(1);
           dcl-s zero packed(1) inz(0);
    
           result = one / zero;
    
           End-Proc;

  • #2
    We have something similar, but we have a general error handling in each procedure/function (First C-Spec in a procedure/Function is MONITOR and the last statements are ON-ERROR - Handle Error or Logging and Send an Escape Message - EndMon.
    If an error occurs it is logged including the procedure/function where it occured.
    After loggig it is either handled or resent (using the QMHSNDPM API - wrapped itself in a procedure) to the caller procedure.
    In this way we keep track of, where the error occured and in which sequence the procedures were called (before handling the error or before the final crash)

    Birgitta

    Comment


    • #3
      So basically you would do the below. Then use the QMHSNDPM to push the error up if it is not handled. That was my next step to figure out how to keep an error from getting ignored and make sure it stops programs up stream depending on if it is an interactive job or batch job. An interactive job we just want to pop a simple message to the user saying there was an issue. And batch jobs we still want to pop up the normal halt as we have a monitoring job that looks for messages, emails the group, and lets us answer through email if we want.

      Code:
             // header specifications
             /Include misjd/qcpysrc,copyhspec
      
             // include procedure definitions
             /Include misjd/qcpysrc,copyallprc
             // copy psds
             /Include misjd/qcpysrc,copypsds
      
      
             Monitor;
               Main();
             On-Error;
               MiscP_GeneralError(pgmsds);
             EndMon;
      
             *inlr = *on;
             return;
      
             Dcl-Proc Main;
      
             Dcl-S result packed(1);
             dcl-s one packed(1) inz(1);
             dcl-s zero packed(1) inz(0);
      
             Monitor;
             result = one / zero;
             On-Error;
               MiscP_GeneralError(pgmsds);
             EndMon;
      
             End-Proc;

      Comment


      • #4
        I have been developing this when I have time, I have used the QMHSNDPM to send job log messages and QMHSNDBM to send break messages which is getting us closer to what we want though needs more work. The one request I had from my manager was the ability to send the regular system message when the program is in development. So in a sense remove the monitor/messaging handling automatically so that when someone is developing they see the normal message and are able to give a response. Not sure this is possible as once the monitor is tripped I don't think you can just send it back to where it was and let it continue.

        Comment


        • #5
          So I have built my procedures, I wrap everything in a monitor. If it is an interactive job I send a break message to the user and then send a program message. And if I am in batch I send a message to qsysopr and then a program message. The program messages are escapes so the logic does not continue, the issue I am running into is when I get to the top of the call it still errors out because I am sending that escape message. I feel like I am missing something obvious here.

          Comment


          • #6
            I'm not sure that I understand where you are stuck. *ESCAPE messages do cause an exception, so you should monitor for that unless you want the OS to handle them (causing your program to "crash")

            One I approach that I sometimes use (and like a lot) is to send an *ESCAPE message to my caller. The caller will, in turn, monitor for any exceptions and re-send them to it's caller, etc. So if an exception occurs in a service program, it gets sent back to the program that used it. That program monitors for, and re-sends the escape message to the program that called it, etc... this percolates all the way back up the call stack until it gets to something that I didn't write (such as an OS screen, like a menu) where the OS says the program ended in error.

            Obviously, that is for "unexpected" errors. For expected errors, the program just handles them and continues on it's merry way.

            You say that yours is failing when you get to the "top of the call stack"... but the top of the call stack should be an OS program, right? So in a batch job, the job would go to MSGW. In an interactive program, it would show the error at the bottom of the screen saying that it failed. If that's not working, it sounds like you're not percolating the message all the way to the OS program?

            Unfortunately, the English language is not very precise. It'd be much easier to understand if you showed us exactly how to reproduce the problem you're stuck on.

            Comment


            • #7
              I think I got it now, I was not putting the correct number into the call stack counter and that was causing it to blow up when sending escape message instead of displaying it once it got back to the OS. Either way here is some code if anyone has any other suggestions, so far it seems to be working well in my tests. The goal was just to provide some very easy to use code and guidelines on new programming so that at the very least we don't splash up system messages to our users. So far it does it and does not let the programs continue since our users would just give a C anyway.

              Code:
              // header specifications
                     /Include misjd/qcpysrc,copyhspec
              
                     // include procedure definitions
                     /Include misjd/qcpysrc,copyallprc
                     // copy psds
                     /Include misjd/qcpysrc,copypsds
              
              
                     Monitor;
                       Main();
                       MiscP_SndMsg('prctest3 continued');
                     On-Error;
                       MiscP_GeneralError(pgmsds);
                     EndMon;
              
                     *inlr = *on;
                     return;
              
                     Dcl-Proc Main;
              
                     Dcl-S result packed(1);
                     dcl-s one packed(1) inz(1);
                     dcl-s zero packed(1) inz(0);
              
                     Monitor;
                     result = one / zero;
                     On-Error;
                       MiscP_GeneralError(pgmsds);
                     EndMon;
              
                       MiscP_SndMsg('prctest3 main continued');
              
                     End-Proc;
              Service Program code.

              Code:
              // Procedure: MiscP_GeneralError
                     // Created by: JJ Dahlheimer
                     // Purpose: Logs general error while hiding error from user
                     Dcl-Proc MiscP_GeneralError Export;
                       Dcl-Pi *n;
                         psds likeds(pgmsds) const;
                       End-Pi;
              
                       Dcl-S outMsg char(50);
                       Dcl-S outCause char(80);
                       Dcl-S today packed(7);
                       Dcl-S time packed(6);
              
                       today = %Dec(%Date():*CYMD);
                       time  = %Dec(%Time());
              
              
                       outCause = psds.EXCP_DATA;
                       outMsg = %Trim(psds.proc_name) + ' Line ' + psds.line_num +
                                ' Msg ' + psds.excp_msgid;
              
                       //Skip if cpf9897 exception already logged
                       If psds.excp_msgid <> 'CPF9897';
                         Exec Sql
                           Insert Into Misjd/Joberrf01(Jname,
                                                       Jnbr,
                                                       Uname,
                                                       Edate,
                                                       Etime,
                                                       Msg,
                                                       Cause)
                             Values (:Psds.Job_Name,
                                     :Psds.Job_No,
                                     :Psds.Curr_User,
                                     :Today,
                                     :Time,
                                     :Outmsg,
                                     :Outcause);
                       EndIf;
              
                       If MiscP_GetJobType() = 'I';
                         If psds.excp_msgid <> 'CPF9897';
                           MiscP_SndBrkMsg('There was an issue, check your output. Error has +
                                        been logged, if it continues plesase submit a Jira.');
                         EndIf;
                         MiscP_SndPgmMsg(outMsg + ' ' + outCause:'*ESCAPE');
                       Else;
                         If psds.excp_msgid <> 'CPF9897';
                           MiscP_SndMsg(outMsg + ' ' + outCause);
                         EndIf;
                         MiscP_SndPgmMsg(outMsg + ' ' + outCause:'*ESCAPE');
                       EndIf;
              
                       Return;
                     end-proc;
              
                     // Procedure: MiscP_SndPgmMsg
                     // Created by: JJ Dahlheimer
                     // Purpose: Sends program message
                     Dcl-Proc MiscP_SndPgmMsg Export;
                       Dcl-Pi *n;
                         msg varchar(32000) Const;
                         msgType Char(10) Const options(*nopass);
                       End-Pi;
              
                       Dcl-Ds ErrorCode;
                         BytesProv int(10) inz;
                         BytesAvail int(10) inz;
                       End-Ds;
              
                       Dcl-Pr QMHSNDPM ExtPgm('QMHSNDPM');
                         MessageID char(7) Const;
                         QualMsgF char(20) Const;
                         MsgData char(32000) Const options(*varsize);
                         MsgDtaLen int(10) Const;
                         MsgType Char(10) Const;
                         CallStkEnt Char(10) Const;
                         CallStkCnt Int(10) Const;
                         MessageKey Char(4);
                         ErrCode likeds(ErrorCode);
                       End-Pr;
              
                       Dcl-S MsgKey Char(4);
                       Dcl-S inMsgType char(10);
              
                       If %Parms > 1;
                         inMsgType = msgType;
                       Else;
                         inMsgType = '*DIAG';
                       EndIf;
              
                       QMHSNDPM( 'CPF9897'
                                  : 'QCPFMSG   *LIBL'
                                  : msg
                                  : %len(msg)
                                  : inMsgType
                                  : '*'
                                  : 3
                                  : MsgKey
                                  : ErrorCode );
              
                     End-Proc;
              
                     // Procedure: MiscP_SndBrkMsg
                     // Created by: JJ Dahlheimer
                     // Purpose: Sends Break message
                     Dcl-Proc MiscP_SndBrkMsg Export;
                       Dcl-Pi *n;
                         msg varchar(32000) Const;
                       End-Pi;
              
                       Dcl-Ds ErrorCode;
                         BytesProv int(10) inz;
                         BytesAvail int(10) inz;
                       End-Ds;
              
                       Dcl-Pr QMHSNDBM ExtPgm('QMHSNDBM');
                         MsgData char(32000) Const options(*varsize);
                         MsgDtaLen int(10) Const;
                         MsgType Char(10) Const;
                         MsgQueue Char(20) Const;
                         MsgNum Int(10) Const;
                         RcvQueue Char(20) Const;
                         ErrCode likeds(ErrorCode);
                       End-Pr;
              
                       QMHSNDBM(msg
                                : %len(msg)
                                : '*INFO'
                                : JOB_NAME+'QSYS'
                                : 1
                                : JOB_NAME+'QSYS'
                                : ErrorCode );
              
                     End-Proc;
              
              // Procedure: MiscP_SndMsg
                     // Created by: JJ Dahlheimer
                     // Purpose: Sends message
                     Dcl-Proc MiscP_SndMsg Export;
                       Dcl-Pi *n;
                         msg varchar(32000) Const;
                         msgType Char(10) Const options(*nopass);
                       End-Pi;
              
                       Dcl-Ds ErrorCode;
                         BytesProv int(10) inz;
                         BytesAvail int(10) inz;
                       End-Ds;
              
                       Dcl-Pr QMHSNDM ExtPgm('QMHSNDM');
                         MessageID char(7) Const;
                         QualMsgF char(20) Const;
                         MsgData char(32000) Const options(*varsize);
                         MsgDtaLen int(10) Const;
                         MsgType Char(10) Const;
                         MsgQueues char(20) const dim(50) options(*varsize);
                         NumQueues Int(10) Const;
                         RpyQueue Char(20) Const;
                         MsgKey char(4);
                         ErrorCodeDs likeds(ErrorCode);
                         CCSID Int(10) Const options(*nopass);
                       End-Pr;
              
                       Dcl-S MsgKey Char(4);
                       Dcl-S inMsgType char(10);
              
                       If %Parms > 1;
                         inMsgType = msgType;
                       Else;
                         inMsgType = '*INFO';
                       EndIf;
              
                       QMHSNDM( 'CPF9897'
                                  : 'QCPFMSG   *LIBL'
                                  : msg
                                  : %len(msg)
                                  : inMsgType
                                  : 'QSYSOPR   *LIBL     '
                                  : 1
                                  : ''
                                  : MsgKey
                                  : ErrorCode );
              
                     End-Proc;

              Comment


              • #8
                Originally posted by jj_dahlheimer View Post
                The one request I had from my manager was the ability to send the regular system message when the program is in development. So in a sense remove the monitor/messaging handling automatically so that when someone is developing they see the normal message and are able to give a response. Not sure this is possible as once the monitor is tripped I don't think you can just send it back to where it was and let it continue.
                I don't know of a way make it go back to the statement following the one that caused the error, JJ. The only way I know of to run it one way in production & another in test is to use compiler directives to condition the monitor message. Something like this:

                Code:
                /define EnableMonitoring
                
                /if defined(EnableMonitoring)
                Monitor;
                /endif
                   Main();
                /if defined(EnableMonitoring)
                On-Error;
                   MiscP_GeneralError(pgmsds);
                EndMon;
                /endif
                I prefer this method to commenting & uncommenting the error-handling code.

                Comment


                • #9
                  I will keep that, for the moment everyone seems good as all the complete messages get written to the job log so as soon as a programmer gets a break message they can just display that. I think what he was after was the ability to dump but that can be added easy enough to the monitor block if necessary. Thanks.

                  Comment

                  Working...
                  X