Disclaimer

The script explained in this column is intended for development and debug purposes only, it is not intended for production deployed Iflows. As explained in this post storing payload logs in the Message Process Log can cause performance issues and, in worst case scenario’s, out of memory exceptions. So be careful when using this script, use a debug flag as explained in the Iflow chapter.

Background

Payload logging is an important aspect of integration. It will give you insight in the content of input-, intermediate- and output messages which will help in creating, altering and maintaining the integration flow. When looking at payload logging approaches I’ve found this post covering the basics. However, there are some additional requirements which will make our life easier when logging:

  • Parameterize the script: we want to set the title and other log parameters dynamically. In this way we only need one logging script
  • Pretty print message content: we want to make the messages readable
  • Completeness: if there is no input message to log we want to switch to the exception message

In the remainder of this blog I will explain how I extended the example script to adhere to these requirements, resulting in a clearer message processing log.

The logging script

Input parameters

The script relies on the following parameters:

  • logTitle: the header of the log message details
  • logType: the type of input message we present to the logging script
  • ErrorMessage: parameter which holds the exception message when used in exception sub-processes

Message formats

A switch statement is used to format the message based on the given type. The following message types are supported:

XML

The static serialize method from the  XmlUtil class is used to format the message. As an extra step a newline is set after the XML tag using the replaceAll method and a regular expression, $1 denotes the match in the replacement pattern. I think it improves the readability of the message, consider it a personal preference.

JSON

The JsonOutput class contains static method prettyPrint which formats the message. The input message is parsed with the JsonSlurper class in order to ensure correct format. The iterator is used to produce the correct indentation.

CSV

No formatting is applied since most of the CSV messages are already readable. Alternatively, you can replace separators characters to improve readability with the replaceAll and a regular expression, e.g. replace the field separator with a tab to improve readability.

Default

The default text is word wrapped using the regular expression (.{100})(s+|). After every 100 characters ending with one or more spaces or the end of a string we apply a new line. Be aware of the backslash escaping when applying this expression.

Also, note that if there is not body message we attempt to access the exception message. We make use of the ternary operator. Alternatively the elvis operator can be used, it’s a matter of personal preference.

Logging action

Before we actually log the message we check if the required input parameters are filled. If this is not the case we will log a warning as title but still log the body message.

Usage in Iflows

Below an example implementation of the script. At the start of the Iflow we set the debug property to true if we want to log debug messages. If the debug property is set to true the properties are set and the message is logged. Alternatively, a local integration process can be used to hold the router and logging steps.  I also use the script in the exception subprocess to log exceptions. Be careful with outputting messages though, you do not want to overload you tenant.

The next image displays the conditions for the router:

 

The parameter setup is displayed in the image below:

The next image displays the different log messages. The enumeration corresponds to various processing steps in the Iflow. This approach will limit debug time in case of an error message in the log, you can retrace the part of the flow where the error originated.

Below the output of the mesages is shown*.

CSV:

XML:

Text:

JSON:

*Message content is either self-created or publicly available.

 

The script itself:

import com.sap.gateway.ip.core.customdev.util.Message; import java.util.HashMap; import groovy.xml.*; import groovy.json.*; import java.util.regex.*; def Message processData(Message message) { def body = message.getBody(java.lang.String) as String; def properties = message.getProperties(); def messageLog = messageLogFactory.getMessageLog(message); def logTitle = properties.get("logTitle"); def logType = properties.get("logType"); def exceptionMessage = properties.get("ExceptionMessage"); String logMessage = ""; String logMessageType = "text/plain"; switch(logType.toLowerCase()) { //There is case statement defined for 4 cases // Each case statement section has a break condition to exit the loop case 'xml': XmlUtil xmlUtil = new XmlUtil(); def xmldoc = new XmlParser().parseText(body); def serialized = XmlUtil.serialize(xmldoc); serialized = serialized.replaceAll('<\?xml version="1.0" encoding="UTF-8"\?>', '<\?xml version="1.0" encoding="UTF-8"\?>   '); logMessage = serialized; break; case 'json': def json = new JsonSlurper().parseText(body); json.each { logMessage = logMessage + JsonOutput.prettyPrint(JsonOutput.toJson(it)); } break; case 'csv': logMessage = body; break; default: String defaultMessage = body ? body: exceptionMessage; logMessage = defaultMessage.replaceAll("(.{100})(\s+|\Z)","$1  "); break; } body = logMessage; if(messageLog != null){ if(logTitle != null && logMessage != null && logMessageType != null){ messageLog.addAttachmentAsString(logTitle, logMessage, logMessageType); }else{ messageLog.addAttachmentAsString("Warning: one of required log parameters not set", "logTitle: "+logTitle+" logMessage: " + logMessage + "logMessageType: " +logMessageType+ "     "+ body, 'text/plain'); } } return message; } 

 

New NetWeaver Information at SAP.com

Very Helpfull

User Rating: Be the first one !