Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Log Javascript object as string Google App Scripts

Is there a way to log the contents of an object using Logger.log in Google App Scripts?

If I have Logger.log(data), the log is 'DataTableBuilder', or 'object' or something similar and incredibly unhelpful.

I want to see JSON strings of these objects if possible...

like image 212
Zach Smith Avatar asked Jun 17 '15 15:06

Zach Smith


3 Answers

As of Nov 2013, you can use JSON.stringify() to convert Objects into Strings. Some Google documentation can be found here, while the full JavaScript method documentation is available on your preferred API reference, such as MDN.

Example usage:

Logger.log(JSON.stringify(obj, null, 2));

Alternately, via Stackdriver Logging (as of 2017 June):

console.log({message: "Interactive object serialization", theObject: obj})
// or
console.log(JSON.stringify(obj));
like image 131
Gerardo Avatar answered Oct 29 '22 23:10

Gerardo


This a function I modified from another SO answer:

var Log = {

// Thanks to Amos Batto - https://stackoverflow.com/questions/603987/what-is-the-javascript-equivalent-of-var-dump-or-print-r-in-php

  /*
  dump() displays the contents of a variable like var_dump() does in PHP. dump() is
  better than typeof, because it can distinguish between array, null and object.  
  Parameters:
    v:              The variable
    howDisplay:     "none", "body", "alert" (default)
    recursionLevel: Number of times the function has recursed when entering nested
                    objects or arrays. Each level of recursion adds extra space to the 
                    output to indicate level. Set to 0 by default.
  Return Value:
    A string of the variable's contents 
  Limitations:
    Can't pass an undefined variable to dump(). 
    dump() can't distinguish between int and float.
    dump() can't tell the original variable type of a member variable of an object.
    These limitations can't be fixed because these are *features* of JS. However, dump()
  */

  dump: function(functionName, v, recursionLevel) {

      recursionLevel = (typeof recursionLevel !== 'number') ? 0 : recursionLevel;

      var vType = typeof v;
      var out = vType;

      switch (vType) {

          case "number":
              /* there is absolutely no way in JS to distinguish 2 from 2.0
              so 'number' is the best that you can do. The following doesn't work:
              var er = /^[0-9]+$/;
              if (!isNaN(v) && v % 1 === 0 && er.test(3.0))
                  out = 'int';*/

          case "boolean":
              out += ": " + v;
              break;

          case "string":
              out += "(" + v.length + '): "' + v + '"';
              break;

          case "object":
              //check if null
              if (v === null) {
                  out = "null";

              }
              //If using jQuery: if ($.isArray(v))
              //If using IE: if (isArray(v))
              //this should work for all browsers according to the ECMAScript standard:
              else if (Object.prototype.toString.call(v) === '[object Array]') {  
                  out = 'array(' + v.length + '): {\n';
                  for (var i = 0; i < v.length; i++) {
                      out += repeatString('   ', recursionLevel) + "   [" + i + "]:  " + 
                          Log.dump(functionName, v[i], recursionLevel + 1) + "\n";
                  }
                  out += repeatString('   ', recursionLevel) + "}";
              }
              else { //if object    
                  sContents = "{\n";
                  cnt = 0;
                  for (var member in v) {
                      //No way to know the original data type of member, since JS
                      //always converts it to a string and no other way to parse objects.
                      sContents += repeatString('   ', recursionLevel) + "   " + member +
                          ":  " + Log.dump(functionName, v[member], recursionLevel + 1) + "\n";
                      cnt++;
                  }
                  sContents += repeatString('   ', recursionLevel) + "}";
                  out += "(" + cnt + "): " + sContents;
              }
              break;
      }

      Logger.log(functionName + ' - ' + out);

    return;

    // Private Functions
    // -----------------

    /* repeatString() returns a string which has been repeated a set number of times */ 
    function repeatString(str, num) {
        out = '';
        for (var i = 0; i < num; i++) {
            out += str; 
        }
        return out;

    } // Log.dump.repeatString()

  }, // Log.dump()
}
like image 38
Andrew Roberts Avatar answered Oct 29 '22 23:10

Andrew Roberts


I keep coming to this thread looking for a neat way to log nested objects and arrays in GAS. I needed an output like this:

{
    food : {
        pizza : [
            crust,
            sauce,
            topping,
            cheese
        ]
    },
    drink : {
        bananaShake : [
            banana,
            milk,
            sugar
        ]
    }
}

UPDATE: This is a more elegant solution: Logger.log(JSON.stringify(obj, null, 2)). Thanks to @Zach's comment below.

In any case this is the function I was using earlier:

//-------------------------------------------------------------------------------
// Display the passed object in the Logger
// @param {object} obj - object to be logged
// @param {string} log - (for internal use only) final output sent to the logger
// @param {number} count - (for internal user only) keeps track of the number of 
//                         iteration that the program is running in.
//-------------------------------------------------------------------------------
function logObj(obj, log, count) {
  var def = {};
  // Set default values to the passed arguments
  obj = obj == undefined? def : obj;
  log = log == undefined? '\n' : log;
  count = count == undefined? 1 : count;

  // If it's date object convert it to string
  if(obj instanceof Date) {
    obj = obj.toString();
  }
  // If it's a function represent it as a string
  if(typeof obj == 'function') {
    obj = 'function() {}';
  }
  // If it's an Object
  if(typeof obj == 'object') {
    var isArray = obj.constructor.name == 'Array';
    var length = 0;
    for(var i in obj) {
      length++;
    }
    if(isArray) log += '[';
    else log += '{';
    if(length) {
      log += '\n';
      var num = 1;
      for(var i in obj) {
        // add tabs based on which iteration the program is running in
        var tab1 = '';
        var tab2 = ''; // this is one tab less than tab1 
        for(var k = 0; k < count; k++) {
          tab1 += '\t';
          if(k < (count - 1)) {
            tab2 += '\t';
          }
        }
        log += tab1;
        if(!isArray) log += i + ' : ';
        log += logObj(obj[i], '', count + 1);
        if(num < length) {
          log += ',\n';
        }
        num++;
      }
      log += '\n' + tab2;
    }
    if(isArray) log += ']';
    else log += '}';
    // if it's not the first iteration, return the log instead of printing it
    if(count > 1) {
      return log;
    }
  }
  else if(count > 1) {
    return obj;
  }
  else {
    log = obj;
  }
  if(count == 1) {
    Logger.log(log);
  }
}
like image 33
Tuhin Paul Avatar answered Oct 29 '22 22:10

Tuhin Paul