“Self-Monitoring” Batch-Job


Introduction

Hello fellow ABAPers,

have you ever encountered the situation where it might be helpful to monitor running batch jobs via ABAP-Reports (e.g. to get noticed automatically via email when a job fails)?

I want to share some basic ideas/snippets on this topic with the following blog post.

Snippets

It is quite “simple” to retrieve the status of a batch-job via function-module. FM

CALL FUNCTION 'BP_JOB_STATUS_GET' EXPORTING jobcount = p_jbcnt " Kennummer eines Jobs jobname = p_jbnam " Name eines Hintergrundjobs IMPORTING status = job_status " Zustand eines Batchjobs
...

takes the corresponding job name + job count (ID) as input-parameter and returns the status of the job.

The problem is that the job-ID is unknown in advance. So it is not possible to predefine a report variant which passes the job name/job count to this function module.

But with the help of a Template-Job, which is making use of selection-variables (tvarvc), this lack of information can be overcome in a simple way (at least in my opinion :)).

Template of Job-Monitor

This example of a template for the job monitor is quite simple.

It consists of a single step (Z-Report) which only reads two selection-screen parameters and checks the status of the corresponding batch job in a while loop (until the job is finished or fails).

Selection-Screen%20of%20Job-Monitor

Selection-Screen of Job-Monitor

 WHILE job_status <> zcl_job_base=>btc_finished AND job_status <> zcl_job_base=>btc_aborted. CALL FUNCTION 'BP_JOB_STATUS_GET' EXPORTING jobcount = p_jbcnt " Kennummer eines Jobs jobname = p_jbnam " Name eines Hintergrundjobs IMPORTING status = job_status " Zustand eines Batchjobs EXCEPTIONS job_doesnt_exist = 1 unknown_error = 2 parent_child_inconsistency = 3 OTHERS = 4. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDIF. IF job_status = zcl_job_base=>btc_finished. "or write an email... WRITE:/ |job: { p_jbnam } with Job-ID { p_jbcnt } finished| COLOR COL_POSITIVE. ELSEIF job_status = zcl_job_base=>btc_aborted. "or write an email... WRITE:/ |job: { p_jbnam } with Job-ID { p_jbcnt } aborted| COLOR COL_NEGATIVE. ELSE. WAIT UP TO p_wait SECONDS. ENDIF. ENDWHILE.

The corresponding report variant would look like this (It is important to reference Selection-Variables)

Variant%20with%20Selection-Variables

Variant with Selection-Variables

Finally the step list:

Job with “Automatic” Monitoring

A second Z-Report is needed to make use of this Monitor-Template. The task of this report is to retrieve the current batch runtime info (job name/job count) and transfer this information to the previously defined selection-variables. The template job is then copied and released immediately. The result is an independent running monitoring job (of course, the monitoring job could fail as well (seems to be unlikely), but it is not affected by the result of the job which is being monitored). This report needs to run as the first step in order to implement “real-time” monitoring (before any other step is executed). The selection screen of this report looks like this:

First two parameters must match the name of the selection variables defined in the variant of the monitoring report. The third and fourth parameter are used to find and copy our template.

SELECTION-SCREEN BEGIN OF BLOCK bl_sel_par WITH FRAME TITLE TEXT-001.
PARAMETERS: p_jcnt TYPE tvarvc-name DEFAULT 'ZMONITOR_JOBCOUNT' OBLIGATORY, p_jnam TYPE tvarvc-name DEFAULT 'ZMONITOR_JOBNAME' OBLIGATORY.
SELECTION-SCREEN END OF BLOCK bl_sel_par. SELECTION-SCREEN BEGIN OF BLOCK bl_sel_job WITH FRAME TITLE TEXT-002.
PARAMETERS: p_jmsrc TYPE btcjob DEFAULT 'ZJOB_MONITOR' OBLIGATORY MATCHCODE OBJECT z_sh_planned_jobs.
PARAMETERS: p_jmtgt TYPE btcjob DEFAULT 'ZJOB_MONITOR_RUN'.
SELECTION-SCREEN END OF BLOCK bl_sel_job. AT SELECTION-SCREEN ON p_jmsrc.
**********************************************************************
*** Check Copy Sources for Monitoring Job SELECT FROM tbtco AS job_def FIELDS COUNT( * ) AS cnt_cpy_srces WHERE job_def~jobname EQ @p_jmsrc AND job_def~status EQ @zcl_job_base=>btc_scheduled INTO @DATA(l_cnt). CASE l_cnt. WHEN 0. MESSAGE |Monitor-Job Copy Source not found!| TYPE 'E'. WHEN 1. WHEN OTHERS. MESSAGE |There are more than 1 Copy Sources| TYPE 'W'. ENDCASE. START-OF-SELECTION.
*** Jobname and Jobcount (->Runtime-Info) DATA: jobname TYPE tbtcm-jobname, jobcount TYPE tbtcm-jobcount. DATA: selpar_job_cnt TYPE REF TO zcl_sel_par, selpar_job_name TYPE REF TO zcl_sel_par. DATA: monitor_job TYPE REF TO zcl_job_mon. DATA: enqueue_errors TYPE i. CONSTANTS: max_enqueue_errors TYPE i VALUE 5.
********************************************************************** IF sy-batch IS NOT INITIAL. CALL FUNCTION 'GET_JOB_RUNTIME_INFO' IMPORTING jobcount = jobcount jobname = jobname EXCEPTIONS no_runtime_info = 1 OTHERS = 2. IF sy-subrc <> 0. MESSAGE |Could not obtain Runtime-Info| TYPE 'E'. ENDIF.
**********************************************************************
*** Jobcount and Jobname are transferred via Selection-Variables TRY. selpar_job_cnt = zcl_sel_var_factory=>create_selpar_with_e_lock( p_jcnt ). selpar_job_cnt->set_value( jobcount ). selpar_job_cnt->save_db( ). selpar_job_name = zcl_sel_var_factory=>create_selpar_with_e_lock( p_jnam ). selpar_job_name->set_value( jobname ). selpar_job_name->save_db( ). COMMIT WORK. CATCH zcx_enqueue_error INTO DATA(lo_err). " Kein Treffer bei DB-Abfrage enqueue_errors = enqueue_errors + 1. IF enqueue_errors <= max_enqueue_errors. WAIT UP TO 20 SECONDS. RETRY. ELSE. MESSAGE |Could not perform enqueue: { lo_err->get_text( ) }| TYPE 'E'. ENDIF. ENDTRY.
*** Copy Monitor-Job and release immediatly TRY. CREATE OBJECT monitor_job EXPORTING iv_job_src_name = p_jmsrc. "Copy Job to Default-Name IF p_jmtgt IS INITIAL. monitor_job->copy_to_target_job_dflt_name( ). ELSE. monitor_job->copy_to_target_job( iv_target_name = p_jmtgt iv_dialog = zcl_job_base=>z_btc_no ). ENDIF. monitor_job->release_target_job_now( ). WRITE:/ |Job-Monitor for Job-Name: { p_jmtgt } released|.
*** dequeue after monitor job is released TRY. TYPES: range_status TYPE RANGE OF c. WHILE monitor_job->get_state_of_target_job( ) IN VALUE range_status( ( option = 'EQ' sign = 'I' low = zcl_job_base=>btc_ready ) ( option = 'EQ' sign = 'I' low = zcl_job_base=>btc_released ) ). WAIT UP TO 15 SECONDS. ENDWHILE. CATCH zcx_job_error. " zcx_job_error ENDTRY. selpar_job_cnt->dequeue_tvarv( ). selpar_job_name->dequeue_tvarv( ). CATCH zcx_job_error. MESSAGE |Could not release target Job for Job-Monitor| TYPE 'E'. CATCH zcx_no_entry_found. " zcx_no_entry_found MESSAGE |Copy-Source for Job-Monitor not found| TYPE 'E'. ENDTRY. ENDIF.

The selection-variables are enqueued and released after the monitor job is active and running in order to prevent unwanted overwriting (which makes this report somehow “thread safe”).

Sample Job

Let’s put this all together. Consider the following step list of a job called ZJOB_WITH_MONITORING:

Steplist%20sample%20Job

Step list sample Job

The second step will make this job fail but let’s take a look after the first step is executed.

Release%20of%20Job%20with%20Monitoring

Release of Job with Monitoring

First%20step...

First step…

Template%20Job%20is%20copied

Template Job is copied

You can see that report ZSUBMIT_JOB_MONITOR has read the current runtime info and has transferred this data to the selection variables:

Selection%20variants%20for%20zjob_monitor

Selection variant for zjob_monitor

Runtime%20Info%20of%20ZJOB_WITH_MONITORING

Runtime Info of ZJOB_WITH_MONITORING

The job monitor now runs as an independent job which will not be affected by the failing of ZJOB_WITH_MONITORING as you can see in the next screenshot (the monitoring job could fail as well, but I don’t see any obvious reasons for this).

Failing%20job%20but%20finishing%20job%20monitor

Failing job but finishing job monitor

Result%20of%20monitoring

Result of monitoring

Conclusion

This simple example should demonstrate how “built-in” monitoring of batch jobs could be implemented with the help of a template job and usage of selection-variables (tvarvc).

There is only the need to include a step of report ZSUBMIT_JOB_MONITOR (first step) without having to define various variants for each job that should be monitored (of course, if more differentiation is needed depending on the job (or reaction on failing), there would be more variants, but if only some standard behavior is wanted – like sending an email “job xy failed at <<current_time>>” or raising events – this simple variant should be enough). Since enqueues are used to update the corresponding selection-variables (and only released after the monitoring job is running), this setup (same variables/variant) could be used by different jobs running/starting at the same time.

Coding snippets are available at github: (Please be aware that all BP-FMs are unreleased! The code is for demonstration only 🙂 )

Link to github

I hope you enjoyed reading this blog post (Sorry that some screenshots are partly in German, I wasn’t aware when I took them and was too lazy to do it again 🙂 ).

How do you implement job monitoring? I am quite interested in your answers!

Cheers,

Alex!