Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member
0 Kudos

CONTENT

1. What is a SLAW and what are its Benefits?

   1.a.     Extensibility-When the XI Environment is shared between Multiple Projects

   1.b.     Relative Performance Enhancement

   1.c.     Pre-build Error Handling Functions

   1.d.     File Size based Rotation

   1.e.     Controlled Logging & Tracing

2. How to Collaboratively Use XI's Message Monitoring and SLAW's Logging & Tracing Functionality?

3. How does SLAW work?

4. How to Use SLAW for Error Handling & Logging/Tracing in XI?

5. Appendix

DETAILS

-


1. What is a SLAW and what are its Benefits?

-


SLAW (SAP Logging API Wrapper) is an abstract interface developed around SAP Logging APIs to hide its complexity from its users (e.g. XI developers). For example, how to format the logging content using the TraceFormatter patterns, how to load a configuration file instead of hard-coding the configurations, how to implement periodic reload of the configuration file, how to optimize the performance using the static methods of the wrapper instead of initiating objects every time we need to use the SAP logging APIs, etc. The objective of SLAW is to provide developers a common framework and a more powerful option to log and trace in XI relative to XI's default logging and tracing capabilities.

Following are few primary benefits of developing a SLAW:

a. Extensibility-When the XI Environment is shared between Multiple Projects     

SLAW enables multiple projects to meet their unique logging & tracing requirements when sharing a single XI environment. Using XI's built-in logging and tracing configuration may not be an ideal solution in this scenario since this configuration is applied globally to an integration engine.  We can extend the SLAW to allow each project to log and trace at their desired steps at runtime at their desired severity levels (fatal, error, warning, info).

b. Relative Performance Enhancement               

A new Java object is required to be initialized every time we need to use SAP logging API or even the MappingTrace object for logging and tracing in XI. Such frequent initializations can lead to performance burden on JVM in the runtime environment. SLAW uses a static Java object which is initialized only once when the Wrapper class is loaded in JVM. Such programming approach can lead to performance enhancement, especially when the number of interfaces grows during the lifetime of an XI project. SLAW also limits the loading of logging properties (format, logging destination, etc.) to only once when the Wrapper class is loaded in JVM which also improves the performance relative to loading the logging properties every time we need to use SAP logging API in XI during design time.

c. Pre-build Error Handling Functions                

SLAW provides the build-in error handling functions such as assertion and throw which will be used to validate user-defined conditions at runtime and throw errors with relevant information for quicker diagnosis and resolution as compared to just a basic error message (for example, ArrayOutOfBound exception versus country code for interface X is not in correct format). Using the error handling functions of SLAW also gives us more control and flexibility on how to handle the errors compared to pre-determined error handling of XI’s integration engine, for example, it gives us the ability to gracefully log and handle the errors and permit the execution to continue which, based on pre-determined error handling, may completely stop the execution.

d. File Size based Rotation                     

During development, testing and production, it is typical to come across situations when the log and trace files are so huge that it becomes a time-consuming process to first open such huge logs (may be by breaking it up into multiple relatively smaller files) and then be able to use this log for error diagnosis purposes. SLAW gives us the ability to rotate log and trace files based on the file size. For example, we can configure SLAW to rotate to a new file after the current log file reaches a pre-defined file size limit.

e. Controlled Logging & Tracing

SLAW enables more controlled logging & tracing (i.e. what is logged, when is it logged & how much is logged). This can be a big advantage in terms of limiting the growth of database (used by XI to store message information) when just the message header size roughly doubles from tracing level '0' (25KB) to '1' (50KB). If we extrapolate based on this information, it will take twice as long to fill the database in any environment if we have tracing set to '0';. Logging in XI is extremely verbose so it is normally turned off in production.

-


2. How to Collaboratively Use XI's Message Monitoring and SLAW's Logging & Tracing Functionality?

-


Note that default message monitoring in XI logs thorough details for a processed message, such as Sender Namespace, Sender Interface, Receiver Service, Receiver Namespace, Receiver Interface, Queue ID, etc., which are available via the SXMB_ADMIN transaction (Figure 1). Such details may not be captured by SLAW.

Figure 1

On the other hand, SLAW provides significant benefits (as discussed in section, 'What is a SLAW and what are its Benefits?') which are not available via XI's message monitoring. Therefore, it is imperative to use a combined approach to track the processing of a message or to diagnose and resolve any processing errors. Following is an exemplary scenario steps (Figure 2) showing the collaborative use of XI's logging details available through message monitoring and SLAW's logging and tracing functionality to resolve a message processing failure:

Step 1: Notification of a message failure in XI either via SXMB_ADMIN or alerts.

Step 2: Collect XI message details pertaining to the failure from SXMB_ADMIN transaction.

Step 3: Capture the Unique Message ID of the failed message.

Step 4: Search for the message ID in SLAW logs.

Step 5: Proceed with failure diagnosis & resolution.

Figure 2

-


3. How does SLAW work?

-


SLAW is imported into the XI environment in the form of a Java Archive (JAR) file called utility.jar. All functions of the Wrapper class are available globally at runtime. Similarly, utility.jar can also be used to accommodate any globally available user-defined function needed by the XI developers during both design and runtime. For example, we can have a globally available XML manipulative function which adds or remove XML elements from a given XML message.

When utility.jar is imported into the XI environment, the following steps take place as part of the initial loading of the SLAW class:

a. Instantiate a static location object which acts as a linkage between the use of this wrapper and the configurations assigned to this wrapper. Please note that the location name should not contain any numerical characters because such characters will not show up in the logs. For example, A2A will show up as AA, therefore we have decided to use ClientWrapper instead of SLAW.

b. Load the wrapper configuration file. I have included a sample configuration file in the APPENDIX section. This file contains the following configuration which can be easily modified for specific requirements:

i. The logging severity (Severity.DEBUG, Severity.PATH, Severity.INFO, Severity.WARNING, Severity.ERROR, Severity.FATAL, Severity.ALL, and Severity.NONE).

ii. File rotation configuration. For example, the file size limit and the sequence number to be used for file rotation.

iii. Log destination: whether the logs will be outputted to a file or a console or both.

iv. Identify the pattern which will drive the format of the logged information.

c. Call the setPeriordicity method to set the time interval after which the configuration file will be regularly uploaded.

We have wrapped the widely used logging, tracing and error handling methods of SAP logging API with the method declared in the SLAW class. So when any of the methods of the wrapper class is invoked, such as SLAW.info(argumentA,argumentB);, the wrapper calls the corresponding method of the SAP logging API using the same arguments passed to it and returns the results of the method call if applicable. For example, method SLAW.assertion(method, 5 < 3,"INVALID comparison") returns either NULL or LogRecord object.

We have the flexibility to add or change any function in the SLAW class. This could either be a currently available function in SAP logging API or a newly developed user-defined function.

-


4. How to Use SLAW for Error Handling & Logging/Tracing in XI?

-


The following steps briefly explain how to use the Wrapper. Also please see the code sample in the Appendix section of this weblog:

NOTE: C: is used as a root directory in the following explanation. Please note that both the location of the configuration file and log files can be easily modified to meet specific requirements of a given environment. For example, in production, these references can be updated to use a drive hosted on a Storage Area Network (SAN).

a. Create logging and configuration directories on the XI server. This is a one time step. Two directories should be available at the end of this step: C:SLAWclientconfig and C:SLAWclientlogs).

b. Copy the clientlogging.properties file to the C:SLAWclientconfig directory.

c. Modify the configuration values in clientlogging.properties if needed. Following configurations can be modified:

i. Severity

ii. Location of the logs

iii. File rotation limit and sequence range

iv. Log destination (console or log file or both)

v. Format of the output

d. Import the utility.jar in XI. There are three options to import JAR in XI environment:

i. (RECOMMENDED) Import globally such that the access to this jar and the SLAW methods is available at any design step. Following steps are needed to implement this option:

- Save the JAR in the following three sub-directories under the j2ee directory on the XI server:

- j2eedeployinglib

- j2eeclusterdispatcher insystem

- j2eeclusterserver0 insystem

- Restart XI's Java Stack for the JAR to be loaded into JVM.

ii. Import at the message level such that the access is available at the message level.

iii. Import at the data element level such that the access is available only at the data element level. This will lead to loading of the configuration file for each element.

e. Using SLAW in Message & Java Mappings

Start by entering the following statement in the import field of message mappings:

- Code sample showing the use of SLAW in Java Mapping

- Sample SLAW Configuraiton Properties File

- Code sample showing the use of SLAW in Java Mapping. This code also uses DOM4J and JAXEN which I will cover in my next log:

public void execute(InputStream in, OutputStream out) throws StreamTransformationException

{

    String msgid = (String)param.get(StreamTransformationConstants.MESSAGE_ID);

   

    String errorMsg = null;

    SLAW.entering("JavaMapExShowcase");

   

    try

    {

        //START - USING DOM4J TO PARSE INCOMING XML, GETTING ALL DATA & POPULATING TARGET XML ....

        SLAW.info(loc, "@@ START - USING DOM4J TO PARSE INCOMING XML, GETTING ALL DATA & POPULATING TARGET XML ....");

        SAXReader reader = new SAXReader();

        Document InDoc = reader.read(in);

        Document OutDoc = reader.read("C:
xml
out.xml");

        String orderNum = InDoc.selectSingleNode( "ArchMT1/Order/OrderNum" ).getText();

        SLAW.info(loc, "OrderNum = " + orderNum);

        OutDoc.selectSingleNode("//Order/OrderNum").setText(orderNum);

        SLAW.info(loc, "OrderNum in OUTPUT XML = " + OutDoc.selectSingleNode("//Order/OrderNum").getText());

       

        String firstNm = InDoc.selectSingleNode( "ArchMT1/Order/FirstName" ).getText();

        SLAW.info(loc, "FirstNm = " + firstNm);

        String lastNm = InDoc.selectSingleNode( "ArchMT1/Order/LastName" ).getText();

        SLAW.info(loc, "lastNm = " + lastNm);

        String Nm = lastNm + ", " + firstNm;

        SLAW.info(loc, "Nm after concatenation = " + Nm);

        OutDoc.selectSingleNode("//Order/FullName").setText(Nm);

        SLAW.info(loc, "FullName in OUTPUT XML = " + OutDoc.selectSingleNode("//Order/FullName").getText());

       

        String mobileNm = InDoc.selectSingleNode( "ArchMT1/Order/MobileNum" ).getText();

        SLAW.info(loc, "mobileNm = " + mobileNm);

        OutDoc.selectSingleNode("//Order/ContactNum").setText(mobileNm);

        SLAW.info(loc, "FullName in OUTPUT XML = " + OutDoc.selectSingleNode("//Order/ContactNum").getText());

       

        String country = InDoc.selectSingleNode( "ArchMT1/Order/Country" ).getText();

        SLAW.info(loc, "country = " + country);

        OutDoc.selectSingleNode("//Order/HomeCountry").setText(country);

        SLAW.info(loc, "FullName in OUTPUT XML = " + OutDoc.selectSingleNode("//Order/HomeCountry").getText());

        //END - USING DOM4J TO PARSE INCOMING XML, GETTING ALL DATA & POPULATING TARGET XML ....    

        StringWriter sw4 = new StringWriter();

        XMLWriter xw4 = new XMLWriter(sw4);

        xw4.write(OutDoc);

        xw4.flush();

        SLAW.info(loc, "FINAL TARGET XML = " + sw4.toString());

       

        XMLWriter xw3 = new XMLWriter(out);

        xw3.write(OutDoc);

        xw3.flush();

      

        SLAW.info(loc, "END - USING DOM4J TO PARSE INCOMING XML, GETTING ALL DATA & POPULATING TARGET XML ....");

        // END - OUTPUT TO THE TARGET ...

       

        // START - SHOWING HOW TO ADD AND REMOVE ELEMENTS ......

       

        // start - print out the raw uploaded xml before deleting an element like CDATA

        // test statements

        SLAW.info(loc, "@@ START - SHOWING HOW TO REMOVE CDATA ....");

        StringWriter sw = new StringWriter();

        XMLWriter xw = new XMLWriter(sw);

        xw.write(InDoc);

        xw.flush();

        SLAW.info(loc, "INCOMING XML = " + sw.toString());

        // end - print out the raw uploaded xml

               

        // START - REMOVE CDATA ......

        SLAW.info(loc, "CALLING removeCDATA FROM EXECUTE(..) .......");

        removeCDATASimple(InDoc.getRootElement());

       

        // test statements

        StringWriter sw1 = new StringWriter();

        XMLWriter xw1 = new XMLWriter(sw1);

        xw1.write(InDoc);

        xw1.flush();

        SLAW.info(loc, "INCOMING XML AFTER REMOVING CDATA = " + sw1.toString());   

        SLAW.info(loc, "END - SHOWING HOW TO REMOVE CDATA ....");

        // END - REMOVE CDATA ......

       

       

        // START - ADD CDATA ......

        SLAW.info(loc, "@@ START - SHOWING HOW TO ADD CDATA ....");

        SLAW.info(loc, "CALLING addCDATA FROM EXECUTE(..) .......");

        addCDATA(InDoc.getRootElement(), "NEW - all tags and entity references are ignored by an XML processor that treats them just like any old character data");

        // test statements

        StringWriter swi2 = new StringWriter();

        XMLWriter xwi2 = new XMLWriter(swi2);

        xwi2.write(InDoc);

        xwi2.flush();

        SLAW.info(loc, "INCOMING XML AFTER ADDING CDATA = " + swi2.toString());

        SLAW.info(loc, "END - SHOWING HOW TO ADD CDATA ....");

        // END - ADD CDATA ......       

           

        // END - SHOWING HOW ADD AND REMOVE ELEMENTS ......

       

       

        // START - SHOWING HOW TO USE SLAW .....

        SLAW.info(loc, "START - SHOWING HOW TO USE SLAW .....ASSERTION WILL PASS ONLY WHEN THE LAST NAME = kho AND FIRST NAME = a");

        if (SLAW.assertion(loc, Nm.equals("kho, a"), "INVALID NAME ... PLEASE CHECK THE INCOMING DATA ...") != null)

        {      

                // ASSERTION FAILS ... SENDING A FATAL MSG TO THE LOG FILE ...

                SLAW.debug(loc, "--> @loc returned null. assertion is FALSE ..........");

                SLAW.warning(loc, "--> @loc returned null. assertion is FALSE ..........");

                SLAW.info(loc, "--> @loc returned null. assertion is FALSE ..........");

                SLAW.error(loc, "--> @loc returned null. assertion is FALSE ..........");

                SLAW.path(loc, "--> @loc returned null. assertion is FALSE ..........");

                SLAW.fatal(loc, "--> @loc returned null. assertion is FALSE ..........");

                // EITHER THROW THE EXECPTION OR IGNORE IF ... THROWING EXCEPTION WILL SKIP THE REST OF THE CODE IN THE TRY BLOCK & JUMP STRAIGHT TO THE CATCH BLOCK ...

                throw new Exception("Msg id = " + msgid + "; INVALID NAME ... PLEASE CHECK THE INCOMING DATA .... Actual = " + Nm + "; Expected = " + "kho, a");

        }

        else

        {

                // ASSERTION PASSES ...

                SLAW.debug(loc, "--> @loc returned null. assertion is TRUE..........");

                SLAW.warning(loc, "--> @loc returned null. assertion is TRUE ..........");

                SLAW.info(loc, "--> @loc returned null. assertion is TRUE ..........");

                SLAW.info(loc, "Msg id = " + msgid + "; VALID NAME ... Actual = " + Nm + "; Expected = " + "kho, a");

                SLAW.error(loc, "--> @loc returned null. assertion is TRUE ..........");

                SLAW.path(loc, "--> @loc returned null. assertion is TRUE ..........");    

        }

        SLAW.info(loc, "END - SHOWING HOW TO USE SLAW .....ASSERTION WILL PASS ONLY WHEN THE LAST NAME = kho AND FIRST NAME = a");

        // END - SHOWING HOW TO USE SLAW .....

   }

   catch (Exception e) {

      SLAW.fatal(loc, "ERROR processing object .......");

      SLAW.throwException(loc, e);

      throw new RuntimeException("EXCEPTION for MSG ID = " + msgid + "; " + e.toString());

    }

   

   finally

   {

       SLAW.exiting();

       loc = null;

   }

}

- Sample SLAW Configuraiton Properties File

        1. Set up a FileLog, for storing trace, with <id>: ‘File’

        2. %g = The sequence number of the file.

        3. file limit is in bytes

        4. cnt specifies the sequence number range

log[File]                           = FileLog

log[File].pattern                    = C:
SLAW
projectA
logs
PROJECTA_SLAW.%g.txt

log[File].limit                    = 10000

log[File].cnt                    = 10

log[File].formatter                  = formatter[TraceID]

        1. Set up a ConsoleLog, with <id>: ‘Console’

log[Console]                      = ConsoleLog

log[Console].formatter               = formatter[TraceID]

        1. Set up a TraceFormatter, with <id>: ‘TraceID’

        2. and its pattern starts with the Severity level, and consists no date/timestamp

        3. Use the following website to explore additonal options available for formating the log msg using the trace formatter pattern:

        4. https://www.sdn.sap.com/irj/servlet/prt/portal/prtroot/docs/library/javadocs/pre%20nw04%20sp2/sap%20...

formatter[TraceID]                    = TraceFormatter

formatter[TraceID].pattern             = %30d %-50l %s : %m

SLAW.logs                 = log[Console], log[File]

8 Comments