An another ABAP logging class allowing programs to focus on their functionality rather than being buried under lines of logging code.
Link to download latest versions :
- Latest : v0.1.2 (Latest)
- Latest ABAP 7.30 : v0.1.1 for ABAP 7.30 (Latest)
- Summary
- Features Overview
- Introduction : Genesis of this class
- Installation
- Getting Start
- Detailed documentation
- Logging messages in own internal table type :
- Next to statement
MESSAGES
. - System message stored in global structure
SY
. - Standard BAPI return structure or table (eg
BAPIRET2
). - Custom return structure or table.
- A free message text.
- A message using a message class.
- Next to statement
- Logging messages in Application Log (TCODE :
SLG1
) . - Displaying logs in the report :
- In an ALV grid from your own log table.
- The method can be used to display any kind of internal table.
- In the same presentation of
SLG1
from Application Log.
- In an ALV grid from your own log table.
- Managing logs between foreground and background execution (batch) :
- For batch mode, you can easily set which message type must be displayed in the spool and in the protocol in an independent way.
- The best for the end, overloading logs messages using provided settings table
or your own one :
- Update messages components on the fly according to the rules in settings table.
- Skip messages.
- Appending an extra message.
- Messages can be uniquely identified by using a Spot ID in your program.
As part of the development of ABAP interfaces program executed by batch, we have been confronted several times with subjects around error logs. First, we had problems with their display between the area of ​​the job execution protocol and those to display in the spool to send an email. Later we had a request to handle standard BAPI error messages being significant as false positive. Finally for an advanced follow-up for possible anomaly analysis in production, the use of the application can be a precious help.
All of its subjects are at the very close ABAP level (message management) but the implementation varies greatly depending on the nature of the subject. The reuse of codes is very complex because of its processive implementation.
The need to develop a class designed to cover all needs,
without modifying the way of logging into the program and leaving the core of
the program readable has become evident.
It is for this reason that I decided to develop the ZCL_LOG_UTIL
class.
It is intended to be easy to use (minimum configuration)
while offering a range of functions (requires more configuration,
but always wants to be as simple as possible).
Due to this complexity,
please find detailed documentation of the class and its use.
- Install this project via ABAPGit.
- Create Application Log object in transaction code
SLG0
:- Object :
ZLOGUTIL
(Main default object for entries registred with ZCL_LOG_UTIL
).
- Object :
- Run the report :
ZCL_LOG_UTIL_INSTALL
to fill the default tableZLOG_UTIL_OVERLO
of the class for example report. - The table maintenance view must be created manually thanks to transaction
SE11
(See: Table Maintenance Generator)
Now you're ready to get started.
The ZCL_LOG_UTIL
project comes with an example program that contains and
uses all of the functionality offered by the class.
Its goal is to allow all users to have an example of a precise use,
method call or implementation of a feature for their projects.
There is nothing worse than having the method without understanding how to use
it.
First follow the guide to understand its use in its simplest form before using
the ZCL_LOG_UTIL_EXAMPLES
(SE38
/ SE80
) example program.
I will mention which demo contain the appropriate code explain in this guide.
For the simplest possible use,
I advise you to use the type of log table provided with the class.
Instantiation works in the same way as that of an ALV grid of class
CL_SALV_TABLE
:
" Data declaration :
DATA: lt_log_table TYPE TABLE OF zcl_log_util=>ty_log_table ,
lr_log_util TYPE REF TO zcl_log_util .
" Instanciation
zcl_log_util=>factory(
IMPORTING
e_log_util = lr_log_util
CHANGING
c_log_table = lt_log_table
).
Available in
Demo 010
.
To log messages, there are a number of different ways to proceed. I advise you to do it in the following way. This method offers the advantage of being able to do the where-used on the message class and on the message number, a powerful feature of SAP.
" Creating a variable to catch generated message
DATA: lv_dummy TYPE string .
" Raising message :
" - 1.) Message texte is available in lv_dummy.
" - 2.) Log raised message
MESSAGE e504(vl) INTO lv_dummy.
lr_log_util->log( ).
" Display you log table
lr_log_util->display( ).
Hints : When you use statement MESSAGE
, SAP automatically feed
structure SY
. Using lr_log_util->log( )
will log message using the message
components available in structure SY
.
When a standard Function Module or BAPI implicitely
stored errors in SY
you can log system message using log( )
method
without writing statement MESSAGE
.
Available in
Demo 010
.
A log message in SAP is an integral part of the system which is very
well designed.
In the most basic usage case, they are used to handle the program logic errors
to inform (I
), warn (W
) or interrupt (E
or A
) the program.
Texts messages are stored in a class message (ID
) which associates them
to a number. The message text can have until four variables placeholders (&
).
The class message also handle the internationalization.
The best practice is to use class message to manage your own custom messages. You can raised the message by specifying the type or generate a text message for a log registry (internal table for final display or any purpose).
Keep in mind the following message components with their roles :
TYPE
: Defines the kind of the message :A
(Abort
) which will interrupt the program.E
(Error
) which will interrupt the program.W
(Warning
) which will display a warning in status bar. It did not interrupt the rest of the program.I
(Information
) which will display a information popup. It did not interrupt the rest of the program.
ID
: specifies the class message which contain the textNUMBER
: This is the number of the message to get in the provided class message (ID
)MESSAGE VALUE 1
: The message text variable to put in placeholders (first&
or&1
).MESSAGE VALUE 2
: The message text variable to put in placeholders (second&
or&2
).MESSAGE VALUE 3
: The message text variable to put in placeholders (third&
or&3
).MESSAGE VALUE 4
: The message text variable to put in placeholders (fourth&
or&4
).
There are two ways to instantiate the ZCL_LOG_UTIL
class.
The first method will be the most frequent method.
The second method makes it possible to defer the association of the internal
table to a later moment in the processing of the program.
It also offers a way to change log tables at any time which allows you to use
a single instance to manage different internal tables.
- First method : classic way, same as chapter
Getting Start
" Data declaration :
DATA: lt_log_table TYPE TABLE OF zcl_log_util=>ty_log_table ,
lr_log_util TYPE REF TO zcl_log_util .
" Instanciation (making link between instance and internal table)
zcl_log_util=>factory(
IMPORTING
e_log_util = lr_log_util
CHANGING
c_log_table = lt_log_table
).
Available in
Demo 010
.
- Second method : differed way, for multiple log table in one report.
# -----[ Instanciation ]---------------------------------------
" Data declaration :
DATA: lr_log_util TYPE REF TO zcl_log_util .
" Instanciation
zcl_log_util=>factory(
IMPORTING
e_log_util = lr_log_util
).
# -----[ Linking log table ]-----------------------------------
" Data declaration :
DATA: lt_log_table TYPE TABLE OF zcl_log_util=>ty_log_table .
" Linking log table :
lr_log_util->set_log_table(
CHANGING
t_log_table = lt_log_table
).
Available in
Demo 065
.
The main objective of this class is to provide the maximum possibility of
logging different message sources with different format types using only one method.
Here are the ways to log messages using the instance reference lr_log_util
of class zcl_log_util
.
By definition, system message are stored in the global system structure SY
.
Calling the method log( )
, without import parameter will add the
system message to the linked log table :
" Log system message
lr_log_util->log( ).
Available in
Demo 010
.
As mentioned previously, the statement MESSAGE
will update
the global system structure.
So to log the message in the log table simplify do as following :
" Done like this, the message will be redirected into lv_dummy as string.
MESSAGE i123(zmm01) WITH 'message' 'components' INTO DATA(lv_dummy).
lr_log_util->log( ).
Available in
Demo 010
.
From my point of view, this is the best way to register an entry in the log table, because the here before statement will respond to the where-used case.
You can log any kind of structure as long as you have defined the field roles.
The zcl_log_util
class already knows the field roles of the SAP standard
message structure (and table).
Please find below structure which are natively handle by zcl_log_util
:
Name | Msg. Text | Msg. Type | Msg. ID | Msg. Number | Msg. Val. 1 | Msg. Val. 2 | Msg. Val. 3 | Msg. Val. 4 |
---|---|---|---|---|---|---|---|---|
zcl_log_util=>ty_log_table | MESSAGE | TYPE | ID | NUMBER | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
sy | - | MSGTY | MSGID | MSGNO | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
prott | - | MSGTY | MSGID | MSGNO | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
bapiret1 | MESSAGE | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bapiret2 | MESSAGE | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bapi_coru_return | - | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bapi_order_return | - | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bdcmsgcoll | - | MSGTYP | MSGID | MSGNR | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
See chapter Set your own definitions
/ Set a custom log table type or unregistred SAP standard type
to register a unknown structure. Jump
DATA ls_bapiret2 TYPE bapiret2.
" Considering ls_bapiret2 is not initial
lr_log_util->log( ls_bapiret2 ).
You can log any kind of table as long as you have defined the field roles.
The zcl_log_util
class already knows the field roles of the SAP standard
message structure (and table).
Please find below structure which are natively handle by zcl_log_util
:
Name | Msg. Text | Msg. Type | Msg. ID | Msg. Number | Msg. Val. 1 | Msg. Val. 2 | Msg. Val. 3 | Msg. Val. 4 |
---|---|---|---|---|---|---|---|---|
zcl_log_util=>ty_log_table | MESSAGE | TYPE | ID | NUMBER | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
sy | - | MSGTY | MSGID | MSGNO | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
prott | - | MSGTY | MSGID | MSGNO | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
bapiret1 | MESSAGE | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bapiret2 | MESSAGE | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bapi_coru_return | - | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bapi_order_return | - | TYPE | ID | NUMBER | MESSAGE_V1 | MESSAGE_V2 | MESSAGE_V3 | MESSAGE_V4 |
bdcmsgcoll | - | MSGTYP | MSGID | MSGNR | MSGV1 | MSGV2 | MSGV3 | MSGV4 |
See chapter Set your own definitions
/ Set a custom log table type or unregistred SAP standard type
to register a unknown structure. Jump
DATA lt_bapiret2 TYPE TABLE OF bapiret2.
" Considering lt_bapiret2 is not initial
lr_log_util->log( lt_bapiret2 ).
Available in
Demo 040
.
The class offer the possibility to register a new entry in your log table
by passing message components directly in the import parameter of the
method log( )
.
Be aware that "where-used" will not be able to find the message
lr_log_util->log(
IMPORTING
i_log_msgid = 'zmm01'
i_log_msgno = '123'
i_log_msgty = 'I'
i_log_msgv1 = 'Message'
i_log_msgv2 = 'Component'
* i_log_msgv3 = 'optional'
* i_log_msgv4 = 'optional'
)
If you have a simple text or a variable which contains your text message you can register it in the log table.
Note : The class is not able to find back the message components, so in the log table you will only find the message in the message column.
lr_log_util->log( 'My free text message' ).
Available in
Demo 090
.
Available in
Demo 030
,Demo 050
,Demo 060
.
The zcl_log_util
class can be used as library
to handle logging throught Application Log.
Indeed, all registered messages from method log( )
,
can be stored in a Application Log ledger.
This is not the maim purpose of this class, so some settings steps must be perform to enable Application Log.
At least, to create a Application Log register, we have to set the main object and eventually one of its sub-object.
By default, zcl_log_util
use the default main object
ZLOGUTIL
which you have created in during Installation
step
thanks to SLG0
.
So at least, you can simply enable the Application log,
but I advise to use your own SLG
objects.
To handle Application Log from zcl_log_util
,
you can define a local reference to simplify the code writing
or make direct call from your instance.
Here we will use a local reference lr_slg
to increase code readability :
DATA lr_slg TYPE REF TO zcl_log_util_slg.
lr_slg = lr_log_util->slg( ).
Available in
Demo 070
.
To set/change your own main object,
simply use the method set_object( )
:
" Main object ZMYPO must be exist in SLG0.
lr_slg->set_object( 'ZMYPO' ).
Available in
Demo 070
.
To set/change the sub-object,
simply use the method set_sub_object( )
:
" Sub-object 'PO_CHANGE' must be a sub-obecjt of 'ZMYPO'.
lr_slg->set_sub_object( 'PO_CHANGE' ).
Available in
Demo 070
.
The external number is optionnal.
Its helps to reduce result when you search for log in SLG1
.
For instance, you can set the PO Number as external number :
lr_slg->set_external_number( '4500001189' ).
Available in
Demo 070
.
The retention delay is required for the garbage collector
to delete old log.
By default, the retention is set to 30
days.
lr_slg->set_retention( 60 ).
Available in
Demo 070
.
You can enable / disable at anytime the recording of log message in the Application Log.
Simply use method enable( )
to activate the functionality and
disabled( )
to turn off recording.
" Turn on Application Log
lr_slg->enable( ).
" Turn off Application Log
lr_slg->disable( ).
Available in
Demo 070
and080
.
Enabling / Disabling Application Log will not close the register. Register is saved at the end of the program.
If you want to display the Application Log in your report
to avoid using TCODE SLG1
, simply call method display( )
.
" Display the Application Log register
lr_slg->display( ).
Available in
Demo 070
.
Result in your own log table :
Result in Application Log using display( )
:
This functionality is the most complex of the class and this is the need which lead me to create it.
This feature must be used with parsimony, because rewriting messages is not harmless. This a very powerful functionality which is configured thanks to a customizing table. So, once in production, issues can became more complex for analysis.
So the programs which use overloading must be well documented in functional and technical documentation (SFD / STD) and as possible in the program using comments.
The overloading consist to alter the log message which is registering
with method log( )
(also with message( )
) according to rules set in
the customization table.
Alteration can be one of the following :
- Change the error message
- Skipping the error message
- Append an extra message
For instance, in our case, an interface program calls a BAPI which returns an error message. However, our processing was successfully done/commit in SAP. The customer asked us to consider this error message as false positive (and some other ones).
For this message we only change the message type from E
to W
.
Another example that can explain overloading functionality is if you want to return a more speaking message for end users than those SAP which can be a little bit too technical.
All components which compose a log message (Cf Definition of log message
)
can be overloaded (one to many at once).
Overloading must be managed using a customizing table.
The class ZCL_LOG_UTIL
allows you to reuse your own customizing table,
but if you do not have this kind of table, she is delivered with
her own table ZLOG_UTIL_OVERLO
.
(See chapter Using your own custom setting table
to set your own table).
At least, the customizing table must have four inputs fields
and three outputs fields - marked as Obligatory - to be functional,
but please find all configurable fields with their role.
You can find the corresponding field of table ZLOG_UTIL_OVERLO
between brackets.
- Input fields :
- Obligatory :
- To set the
Message ID
of registering message to overload. [INPUT1
] - To set the
Message Number
of registering message to overload. [INPUT2
] - To set the
Message Type
of registering message to overload. [INPUT3
] - To set the
Spot ID
. We will see this feature later. [INPUT4
]
- To set the
- Optional :
- For pre-filtering : (identifying you development subject)
- One standing for development code. [
CODE
] - One standing for development domain. [
DOMAINE
] - One standing for kind of data. [
DATA
]
- One standing for development code. [
- For extra parameters :
- Extra parameter 1. [
INPUT5
] - Extra parameter 2.
No set
- Extra parameter 1. [
- For pre-filtering : (identifying you development subject)
- Obligatory :
- Output fields :
- Obligatory :
- To set the new value of the
Message ID
. [OUTPUT1
] - To set the new value of the
Message Number
. [OUTPUT2
] - To set the new value of the
Message Type
. [OUTPUT3
]
- To set the new value of the
- Optional :
- To set the new value of the
Message value 1
. [OUTPUT4
] - To set the new value of the
Message value 2
. [OUTPUT5
] - To set the new value of the
Message value 3
. [OUTPUT6
] - To set the new value of the
Message value 4
. [OUTPUT7
] - To set the overloading mode. [
OUTPUT8
]
- To set the new value of the
- Obligatory :
Important : If the field standing for overloading mode is not defined all rules will be processed as mode
O
(Overloading).
The overloading allows you to restrict rules to a dedicated scope using pre-filters and extra-parameters.
When you instantiate a reference of zcl_log_util
,
rules are loaded on the first call of method log( )
.
It selects rules from customizing table using pre-filters and stores them
in an internal table of the instance.
Overloading only works with rules internally stored to prevent many queries.
Pre-filters allows you to get overloading rules for you program.
Eventually, if you want to have variants of rule, you can use - by settings up at anytime - in your program until two extra parameters to fine the rule selection. It can be useful to manage rules in different language.
Please find below a chart showing how fields are used to fine the selection :
Available in
Demo 105
.
This mode offer you to edit one to many component of the message.
Considering you are logging the following message e508(vl)
and you have
this rule in the customizing table :
INPUT1 | INPUT2 | INPUT3 | INPUT4 | OUTPUT1 | OUTPUT2 | OUTPUT3 |
---|---|---|---|---|---|---|
VL | 508 | E | W |
With this rule the message will be logged / displayed as w508(vl)
Note : The overloading mode is not set (available) in the rule, so this is this mode which used.
This mode offer the possibility to mute message which respond to a rule set in customization table.
Note : The mode field must be defined
Considering you are logging the following message w010(02)
and you have
this rule in the customizing table :
INPUT1 | INPUT2 | INPUT3 | INPUT4 | OUTPUT1 | OUTPUT2 | OUTPUT3 | OUTPUT8 |
---|---|---|---|---|---|---|---|
02 | 010 | W | I |
With this rule the message will be ignored (no entry in table / no display)
This mode offer the possibility to add an extra message to those responding to the rule.
Considering you are logging the following message e001(vn)
and you have
this rule in the customizing table :
INPUT1 | INPUT2 | INPUT3 | INPUT4 | OUTPUT1 | OUTPUT2 | OUTPUT3 | OUTPUT8 |
---|---|---|---|---|---|---|---|
VN | 001 | E | 01 | 012 | E | A |
With this rule you will have both error message :
e001(vn)
e012(01)
Note : This mode is most designed when logs are registered in log table instead of raising message even if that works as well.
Even if the overloading functionality seams to be complex and powerful, the configuration is very easy.
In the program, the minimal configuration to do is to enable the feature. The rest is handle in the customization table.
By default, overloading functionality is not enabled. Use the following statement to activate it.
Note : You can enable / disabled the feature at any time in the program depending of your need.
" Direct method
lr_log_util->overload( )->enable( ).
" Using intermediate reference
DATA: lr_log_util_overload TYPE REF TO zcl_log_util_overload
lr_log_util_overload = lr_log_util->overload( ).
lr_log_util_overload->enable( ).
To disable :
lr_log_util_overload->disable( ).
Available in
Demo 100
.
The purpose of pre-filters is to reduce / restrict rules to a specific scope (mainly a program).
The idea is to set pre-filter in the INITIALIZATION
event.
Then overloading through method log( )
will only use
rule responding to pre-filters.
To set filter, please use these methods. You can you them in an independent way depending of your need and your customizing table.
lr_log_util->overload( )->set_filter_devcode_value( 'ZCLLOGUTIL' ).
lr_log_util->overload( )->set_filter_domain_value( 'BC' ).
lr_log_util->overload( )->set_filter_data_value( 'OVER_LOG' ).
Available in
Demo 140
.
The Spot ID is a special and dedicated parameter to use a specific rule from your customizing table during overloading.
Imagine you use a BAPI W_DELIVERY_UPDATE_2
more than one time
lead by specific managements rules.
In one case the error message is a false positive
(little modification) and in the other case, the error message is a real issue.
You may want to have a overloading rule for both case.
Spot ID is design to identified a precise moment in your program. It's inspired from Enhancement Point.
You can set and change the Spot ID at any time in the program. You can easily enable or disable the Spot ID.
Considering the following entries in your customization table :
INPUT1 | INPUT2 | INPUT3 | INPUT4 | OUTPUT1 | OUTPUT2 | OUTPUT3 | OUTPUT8 |
---|---|---|---|---|---|---|---|
VN | 001 | E | VN | 001 | W | O | |
VN | 001 | E | MGT1 | I | |||
VN | 001 | E | MGT2 | 01 | 012 | E | O |
Please find below how to use Spot ID and the resulting behavior.
- Logging without using Spot ID :
lr_log_util->e( i_log_msgid = 'VN' i_log_msgno = '001' ). " Output message -> w001(vn) (Table Line 1)
- Setting & enabling Spot ID
" Set Spot ID and enable it lr_log_util->spot( 'MGT1' )->start( ). lr_log_util->e( i_log_msgid = 'VN' i_log_msgno = '001' ). " Result is no message will be raised/display/log " because table line 2 indicating mode I (ignoring the message)
- Changing Spot ID
" Spot is already enabled. Just change the Spot ID lr_log_util->spot( 'MGT2' ). lr_log_util->e( i_log_msgid = 'VN' i_log_msgno = '001' ). " Result is two message : e001(vn) (Original one) e012(01) (Appended message from table line 3) (Mode A = Append)
- Stopping Spot ID
" Spot is already enabled. Just change the Spot ID lr_log_util->spot( )->stop( ). lr_log_util->e( i_log_msgid = 'VN' i_log_msgno = '001' ). " Output message -> w001(vn) (Table Line 1)
Available in
Demo 120
andDemo 105
.
Available in
Demo 110
.
Available in
Demo 130
.
Available in
Demo 150
.
Available in
Demo 160
.
Available in
Demo 020
.
Available in
Demo 110
.