Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Karol-K
Advisor
Advisor

my problem was, as more and more ZTL contributions were created by me - I had a feeling this is a kind of blind editing, recompilation, restart and trying to imagine what was wrong.. then I was searching for a way how to analyze and inspect the parameters in the scripts to see what are the current values at least.

Here the outputs...

First, you have to install the Design Studio SDK: BIAL and ZTL Debugger Code Inspector Tool.

Then, we have to understand what we can inspect at the point of time when we have the context.

Global Variables

As the code runs in Rhino engine, there is a global context which can be read and inspected. by this you can see at any point what are values of global variables.

Owner Properties

All properties of the component are accessible on "this" object, therefor a loop on it will give you the values. In this area small extension is done, when the property starts with "[" or "{" I assume (hopefully positive) that it is a JSON string and try to parse as JSON.

Local Variables

This is a bit tricky topic, but after some search in google I could fins an acceptable workaround which allows parsing of local defined variables. For this, the ZTL script notation of your component needs to be adjusted a bit.

     Instead:

          var myVariable = "some text here";

     You need following:

          var l = {}; // define a local JSON object, I use the small L letter -> "l" to have quick access.

          l.myVariable = "some text here"; // now all local variables must be bound to the "l" object

And, finally the methods on the debugger component allow passing of this "l" object, which can be dynamically parsed.


Important




You need to place the DEBUGGER component in the application, then you can give it any name which you want (default is DEBUGGER_1) - BUT...


    the component name must be used also in your ZTL script



Why? I use the fact that ZTL can call other components if you know the name, like:


APPLICATION.createWarningMessage("- your message here -");





So, name "DEBUG" must be used as component name in my example below.




Example:



  • Name: DEBUG

  • line in ZTL: if(this.isDebug()) { DEBUG.openZtl(that, l)};





Which methods can be used?

There are few methods to allow the inspection:

  • DEBUG.openZtl -> this is to be placed at the beginning of the method
  • DEBUG.inspectZtl -> at any place in the method where the properties are interesting
  • DEBUG.exitZtl -> this is to be placed on at the end
  • DEBUG.processZtlException -> in case you get exception

How to write your ZTL script?

Based on the facts above, I have prepared following method statement:


void mySomeZtlMethod(String mySomeInput) {*


  /* entering general block*/


  var that = this;


  var l = {}; // define before try to have the values in exception case



  try{


  l._firstProperty = mySomeInput;



  if(this.isDebug()) { DEBUG.openZtl(that, l)}; // in this case the component name is "DEBUG" and the method checks this


  /* entering general block*/



  // your code....



  l.localValue = "NO";



  if(this.isDebug()) { DEBUG.inspectZtl(that, l)};






  // your another code....



  /* exiting general block*/


  if(this.isDebug()) { DEBUG.exitZtl(that, l)};



  } catch (e) {


  if(this.isDebug()) { DEBUG.processZtlException(that, l, e)};throw e;


  }


  /* exiting general block*/


*}










with this part, you can inspect your input parameters, you can define some local parameters and all those will be printed out. In addition, when exception will occur all information will be logged.

Explanation to Snippets Above

in the "entering block"

  • I start always with that definition, this is just continence
  • the "l" variable is better to be defined before "try" - then in case of exception, the content can be printed out
  • after try statement, you can place your input variables on to "l" - then those will be also printed out

in between, you can place as many inspects as you want ... but, as the debugger is printing the content out, placements in loops is not best idea - you can get timeouts on execution as there is some maximal processing time in ZTL (5 secs or so), perhaps you can place there something like:

if(this.isDebug("DEBUG_LOOP")) { DEBUG_LOOP.inspectZtl(that, l)};

and then place second DEBUGGER component in the application with DEBUG_LOOP. Then you can activate this only if really required.

in the "closing block"

  • last DEBUG statement before exiting - for normal case exit
  • and the catch block with exceptional exit
  • assure the exception is thrown always (outside of "if" statement).

Required extension in the component (isDebug() method)

My initial thought with simple check "if(DEBUG)" is not working as the Rhino is throwing exception at the access, therefore I had to rewrite this part. First, two methods are used:


  /**


   * DO NOT USE, ONLY FOR DEBUG ACTIVATION


   */


  @Visibility(private)


  boolean isDebug(optional String componentName) {*


    // quick check here


    if(componentName == undefined) {


      if(this._dbgDEBUG == "+") { return true; }


      if(this._dbgDEBUG == "-") { return false; }


      componentName = "DEBUG";


    } else {


      if(this["_dbg" + componentName] == "+") { return true; }


      if(this["_dbg" + componentName] == "-") { return false; }


    }



    internal_assureGlobalAccess ();


  


    var something = this.getGlobal()[componentName];


    if(something != undefined) {


      this["_dbg" + componentName] == "+";


      return true;


    } else {


      this["_dbg" + componentName] == "-";


      return false;


    }


  *}



  /**


   * Internal function to read return global scope


   * http://stackoverflow.com/questions/1162998/how-can-i-add-an-object-property-to-the-global-object-in-...


   */


  @Visibility(private)


void internal_assureGlobalAccess () {*


    if(this.getGlobal == undefined) {


      this.getGlobal = function () {


        return (function() {


          return this;


        }).call(null);


      }


    }


  *}



The idea is here to keep always the statements in code, but check once if the component is exisiting and then cache the information in the component.

you can copy those 2 methods to your component if you do not want to uncomment the DEBUG statements - or you can just use your own local propery.

Example

Some example has been made in the FacetFilter component, you can check up the internal ZTL methods, eg this one:


void reloadDataSource(/*Data Source which should be used for member selection, in case not the linked Data Source*/optional DataSourceAlias memberAccessSource) {*
  /* entering general block*/
  var that = this;
  try{
  if(this.isDebug()) { DEBUG.openZtl(that)};
  var l = {};
  /* entering general block*/
  l.elementsJson = [];
  l.DS = this.getDataSource();
  if(!memberAccessSource) {
  memberAccessSource = l.DS;
  }
  if(l.DS) {
  l.dimensions = undefined;
  if(this.DContentMode == "Only from Result Set") {
  l.dimensionsRows = DS.getDimensions(Axis.ROWS);
  l.dimensionsColumns = DS.getDimensions(Axis.COLUMNS);
  l.dimensions = [];
  l.dimensionsRows.forEach(function(dimension, indexD) {
  l.dimensions.push(dimension);
  });
  l.dimensionsColumns.forEach(function(dimension, indexD) {
  l.dimensions.push(dimension);
  });
  } else {
  l.dimensions = DS.getDimensions();
  }
  if(this.isDebug()) { DEBUG.inspectZtl(that, l)};
  l.dimensionKeys = "";
  l.dimensions.forEach(function(dimension, index) {
  l.dimensionKeys = l.dimensionKeys + dimension.name;
  });
  if(this.oldDimensionKeys != l.dimensionKeys) {
  l.dimensions.forEach(function(dimension, index) {
  l.members = memberAccessSource.getMembers(dimension, that.getMaxMembers());
  l.dimensionJson = {};
  l.dimensionJson.name = dimension.name;
  l.dimensionJson.text = dimension.text;
  l.dimensionJson.isMeasuresDimension = dimension.isMeasuresDimension;
  l.dimensionJson.hierarchyActive = DS.isHierarchyActive(dimension);
  l.dimensionJson.filterExt = ";" + DS.getFilterExt(dimension);
  l.dimensionJson.members = l.members;
  l.elementsJson.push(l.dimensionJson);
  });
  this.DElements = JSON.stringify(l.elementsJson);
  this.oldDimensionKeys = l.dimensionKeys;
  if(this.isDebug()) { DEBUG.inspectZtl(that, l)};
  }
  }
  /* exiting general block*/
  if(this.isDebug()) { DEBUG.exitZtl(that, l)};
  } catch (e) {
  if(this.isDebug()) { DEBUG.processZtlException(that, l, e)};throw e;
  }
  /* exiting general block*/
  *}







Print Out

In the stack view, you can see always the method and line number, like this:

The first question was "how to count the line number?"

Obviously, the Rhino engine is counting the method declaration as first line and then also empty lines are counted. But you should be able to quickly find the inspected line.

Variables

The variables re printed out with properties, like here:

and as the properties can be "big" I have also created a field below to see the content - which is again re-escaping JSON strings (very primitive and not performant way, but it works!), so you can see complex properties like here:

and use ti for copy and paste and formatting via URL http://jsonformatter.curiousconcept.com/

Work in Real Applications

As the component has a flag "active" and the code is checking on existence via IF:


if(this.isDebug()) { DEBUG.exitZtl(that, l)};


or


if(this.isDebug("DEBUG_CUSTOM")) { DEBUG_CUSTOM.exitZtl(that, l)};










you can easily leave such statements in the ZTL code, those will be only executed when the component "DEBUG" is in application, and also then you can always switch it ON / OFF to assure there is no performance impact.

Closing Words

Hope this will be helpful for many of you in the coding area. Also for users coding only in BIAL scrips, the method "inspectNow" should bring some lights into the context.

  • have fun!
2 Comments